1 /**
  2  * The Render Engine
  3  * BehaviorMover2D
  4  *
  5  * @fileoverview A component which moves game objects using behavior components.
  6  *
  7  * @author: Brett Fattori (brettf@renderengine.com)
  8  * @author: $Author: bfattori $
  9  * @version: $Revision: 1573 $
 10  *
 11  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 12  *
 13  * Permission is hereby granted, free of charge, to any person obtaining a copy
 14  * of this software and associated documentation files (the "Software"), to deal
 15  * in the Software without restriction, including without limitation the rights
 16  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 17  * copies of the Software, and to permit persons to whom the Software is
 18  * furnished to do so, subject to the following conditions:
 19  *
 20  * The above copyright notice and this permission notice shall be included in
 21  * all copies or substantial portions of the Software.
 22  *
 23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 26  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 28  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 29  * THE SOFTWARE.
 30  *
 31  */
 32 R.Engine.define({
 33     "class":"R.components.transform.BehaviorMover2D",
 34     "requires":[
 35         "R.components.Transform2D",
 36         "R.struct.HashContainer"
 37     ]
 38 });
 39 
 40 // Add behavior options
 41 if (R.Engine.options.behaviors === undefined) {
 42     R.Engine.options.behaviors = {};
 43 }
 44 
 45 $.extend(R.Engine.options.behaviors, {
 46     "behaviorLagForceRatio":5,
 47     "behaviorDefaultBlending":1.0
 48 });
 49 
 50 /**
 51  * @class A 2d transform component driven by different behaviors.  Behaviors are located in the
 52  *        <code>R.components.logic</code> package.
 53  *
 54  * @param name The name of the component
 55  * @param maxForce The maximum force that can be applied to the vehicle
 56  * @param maxSpeed The top speed of the vehicle
 57  * @extends R.components.Transform2D
 58  * @constructor
 59  */
 60 R.components.transform.BehaviorMover2D = function () {
 61     "use strict";
 62     return R.components.Transform2D.extend(/** @scope R.components.transform.BehaviorMover2D.prototype */{
 63 
 64         velocity:null,
 65         maxForce:0,
 66         maxSpeed:0,
 67         stopped:false,
 68         behaviors:null,
 69 
 70         /** @private */
 71         constructor:function (name, maxForce, maxSpeed) {
 72             this.base(name);
 73             this.stopped = true;
 74             this.velocity = R.clone(R.math.Vector2D.ZERO);
 75             this.maxForce = maxForce || R.components.transform.BehaviorMover2D.MAX_FORCE;
 76             this.maxSpeed = maxSpeed || R.components.transform.BehaviorMover2D.MAX_SPEED;
 77             this.behaviors = R.struct.HashContainer.create();
 78         },
 79 
 80         /**
 81          * Add a behavior to this component.
 82          * @param name {String} The name of the behavior
 83          * @param behavior {R.components.Logic} A behavior component to add
 84          * @param weight {Number} The blending weight of the component amongst all other components
 85          */
 86         addBehavior:function (name, behavior, weight) {
 87             Assert((behavior instanceof R.components.logic.behaviors.BaseBehavior), "Cannot add non-behavior component to BehaviorMover2D!");
 88             this.behaviors.add(name, {
 89                 b:behavior,
 90                 w:weight || R.Engine.options.behaviors.behaviorDefaultBlending
 91             });
 92             behavior.setGameObject(this.getGameObject());
 93             behavior.setTransformComponent(this);
 94         },
 95 
 96         /**
 97          * Get the behavior component, by name.
 98          * @param name {String}
 99          * @return {R.components.Logic} The behavior component
100          */
101         getBehavior:function (name) {
102             var b = this.behaviors.get(name);
103             return b ? b.b : null;
104         },
105 
106         /**
107          * Remove a behavior, by name.
108          * @param name {String} The behavior component to remove
109          * @return {R.components.Logic} The component which was removed
110          */
111         removeBehavior:function (name) {
112             var behavior = this.behaviors.removeHash(name);
113             if (behavior) {
114                 behavior = behavior.b;
115                 behavior.setGameObject(null);
116                 behavior.setTransformComponent(null);
117             }
118             return behavior;
119         },
120 
121         /**
122          * Run the behaviors and combine the steering vector from all, weighted by
123          * each behavior.
124          * @param time {Number} The world time
125          * @param dt {Number} The time since the last frame was generated
126          * @private
127          */
128         runBehaviors:function (time, dt) {
129             var acc = R.math.Vector2D.create(0, 0), itr = this.behaviors.iterator(),
130                 steer = R.clone(R.math.Vector2D.ZERO);
131             while (itr.hasNext()) {
132                 var behavior = itr.next();
133                 var bStr = behavior.b.execute(time, dt);
134                 steer.set(bStr).mul(behavior.w);
135                 acc.add(steer);
136                 if (bStr !== R.math.Vector2D.ZERO) {
137                     // Clean up the steering for the behavior
138                     bStr.destroy();
139                 }
140             }
141             return acc;
142         },
143 
144         /**
145          * Execute this component, calculating motion based on the behaviors.
146          * @param renderContext {R.rendercontexts.AbstractRenderContext} The rendering context
147          * @param time {Number} The world time
148          * @param dt {Number} The time since the last frame was generated
149          */
150         execute:function (renderContext, time, dt) {
151             if (this.stopped) {
152                 return;
153             }
154 
155             // Run the behaviors
156             var acceleration = this.runBehaviors(time, dt);
157 
158             // Allow the max force to exceed the baseline if the simulation is lagging
159             acceleration.truncate(this.maxForce * ((dt / R.Engine.fpsClock) * R.Engine.options.behaviors.behaviorLagForceRatio));
160 
161             // Add acceleration to velocity
162             this.velocity.add(acceleration);
163             this.velocity.truncate(this.maxSpeed);
164 
165             // Adjust for time lag
166             this.velocity.mul(dt / R.Engine.fpsClock);
167 
168             // Move the vehicle
169             this.setPosition(this.getPosition().add(this.velocity));
170 
171             // Rotate the vehicle to match the direction of travel
172             var ang = R.math.Vector2D.UP.signedAngleBetween(this.velocity);
173             this.setRotation(ang);
174 
175             this.base(renderContext, time, dt);
176         },
177 
178         /**
179          * Stop or start behavior processing.
180          * @param state {Boolean} Set to <code>true</code> to stop behaviors from executing
181          */
182         setStopped:function (state) {
183             this.stopped = state;
184         },
185 
186         /**
187          * Get the current velocity of the mover component.
188          * @return {R.math.Vector2D}
189          */
190         getVelocity:function () {
191             return this.velocity;
192         },
193 
194         /**
195          * Set the velocity, directly.
196          * @param vel {R.math.Vector2D|Number} The velocity vector, or X component
197          * @param y {Number} The Y component, if <tt>vel</tt> is the X component
198          */
199         setVelocity:function (vel, y) {
200             this.velocity.set(vel, y).truncate(this.maxSpeed);
201         },
202 
203         /**
204          * Get the maximum speed of this mover component.
205          * @return {Number}
206          */
207         getMaxSpeed:function () {
208             return this.maxSpeed;
209         },
210 
211         /**
212          * Get the maximum force that can be applied to this mover component.
213          * @return {Number}
214          */
215         getMaxForce:function () {
216             return this.maxForce;
217         },
218 
219         /**
220          * Set the maximum speed which can be applied to this mover component.
221          * @param speed {Number} The maximum speed
222          */
223         setMaxSpeed:function (speed) {
224             this.maxSpeed = speed;
225         }
226 
227     }, /** @scope R.components.transform.BehaviorMover2D.prototype */{
228         getClassName:function () {
229             return "R.components.transform.BehaviorMover2D";
230         },
231 
232         MAX_FORCE:10,
233         MAX_SPEED:3
234     });
235 };
236