1 /**
  2  * The Render Engine
  3  * 2D platformer mover
  4  *
  5  * @fileoverview A transform component for movement around a tile map as a "platformer".
  6  *
  7  * @author: Brett Fattori (brettf@renderengine.com)
  8  *
  9  * @author: $Author: bfattori $
 10  * @version: $Revision: 1555 $
 11  *
 12  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 13  *
 14  * Permission is hereby granted, free of charge, to any person obtaining a copy
 15  * of this software and associated documentation files (the "Software"), to deal
 16  * in the Software without restriction, including without limitation the rights
 17  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 18  * copies of the Software, and to permit persons to whom the Software is
 19  * furnished to do so, subject to the following conditions:
 20  *
 21  * The above copyright notice and this permission notice shall be included in
 22  * all copies or substantial portions of the Software.
 23  *
 24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 29  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 30  * THE SOFTWARE.
 31  *
 32  */
 33 
 34 // The class this file defines and its required classes
 35 R.Engine.define({
 36     "class":"R.components.transform.PlatformMover2D",
 37     "requires":[
 38         "R.components.Transform2D",
 39         "R.math.Math2D",
 40         "R.struct.RayInfo"
 41     ]
 42 });
 43 
 44 /**
 45  * @class A transform component to move around a tile map like the old "platformer" games
 46  *
 47  * @param name {String} Name of the component
 48  * @param tileMap {R.resources.types.TileMap} The tile map to move around in
 49  * @param priority {Number} Between 0.0 and 1.0, with 1.0 being highest
 50  *
 51  * @extends R.components.Transform2D
 52  * @constructor
 53  * @description Creates a transform component.
 54  */
 55 R.components.transform.PlatformMover2D = function () {
 56     "use strict";
 57     return R.components.Transform2D.extend(/** @scope R.components.transform.PlatformMover2D.prototype */{
 58 
 59         tileMap:null,
 60         moveVec:null,
 61         gravity:null,
 62         tileSize:null,
 63 
 64         /** @private */
 65         constructor:function (name, tileMap, priority) {
 66             this.base(name, priority || 1.0);
 67             this.tileMap = tileMap;
 68             this.moveVec = R.math.Vector2D.create(0, 0);
 69             this.gravity = R.math.Vector2D.create(0, 0.2);
 70             if (tileMap instanceof R.resources.types.TileMap) {
 71                 this.tileSize = Math.max(tileMap.getBaseTile().getBoundingBox().w,
 72                     tileMap.getBaseTile().getBoundingBox().h);
 73             }
 74         },
 75 
 76         destroy:function () {
 77             this.moveVec.destroy();
 78             this.gravity.destroy();
 79             this.base();
 80         },
 81 
 82         /**
 83          * Releases the component back into the pool for reuse.  See {@link PooledObject#release}
 84          * for more information.
 85          */
 86         release:function () {
 87             this.base();
 88             this.tileMap = null;
 89             this.moveVec = null;
 90             this.tileSize = null;
 91         },
 92 
 93         setTileMap:function (tileMap) {
 94             if (!tileMap.getBaseTile()) {
 95                 return;
 96             }
 97 
 98             this.tileMap = tileMap;
 99             this.tileSize = Math.max(tileMap.getBaseTile().getBoundingBox().w,
100                 tileMap.getBaseTile().getBoundingBox().h);
101         },
102 
103         getTileMap:function () {
104             return this.tileMap;
105         },
106 
107         getTileSize:function () {
108             return this.tileSize;
109         },
110 
111         getMoveVector:function () {
112             return this.moveVec;
113         },
114 
115         setMoveVector:function (ptOrX, y) {
116             this.moveVec.set(ptOrX, y);
117         },
118 
119         getGravity:function () {
120             return this.gravity;
121         },
122 
123         setGravity:function (xOrPt, y) {
124             this.gravity.set(xOrPt, y);
125         },
126 
127         /**
128          * This method is called by the game object to run the component,
129          * updating its state.
130          *
131          * @param renderContext {R.rendercontexts.AbstractRenderContext} The context the component will render within.
132          * @param time {Number} The global engine time
133          * @param dt {Number} The delta between the world time and the last time the world was updated
134          *          in milliseconds.
135          */
136         execute:function (renderContext, time, dt) {
137             if (this.tileMap) {
138                 var bBox = this.getGameObject().getBoundingBox(), oldPos = R.clone(this.getPosition()),
139                     newPos = R.clone(oldPos), testPt = R.clone(bBox.getCenter()),
140                     mNormal = R.clone(this.moveVec).normalize(), rayInfo,
141                     dir = R.math.Vector2D.create(0, 0);
142 
143                 // If movement along the X coordinate isn't zero, we want to test for collisions along the axis.
144                 // We'll cast a ray in the direction of movement, one tile width long, from the center of the
145                 // bounding box
146                 if (this.moveVec.x != 0) {
147                     // We want to cast a ray along the X axis of movement
148                     testPt.setX((newPos.x + testPt.x) + (bBox.getHalfWidth() * mNormal.x));
149                     dir.set(this.moveVec.x, 0).normalize().mul(this.tileSize);
150                     rayInfo = R.struct.RayInfo.create(testPt, dir);
151 
152                     R.resources.types.TileMap.castRay(this.tileMap, rayInfo, renderContext);
153 
154                     // There's something in the direction of horizontal movement, can't go that way
155                     if (rayInfo.shape) {
156                         this.moveVec.setX(0);
157                         newPos.x -= rayInfo.data.x;
158                     }
159 
160                     rayInfo.destroy();
161                 }
162 
163                 // Add in gravity
164                 if (!this.gravity.equals(R.math.Vector2D.ZERO)) {
165                     this.moveVec.add(this.gravity);
166 
167                     // We'll cast two rays, one from the left side of the bounding box,
168                     // the other from the right. If either collides, zero out gravity.
169                     // -- First one
170                     testPt.set(newPos.x + 1, newPos.y + bBox.h);
171                     dir.set(this.moveVec).normalize().mul(3);
172                     rayInfo = R.struct.RayInfo.create(testPt, dir);
173 
174                     R.resources.types.TileMap.castRay(this.tileMap, rayInfo, renderContext);
175 
176                     // If a collision occurs, stop gravity and adjust position
177                     if (rayInfo.shape) {
178                         this.moveVec.setY(0);
179                         newPos.y -= rayInfo.data.y;
180                     } else {
181                         // -- Second one
182                         testPt.set(newPos.x + bBox.w - 1, newPos.y + bBox.h);
183                         rayInfo = R.struct.RayInfo.create(testPt, dir);
184                         R.resources.types.TileMap.castRay(this.tileMap, rayInfo, renderContext);
185                         if (rayInfo.shape) {
186                             this.moveVec.setY(0);
187                             newPos.y -= rayInfo.data.y;
188                         }
189                     }
190 
191                     rayInfo.destroy();
192                 }
193 
194                 this.setPosition(newPos.add(this.moveVec));
195 
196                 dir.destroy();
197                 oldPos.destroy();
198                 newPos.destroy();
199                 testPt.destroy();
200             }
201 
202             this.base(renderContext, time, dt);
203         }
204 
205     }, { /** @scope R.components.transform.PlatformMover2D.prototype */
206 
207         /**
208          * Get the class name of this object
209          * @return {String} "R.components.transform.PlatformMover2D"
210          */
211         getClassName:function () {
212             return "R.components.transform.PlatformMover2D";
213         }
214 
215     });
216 }