1 /**
  2  * The Render Engine
  3  * GameObject
  4  *
  5  * @fileoverview An object which contains components.  This is a base
  6  *               class for most in-game objects.
  7  *
  8  * @author: Brett Fattori (brettf@renderengine.com)
  9  * @author: $Author: bfattori $
 10  * @version: $Revision: 1573 $
 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.engine.GameObject",
 37     "requires":[
 38         "R.struct.HashContainer",
 39         "R.components.Base"
 40     ]
 41 });
 42 
 43 /**
 44  * @class A game object is a container for components.  Each component within
 45  *        the object provides a portion of the overall functionality.  A game object
 46  *        can have any number of components of any type within it.  Components provide
 47  *        functionality for things like rendering, collision detection, effects, or
 48  *        transformations. This way, an object can be anything, depending on it's components.
 49  *        <p/>
 50  *        A <tt>GameObject</tt> is the logical foundation for all in-game objects.  It is
 51  *        through this mechanism that game objects can be created without having to manipulate
 52  *        large, monolithic objects.  A <tt>GameObject</tt> contains {@link R.components.Base Components},
 53  *        which are the building blocks for complex functionality and ease of development.
 54  *        <p/>
 55  *        By building a <tt>GameObject</tt> from multiple components, the object gains the
 56  *        component's functionality without necessarily having to implement anything.  Many
 57  *        components already exist in the engine, but you are only limited by your imagination
 58  *        when it comes to developing new components.
 59  *
 60  * @extends R.struct.HashContainer
 61  * @constructor
 62  * @description Create a game object.
 63  */
 64 R.engine.GameObject = function () {
 65     "use strict";
 66     return R.struct.HashContainer.extend(/** @scope R.engine.GameObject.prototype */{
 67 
 68         renderContext:null,
 69         dirtyFlag:false,
 70         oldDirty:false,
 71 
 72         prePostComponents:null,
 73         keepAlive:false,
 74 
 75         /** @private */
 76         constructor:function (name) {
 77             this.base(name);
 78             this.dirtyFlag = true;
 79             this.oldDirty = false;
 80             this.prePostComponents = [];
 81             this.renderContext = null;
 82             this.keepAlive = false;
 83         },
 84 
 85         /**
 86          * Release the object back into the object pool.
 87          */
 88         release:function () {
 89             this.base();
 90             this.renderContext = null;
 91             this.dirtyFlag = false;
 92             this.oldDirty = false;
 93             this.prePostComponents = null;
 94             this.keepAlive = false;
 95         },
 96 
 97         /**
 98          * Destroy all of the components within this object and
 99          * remove this object from it's render context.
100          */
101         destroy:function () {
102             if (this.getRenderContext()) {
103                 this.getRenderContext().remove(this);
104             }
105 
106             while (this.prePostComponents.length > 0) {
107                 this.prePostComponents.shift().destroy();
108             }
109 
110             this.cleanUp();
111             this.base();
112         },
113 
114 
115         /**
116          * Marks the object as dirty.  An object is considered dirty if something about
117          * it has changed which would affect how it is rendered.
118          */
119         markDirty:function () {
120             this.dirtyFlag = true;
121         },
122 
123         /**
124          * Check the flag which indicates if the object is dirty.
125          * @return {Boolean}
126          */
127         isDirty:function () {
128             return this.dirtyFlag;
129         },
130 
131         /**
132          * Check the flag which indicates if the object <i>was</i> dirty the last time
133          * it was updated.  Objects which aren't dirty, but were dirty, need to be redrawn
134          * one more time so they aren't missed in the next frame.
135          * @return {Boolean}
136          */
137         wasDirty:function () {
138             return this.oldDirty;
139         },
140 
141         /**
142          * Set the rendering context this object will be drawn within.  This method is
143          * called when a host object is added to a rendering context.
144          *
145          * @param renderContext {R.rendercontexts.AbstractRenderContext} The context
146          */
147         setRenderContext:function (renderContext) {
148             this.renderContext = renderContext;
149             this.markDirty();
150 
151             if (this.afterAdd) {
152                 // If the object being added to the render context has
153                 // an "afterAdd" method, call it
154                 this.afterAdd(renderContext);
155             }
156         },
157 
158         /**
159          * Get the rendering context this object will be drawn upon.
160          *
161          * @return {R.rendercontexts.AbstractRenderContext} The render context the object belongs to
162          */
163         getRenderContext:function () {
164             return this.renderContext;
165         },
166 
167         /**
168          * Update this object within the render context, at the specified timeslice.
169          *
170          * @param renderContext {R.rendercontexts.AbstractRenderContext} The context the object will be rendered within.
171          * @param time {Number} The global time within the engine.
172          * @param dt {Number} The delta between the world time and the last time the world was updated
173          *          in milliseconds.
174          */
175         update:function (renderContext, time, dt) {
176 
177             // Run the components
178             var components = this.iterator();
179 
180             while (components.hasNext()) {
181                 components.next().execute(renderContext, time, dt);
182             }
183 
184             components.destroy();
185             this.oldDirty = this.dirtyFlag;
186             this.dirtyFlag = false;
187 
188             this.base(renderContext, time, dt);
189         },
190 
191         /**
192          * Keep object alive, even when outside viewport.  Setting an object to the "keep alive"
193          * state will keep the object from being put into the render context's inactive bin,
194          * even when it is outside of the expanded viewport.  This is good for objects which
195          * traverse a large area of the game world.
196          * @param state {Boolean} <code>true</code> to keep the object alive at all times
197          */
198         setKeepAlive:function (state) {
199             this.keepAlive = state;
200         },
201 
202         /**
203          * Returns <code>true</code> if the object is to be kept alive (updated) at all times.
204          * @return {Boolean}
205          */
206         isKeepAlive:function () {
207             return this.keepAlive;
208         },
209 
210         /**
211          * Add a component to the game object.  The components will be
212          * sorted based on their type then their priority within that type.
213          * Components with a higher priority will be sorted before components
214          * with a lower priority.  The sorting order for type is:
215          * <ul>
216          * <li>Input</li>
217          * <li>Transform</li>
218          * <li>Logic</li>
219          * <li>Collision</li>
220          * <li>Rendering</li>
221          * </ul>
222          *
223          * @param component {R.components.Base} A component to add to the host
224          */
225         add:function (component) {
226 
227             Assert((R.components.Base.isInstance(component)), "Cannot add a non-component to a GameObject");
228             Assert(!this.isInHash(component.getName()), "Components must have a unique name within the host");
229 
230             // Special handling for pre and post processing components
231             if (component.getType() == R.components.Base.TYPE_PRE ||
232                 component.getType() == R.components.Base.TYPE_POST) {
233 
234                 // Only one of each can be added
235                 this.setPreOrPostComponent(component);
236                 component.setGameObject(this);
237                 return;
238             }
239 
240             this.base(component.getName(), component);
241 
242             component.setGameObject(this);
243             if (this.getObjects().length > 1) {
244                 this.sort(R.engine.GameObject.componentSort);
245             }
246             this.markDirty();
247         },
248 
249         /**
250          * Remove the component from the game object
251          * @param component {String|R.components.Base} The component to remove, or the name of the component to remove
252          * @return {R.components.Base} The component which was removed
253          */
254         remove:function (component) {
255             var c = typeof component === "string" ? this.get(component.toUpperCase()) : component;
256             return this.base(c);
257         },
258 
259         /**
260          * Setting up pre- or post-process components.  Only one of each can be assigned.
261          * This is intended to be used internally as the components are processed externally
262          * to the normal component handling.
263          * @private
264          */
265         setPreOrPostComponent:function (component) {
266             if (component.getType() == R.components.Base.TYPE_PRE) {
267                 this.prePostComponents[0] = component;
268             } else {
269                 this.prePostComponents[1] = component;
270             }
271         },
272 
273         /**
274          * Get the component with the specified name from this object.
275          *
276          * @param name {String} The unique name of the component to get
277          * @return {R.components.Base}
278          */
279         getComponent:function (name) {
280             return this.get(name.toUpperCase());
281         },
282 
283         /**
284          * Get a component by class name.  If there is more than one component with the given
285          * class, returns the first occurrence.
286          * @param className {String} The class name
287          * @return {R.components.Base} The component, or <code>null</code> if not found
288          */
289         getComponentByClass:function (className) {
290             var clazz = R.getClassForName(className);
291             if (undefined === clazz) {
292                 return null;
293             }
294 
295             var c = this.getAll();
296             for (var i in c) {
297                 if (c[i] instanceof clazz) {
298                     return c[i];
299                 }
300             }
301             return null;
302         },
303 
304         /**
305          * Returns a property object with accessor methods.
306          * @return {Object}
307          */
308         getProperties:function () {
309             var self = this;
310             var prop = this.base(self);
311             return $.extend(prop, {
312                 "RenderContext":[function () {
313                     return self.renderContext.getName();
314                 }, null, false]
315             });
316         }
317 
318     }, /** @scope R.engine.GameObject.prototype */{
319 
320         /**
321          * Sort components within this object based upon their component
322          * type, and the priority within that type.  Components with a higher
323          * priority will be sorted before components with a lower priority.
324          * @static
325          */
326         componentSort:function (component1, component2) {
327             return ((component1.getType() - component2.getType()) +
328                 ((1 / component1.getPriority()) - (1 / component2.getPriority())));
329         },
330 
331         /**
332          * Get the class name of this object
333          *
334          * @return {String} "R.engine.GameObject"
335          */
336         getClassName:function () {
337             return "R.engine.GameObject";
338         }
339 
340     });
341 
342 };