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 };