1 /** 2 * The Render Engine 3 * Engine Class 4 * 5 * @fileoverview The main engine class 6 * 7 * @author: Brett Fattori (brettf@renderengine.com) 8 * @author: $Author: bfattori@gmail.com $ 9 * @version: $Revision: 1557 $ 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 33 /** 34 * @class The main engine class which is responsible for keeping the world up to date. 35 * Additionally, the Engine will track and display metrics for optimizing a game. Finally, 36 * the Engine is responsible for maintaining the local client's <tt>worldTime</tt>. 37 * <p/> 38 * The engine includes methods to load scripts and stylesheets in a serialized fashion 39 * and report on the sound engine status. Since objects are tracked by the engine, a list 40 * of all game objects can be obtained from the engine. The engine also contains the root 41 * rendering context, or "default" context. For anything to be rendered, or updated by the 42 * engine, it will need to be added to a child of the default context. 43 * <p/> 44 * Other methods allow for starting or shutting down then engine, toggling metric display, 45 * setting of the base "frames per second", toggling of the debug mode, and processing of 46 * the script and function queue. 47 * <p/> 48 * Since JavaScript is a single-threaded environment, frames are generated serially. One 49 * frame must complete before another can be rendered. By default, if frames are missed, 50 * the engine will wait until the next logical frame can be rendered. The engine can also 51 * run where it doesn't skip frames, and instead runs a constant frame clock. This 52 * doesn't guarantee that the engine will run at a fixed frame rate. 53 * 54 * @static 55 */ 56 R.Engine = Base.extend(/** @scope R.Engine.prototype */{ 57 version:"@ENGINE_VERSION", 58 HOME_URL:"@HOME_URL", 59 REF_NAME:"@REF_NAME", 60 61 constructor:null, 62 63 // Global engine options 64 options:{}, 65 66 /* 67 * Engine objects 68 */ 69 idRef:0, // Object reference Id 70 gameObjects:{}, // Live objects cache 71 timerPool:{}, // Pool of running timers 72 livingObjects:0, // Count of live objects 73 74 /* 75 * Engine info 76 */ 77 fpsClock:16, // The clock rate (ms) 78 FPS:undefined, // Calculated frames per second 79 frameTime:0, // Amount of time taken to render a frame 80 engineLocation:null, // URI of engine 81 defaultContext:null, // The default rendering context 82 debugMode:false, // Global debug flag 83 localMode:false, // Local run flag 84 started:false, // Engine started flag 85 running:false, // Engine running flag 86 shuttingDown:false, // Engine is shutting down 87 upTime:0, // The startup time 88 downTime:0, // The shutdown time 89 skipFrames:true, // Skip missed frames 90 totalFrames:0, 91 droppedFrames:0, 92 pclRebuilds:0, 93 94 /* 95 * Sound engine info 96 */ 97 soundsEnabled:false, // Sound engine enabled flag 98 99 /** 100 * The current time of the world on the client. This time is updated 101 * for each frame generated by the Engine. 102 * @type {Number} 103 * @memberOf R.Engine 104 */ 105 worldTime:0, // The world time 106 107 /** @private */ 108 lastTime:0, // The last timestamp the world was drawn 109 110 /** 111 * The number of milliseconds the engine has been running. This time is updated 112 * for each frame generated by the Engine. 113 * @type {Number} 114 * @memberOf R.Engine 115 */ 116 liveTime:0, // The "alive" time (worldTime-upTime) 117 118 /** @private */ 119 shutdownCallbacks:[], // Methods to call when the engine is shutting down 120 121 $GAME:null, // Reference to the game object 122 123 // Issue #18 - Intrinsic loading dialog 124 loadingCSS:"<style type='text/css'>div.loadbox {width:325px;height:30px;padding:10px;font:10px Arial;border:1px outset gray;-moz-border-radius:10px;-webkit-border-radius:10px} #engine-load-progress { position:relative;border:1px inset gray;width:300px;height:5px} #engine-load-progress .bar {background:silver;}</style>", 125 126 //==================================================================================================== 127 //==================================================================================================== 128 // ENGINE PROPERTIES 129 //==================================================================================================== 130 //==================================================================================================== 131 132 /** 133 * Set/override the engine options. 134 * @param opts {Object} Configuration options for the engine 135 * @memberOf R.Engine 136 * @private 137 */ 138 setOptions:function (opts) { 139 // Check for a "defaults" key 140 var configOpts; 141 if (opts.defaults) { 142 configOpts = opts.defaults; 143 } 144 145 // See if the OS has a key 146 var osOpts, platformDefaults, versionDefaults, platformVersions; 147 if (opts["platforms"] && opts["platforms"][R.engine.Support.sysInfo().OS]) { 148 // Yep, extract that one 149 osOpts = opts["platforms"][R.engine.Support.sysInfo().OS]; 150 151 // Check for platform defaults 152 if (osOpts && osOpts["defaults"]) { 153 platformDefaults = osOpts["defaults"]; 154 } 155 } 156 157 // Check for general version options 158 if (opts["versions"]) { 159 versionDefaults = {}; 160 for (var v in opts["versions"]) { 161 if (R.engine.Support.sysInfo().version == v) { 162 // Add version specific matches 163 versionDefaults = $.extend(versionDefaults, opts["versions"][v]); 164 } 165 166 if (parseFloat(R.engine.Support.sysInfo().version) >= parseFloat(v)) { 167 // Add version match options 168 versionDefaults = $.extend(versionDefaults, opts["versions"][v]); 169 } 170 } 171 } 172 173 // Finally, check the OS for version options 174 if (osOpts && osOpts["versions"]) { 175 platformVersions = {}; 176 for (var v in osOpts["versions"]) { 177 if (R.engine.Support.sysInfo().version == v) { 178 // Add version specific options 179 platformVersions = $.extend(platformVersions, osOpts["versions"][v]); 180 } 181 182 if (parseFloat(R.engine.Support.sysInfo().version) >= parseFloat(v)) { 183 // Add version match options 184 platformVersions = $.extend(platformVersions, osOpts["versions"][v]); 185 } 186 } 187 } 188 189 $.extend(R.Engine.options, configOpts, platformDefaults, versionDefaults, platformVersions); 190 }, 191 192 /** 193 * Set the debug mode of the engine. Engine debugging enables helper objects 194 * which visually assist in debugging game objects. To specify the console debug 195 * message output level, see {@link R.debug.Console@setDebuglevel}. 196 * <p/> 197 * Engine debug helper objects include: 198 * <ul> 199 * <li>A left/up glyph at the origin of objects using the {@link R.components.Transform2D} component</li> 200 * <li>Yellow outline in the shape of the collision hull of {@link R.objects.Object2D}, if assigned</li> 201 * <li>Yellow outline around objects using box or circle collider components</li> 202 * <li>Green outline around objects which are rendered with the {@link R.components.Billboard2D} component</li> 203 * <li>Blue outline around box and circle rigid body objects</li> 204 * <li>Red lines from anchor points in jointed {@link R.objects.PhysicsActor} objects</li> 205 * </ul> 206 * 207 * @param mode {Boolean} <tt>true</tt> to enable debug mode 208 * @memberOf R.Engine 209 */ 210 setDebugMode:function (mode) { 211 R.Engine.debugMode = mode; 212 }, 213 214 /** 215 * Query the debugging mode of the engine. 216 * 217 * @return {Boolean} <tt>true</tt> if the engine is in debug mode 218 * @memberOf R.Engine 219 */ 220 getDebugMode:function () { 221 return R.Engine.debugMode; 222 }, 223 224 /** 225 * Returns <tt>true</tt> if SoundManager2 is loaded and initialized 226 * properly. The resource loader and play manager will use this 227 * value to execute properly. 228 * @return {Boolean} <tt>true</tt> if the sound engine was loaded properly 229 * @memberOf R.Engine 230 */ 231 isSoundEnabled:function () { 232 return R.Engine.soundsEnabled; 233 }, 234 235 /** 236 * Set the FPS (frames per second) the engine runs at. This value 237 * is mainly a suggestion to the engine as to how fast you want to 238 * redraw frames. If frame execution time is long, frames will be 239 * processed as time is available. See the metrics to understand 240 * available time versus render time. 241 * 242 * @param fps {Number} The number of frames per second to refresh 243 * Engine objects. 244 * @memberOf R.Engine 245 */ 246 setFPS:function (fps) { 247 Assert((fps != 0), "You cannot have a framerate of zero!"); 248 R.Engine.fpsClock = Math.floor(1000 / fps); 249 R.Engine.FPS = undefined; 250 }, 251 252 /** 253 * Get the FPS (frames per second) the engine is set to run at. 254 * @return {Number} 255 * @memberOf R.Engine 256 */ 257 getFPS:function () { 258 if (!R.Engine.FPS) { 259 R.Engine.FPS = Math.floor((1 / R.Engine.fpsClock) * 1000); 260 } 261 return R.Engine.FPS; 262 }, 263 264 /** 265 * Get the actual FPS (frames per second) the engine is running at. 266 * This value will vary as load increases or decreases due to the 267 * number of objects being rendered. A faster machine will be able 268 * to handle a higher FPS setting. 269 * @return {Number} 270 * @memberOf R.Engine 271 */ 272 getActualFPS:function () { 273 return Math.floor((1 / R.Engine.frameTime) * 1000); 274 }, 275 276 /** 277 * Get the amount of time allocated to draw a single frame. 278 * @return {Number} Milliseconds allocated to draw a frame 279 * @memberOf R.Engine 280 */ 281 getFrameTime:function () { 282 return R.Engine.fpsClock; 283 }, 284 285 /** 286 * Get the amount of time it took to draw the last frame. This value 287 * varies per frame drawn, based on visible objects, number of operations 288 * performed, and other factors. The draw time can be used to optimize 289 * your game for performance. 290 * @return {Number} Milliseconds required to draw the frame 291 * @memberOf R.Engine 292 */ 293 getDrawTime:function () { 294 return R.Engine.frameTime; 295 }, 296 297 /** 298 * Get the load the currently rendered frame is putting on the engine. 299 * The load represents the amount of 300 * work the engine is doing to render a frame. A value less 301 * than one indicates the the engine can render a frame within 302 * the amount of time available. Higher than one indicates the 303 * engine cannot render the frame in the time available. 304 * <p/> 305 * Faster machines will be able to handle more load. You can use 306 * this value to gauge how well your game is performing. 307 * @return {Number} 308 * @memberOf R.Engine 309 */ 310 getEngineLoad:function () { 311 return (R.Engine.frameTime / R.Engine.fpsClock); 312 }, 313 314 /** 315 * Get the default rendering context for the Engine. This 316 * is the <tt>document.body</tt> element in the browser. 317 * 318 * @return {RenderContext} The default rendering context 319 * @memberOf R.Engine 320 */ 321 getDefaultContext:function () { 322 if (R.Engine.defaultContext == null) { 323 R.Engine.defaultContext = R.rendercontexts.DocumentContext.create(); 324 } 325 326 return R.Engine.defaultContext; 327 }, 328 329 /** 330 * Override the engine's default context. The engine will use 331 * the {@link R.rendercontexts.DocumentContext} as the default context, 332 * unless otherwise specified. 333 * @param defaultContext {R.rendercontexts.AbstracRenderContext} The context to use as the start of the 334 * scene graph. 335 * @memberOf R.Engine 336 */ 337 setDefaultContext:function (defaultContext) { 338 Assert(defaultContext instanceof R.rendercontexts.AbstractRenderContext, "Setting default engine context to object which is not a render context!"); 339 R.Engine.defaultContext = defaultContext; 340 }, 341 342 /** 343 * Get the game object that has been loaded by the engine. The game object isn't valid until the game is loaded. 344 * @return {R.engine.Game} 345 */ 346 getGame:function () { 347 return R.Engine.$GAME; 348 }, 349 350 /** 351 * Get the path to the engine. Uses the location of the <tt>/runtime/engine.js</tt> 352 * file that was initially loaded to determine the URL where the engine is running from. 353 * When files are included, or classes are loaded, they are loaded relative to the engine's 354 * location on the server. 355 * 356 * @return {String} The path/URL where the engine is located 357 * @memberOf R.Engine 358 */ 359 getEnginePath:function () { 360 if (R.Engine.engineLocation == null) { 361 // Determine the path of the "engine.js" file 362 var head = document.getElementsByTagName("head")[0]; 363 var scripts = head.getElementsByTagName("script"); 364 for (var x = 0; x < scripts.length; x++) { 365 var src = scripts[x].src; 366 var m = src.match(/(.*\/engine)\/runtime\/engine\.js/); 367 if (src != null && m) { 368 // Get the path 369 R.Engine.engineLocation = m[1]; 370 break; 371 } 372 } 373 } 374 375 return R.Engine.engineLocation; 376 }, 377 378 //==================================================================================================== 379 //==================================================================================================== 380 // GLOBAL OBJECT MANAGEMENT 381 //==================================================================================================== 382 //==================================================================================================== 383 384 /** 385 * Create an instance of an object within the Engine and get a unique Id for it. 386 * This is called by any object that extends from {@link R.engine.PooledObject}. 387 * 388 * @param obj {R.engine.PooledObject} An object within the engine 389 * @return {String} The global Id of the object 390 * @memberOf R.Engine 391 */ 392 create:function (obj) { 393 if (R.Engine.shuttingDown === true) { 394 R.debug.Console.warn("Engine shutting down, '" + obj + "' destroyed because it would create an orphaned reference"); 395 obj.destroy(); 396 return null; 397 } 398 399 Assert((R.Engine.started === true), "Creating an object when the engine is stopped!", obj); 400 401 R.Engine.idRef++; 402 var objId = obj.getName() + R.Engine.idRef; 403 R.debug.Console.log("CREATED Object ", objId, "[", obj, "]"); 404 R.Engine.livingObjects++; 405 406 return objId; 407 }, 408 409 /** 410 * Destroys an object instance within the Engine. 411 * 412 * @param obj {R.engine.PooledObject} The object, managed by the engine, to destroy 413 * @memberOf R.Engine 414 */ 415 destroy:function (obj) { 416 if (obj == null) { 417 R.debug.Console.warn("NULL reference passed to Engine.destroy()! Ignored."); 418 return; 419 } 420 421 var objId = obj.getId(); 422 R.debug.Console.log("DESTROYED Object ", objId, "[", obj, "]"); 423 R.Engine.livingObjects--; 424 }, 425 426 /** 427 * Add a timer to the pool so it can be cleaned up when 428 * the engine is shutdown, or paused when the engine is 429 * paused. 430 * @param timerName {String} The timer name 431 * @param timer {R.lang.Timer} The timer to add 432 * @memberOf R.Engine 433 */ 434 addTimer:function (timerName, timer) { 435 R.Engine.timerPool[timerName] = timer; 436 }, 437 438 /** 439 * Remove a timer from the pool when it is destroyed. 440 * @param timerName {String} The timer name 441 * @memberOf R.Engine 442 */ 443 removeTimer:function (timerName) { 444 R.Engine.timerPool[timerName] = null; 445 delete R.Engine.timerPool[timerName]; 446 }, 447 448 /** 449 * Get an object by the Id that was assigned during the call to {@link #create}. 450 * Only objects that are contained within other objects will be found. Discreetly 451 * referenced objects cannot be located by Id. 452 * 453 * @param id {String} The Id of the object to locate 454 * @return {R.engine.PooledObject} The object 455 * @memberOf R.Engine 456 */ 457 getObject:function (id) { 458 function search(container) { 459 var itr = container.iterator(); 460 while (itr.hasNext()) { 461 var obj = itr.next(); 462 if (obj.getId && (obj.getId() === id)) { 463 itr.destroy(); 464 return obj; 465 } 466 if (obj instanceof R.struct.Container) { 467 // If the object is a container, search inside of it 468 return search(obj); 469 } 470 } 471 itr.destroy(); 472 return null; 473 } 474 475 // Start at the engine's default context 476 return search(R.Engine.getDefaultContext()); 477 }, 478 479 //==================================================================================================== 480 //==================================================================================================== 481 // ENGINE PROCESS CONTROL 482 //==================================================================================================== 483 //==================================================================================================== 484 485 /** 486 * Load the minimal scripts required for the engine to start. 487 * @private 488 * @memberOf R.Engine 489 */ 490 loadEngineScripts:function () { 491 // Engine stylesheet 492 R.engine.Script.loadStylesheet("/css/engine.css"); 493 494 // The basics needed by the engine to get started 495 R.engine.Linker._doLoad("R.engine.Game"); 496 R.engine.Linker._doLoad("R.engine.PooledObject"); 497 R.engine.Linker._doLoad("R.lang.Iterator"); 498 R.engine.Linker._doLoad("R.rendercontexts.AbstractRenderContext"); 499 R.engine.Linker._doLoad("R.rendercontexts.RenderContext2D"); 500 R.engine.Linker._doLoad("R.rendercontexts.HTMLElementContext"); 501 R.engine.Linker._doLoad("R.rendercontexts.DocumentContext"); 502 503 // Load the timers so that we don't require developers to do it 504 R.engine.Linker._doLoad("R.lang.AbstractTimer"); 505 R.engine.Linker._doLoad("R.lang.IntervalTimer"); 506 R.engine.Linker._doLoad("R.lang.MultiTimeout"); 507 R.engine.Linker._doLoad("R.lang.OneShotTimeout"); 508 R.engine.Linker._doLoad("R.lang.OneShotTrigger"); 509 R.engine.Linker._doLoad("R.lang.Timeout"); 510 }, 511 512 /** 513 * Starts the engine and loads the basic engine scripts. When all scripts required 514 * by the engine have been loaded the {@link #run} method will be called. 515 * 516 * @param debugMode {Boolean} <tt>true</tt> to set the engine into debug mode 517 * which allows the output of messages to the console. 518 * @memberOf R.Engine 519 */ 520 startup:function (debugMode) { 521 Assert((R.Engine.running == false), "An attempt was made to restart the engine!"); 522 523 // Check for supported browser 524 if (!R.Engine.browserSupportCheck()) { 525 return false; 526 } 527 528 R.Engine.upTime = R.now(); 529 //R.Engine.debugMode = debugMode ? true : false; 530 R.Engine.started = true; 531 R.Engine.totalFrames = 0; 532 533 // Load the required scripts 534 R.Engine.loadEngineScripts(); 535 return true; 536 }, 537 538 /** 539 * Starts or resumes the engine. This will be called after all scripts have been loaded. 540 * You will also need to call this if you {@link #pause} the engine. Any paused timers 541 * will also be resumed. 542 * @memberOf R.Engine 543 */ 544 run:function () { 545 if (R.Engine.shuttingDown || R.Engine.running) { 546 return; 547 } 548 549 // Restart all of the timers 550 for (var tm in R.Engine.timerPool) { 551 R.Engine.timerPool[tm].restart(); 552 } 553 554 var mode = "["; 555 mode += (R.Engine.debugMode ? "DEBUG" : ""); 556 mode += (R.Engine.localMode ? (mode.length > 0 ? " LOCAL" : "LOCAL") : ""); 557 mode += "]"; 558 R.debug.Console.warn(">>> Engine started. " + (mode != "[]" ? mode : "")); 559 R.Engine.running = true; 560 R.Engine.shuttingDown = false; 561 562 R.debug.Console.debug(">>> sysinfo: ", R.engine.Support.sysInfo()); 563 564 R.Engine._pauseTime = R.now(); 565 R.Engine._stepOne = 0; 566 R.Engine.lastTime = R.now() - R.Engine.fpsClock; 567 568 // Start world timer 569 R.Engine.engineTimer(); 570 }, 571 572 /** 573 * Steps the engine when paused. Any timers that were paused, stay paused while stepping. 574 * @memberOf R.Engine 575 */ 576 step:function () { 577 if (R.Engine.running) { 578 // Need to pause the engine to step 579 return; 580 } 581 582 R.Engine._stepOne = 1; 583 R.Engine.engineTimer(); 584 }, 585 586 /** 587 * Pauses the engine and any running timers. 588 * @memberOf R.Engine 589 */ 590 pause:function () { 591 if (R.Engine.shuttingDown) { 592 return; 593 } 594 595 // Pause all of the timers 596 R.debug.Console.debug("Pausing all timers"); 597 for (var tm in R.Engine.timerPool) { 598 R.Engine.timerPool[tm].pause(); 599 } 600 601 R.debug.Console.warn(">>> Engine paused <<<"); 602 window.clearTimeout(R.Engine.globalTimer); 603 R.Engine.running = false; 604 R.Engine._pauseTime = R.now(); 605 }, 606 607 /** 608 * Add a method to be called when the engine is being shutdown. Use this 609 * method to allow an object, which is not referenced by the engine, to 610 * perform cleanup actions. 611 * 612 * @param fn {Function} The callback function 613 * @memberOf R.Engine 614 */ 615 onShutdown:function (fn) { 616 if (R.Engine.shuttingDown === true) { 617 return; 618 } 619 620 R.Engine.shutdownCallbacks.push(fn); 621 }, 622 623 /** 624 * Shutdown the engine. Stops the global timer and cleans up (destroys) all 625 * objects that have been created and added to the engine, starting at the default 626 * engine context. 627 * @memberOf R.Engine 628 */ 629 shutdown:function () { 630 if (R.Engine.shuttingDown) { 631 // Prevent another shutdown 632 return; 633 } 634 635 R.Engine.shuttingDown = true; 636 637 if (!R.Engine.running && R.Engine.started) { 638 // If the engine is not currently running (i.e. paused) 639 // restart it and then re-perform the shutdown 640 R.Engine.running = true; 641 setTimeout(function () { 642 R.Engine.shutdown(); 643 }, (R.Engine.fpsClock * 2)); 644 return; 645 } 646 647 R.Engine.started = false; 648 R.debug.Console.warn(">>> Engine shutting down..."); 649 650 // Stop world timer 651 R.global.clearTimeout(R.Engine.globalTimer); 652 653 // Run through shutdown callbacks to allow unreferenced objects 654 // to clean up references, etc. 655 while (R.Engine.shutdownCallbacks.length > 0) { 656 R.Engine.shutdownCallbacks.shift()(); 657 } 658 659 if (R.Engine.metricDisplay) { 660 R.Engine.metricDisplay.remove(); 661 R.Engine.metricDisplay = null; 662 } 663 664 // Cancel all of the timers 665 R.debug.Console.debug(">>> Cancelling all timers"); 666 for (var tm in R.Engine.timerPool) { 667 R.Engine.timerPool[tm].cancel(); 668 } 669 R.Engine.timerPool = {}; 670 671 R.Engine.downTime = R.now(); 672 R.debug.Console.warn(">>> Engine stopped. Runtime: " + (R.Engine.downTime - R.Engine.upTime) + "ms"); 673 R.debug.Console.warn(">>> frames generated: ", R.Engine.totalFrames); 674 675 R.Engine.running = false; 676 677 // Kill off the default context and anything 678 // that's attached to it. We'll alert the 679 // developer if there's an issue with orphaned objects 680 R.Engine.getDefaultContext().destroy(); 681 682 // Dump the object pool 683 R.engine.PooledObject.objectPool = null; 684 685 AssertWarn((R.Engine.livingObjects == 0), "Object references were not cleaned up!"); 686 687 R.Engine.loadedScripts = {}; 688 R.Engine.scriptLoadCount = 0; 689 R.Engine.scriptsProcessed = 0; 690 R.Engine.defaultContext = null; 691 692 // Shutdown complete 693 R.Engine.shuttingDown = false; 694 }, 695 696 /** 697 * See {@link #define} instead. 698 * @deprecated 699 * @memberOf R.Engine 700 */ 701 initObject:function (objectName, primaryDependency, fn) { 702 throw new Error("Unsupported - See R.Engine.define() instead"); 703 }, 704 705 /** 706 * Defines a new class. The format of the object definition is: 707 * <pre> 708 * R.Engine.define({ 709 * "class": "[class name]", 710 * "requires": [ 711 * "R.[package name].[dependency]" 712 * ], 713 * "depends": [ 714 * "[dependency]" 715 * ], 716 * "includes": [ 717 * "/path/to/file.js" 718 * ] 719 * }); 720 * </pre> 721 * Each class must define its class name via the "class" key. This is the name that 722 * other classes will use to locate the class object. The <tt>"requires"</tt> key defines the 723 * classes within the engine that the class is dependent upon. Anything that falls into 724 * the <tt>"R."</tt> namespace should be declared as a requirement here. The "requires" key 725 * performs class loading for these objects automatically. In other words, you do not need 726 * to load classes which start with <tt>"R."</tt>. 727 * <p/> 728 * If your class has dependencies on classes <i>not defined in the <tt>"R"</tt> namespace</i>, 729 * they should be declared via the <tt>"depends"</tt> array. These are classes which your game 730 * classes need to load via {@link R.engine.Game#load} calls. For files which just need to be 731 * loaded, use the <tt>"include"</tt> key to tell the engine where the file is. 732 * <p/> 733 * Until all requirements, dependencies, and included files have been loaded and/or initialized, 734 * a class will, itself, not be initialized. Be aware of class dependencies so you do not create 735 * circular dependencies. First-level circular dependencies are okay, such as <tt>A</tt> requires 736 * <tt>B</tt>, while <tt>B</tt> requires <tt>A</tt>. But second, third, and so on circular 737 * dependencies will cause your classes to remain unresolved. The engine will not start the game, 738 * and an erro message will be sent to the console listing classes which were resolved and those 739 * which are unresolved. 740 * <p/> 741 * The <tt>"requires"</tt>, <tt>"includes"</tt> and <tt>"depends"</tt> keys are optional. You 742 * can either omit them entirely, set them to <code>null</code>, or assign an empty array to them. 743 * <p/> 744 * The <tt>"depends"</tt> key is the only way your game classes can establish class dependencies 745 * which are <i>not in the <tt>"R."</tt> namespace</i>. Classes specified via the 746 * <tt>"depends"</tt> key are not loaded via the engine class loader like <tt>"requires"</tt> 747 * does. Instead, your game will need to load the classes. For example: 748 * <pre> 749 * R.Engine.define({ 750 * "class": "Foo", 751 * "requires": [ 752 * "R.rendercontexts.CanvasContext" 753 * ], 754 * "depends": [ 755 * "Bar" 756 * ] 757 * }); 758 * 759 * // Load the Bar class 760 * R.engine.Game.load("bar.js"); 761 * </pre> 762 * After receiving the definition, the engine will load <tt>R.rendercontexts.CanvasContext</tt> 763 * for <tt>Foo</tt>. The call to <code>R.engine.Game.load("bar.js")</code> would load the 764 * <tt>Bar</tt> class. When the context and <tt>Bar</tt> have loaded and initialized, <tt>Foo</tt> 765 * can be initialized which will enable any classes dependent on <tt>Foo</tt> to be initialized. 766 * 767 * @param classDef {Object} The object's definition 768 * @memberOf R.Engine 769 */ 770 define:function (classDef) { 771 R.engine.Linker.define(classDef); 772 }, 773 774 /** 775 * Check the current browser to see if it is supported by the 776 * engine. If it isn't, there's no reason to load the remainder of 777 * the engine. This check can be disabled with the <tt>disableBrowserCheck</tt> 778 * query parameter set to <tt>true</tt>. 779 * <p/> 780 * If the browser isn't supported, the engine is shutdown and a message is 781 * displayed. 782 * @memberOf R.Engine 783 * @private 784 */ 785 browserSupportCheck:function () { 786 if (R.engine.Support.checkBooleanParam("disableBrowserCheck")) { 787 return true; 788 } 789 var sInfo = R.engine.Support.sysInfo(); 790 var msg = "This browser is not currently supported by <i>" + R.Engine.REF_NAME + "</i>.<br/><br/>"; 791 msg += "Please go <a href='" + R.Engine.HOME_URL + "' target='_blank'>here</a> for more information."; 792 switch (sInfo.browser) { 793 case "iPhone": 794 case "android": 795 case "msie": 796 case "chrome": 797 case "Wii": 798 case "safari": 799 case "safarimobile": 800 case "mozilla": 801 case "firefox": 802 case "opera": 803 return true; 804 default: 805 R.debug.Console.warn("Unsupported Browser"); 806 $("body", document).empty().append($("<div style='font:12pt Arial,sans-serif;'>").html(msg)); 807 return false; 808 } 809 }, 810 811 /** 812 * Prints the version of the engine. 813 * @memberOf R.Engine 814 */ 815 toString:function () { 816 return "The Render Engine " + R.Engine.version; 817 }, 818 819 //==================================================================================================== 820 //==================================================================================================== 821 // THE WORLD TIMER 822 //==================================================================================================== 823 //==================================================================================================== 824 825 /** 826 * This is the process which updates the world. It starts with the default 827 * context, telling it to update itself. Since each context is a container, 828 * all of the objects in the container will be called to update, and then 829 * render themselves. 830 * 831 * @private 832 * @memberOf R.Engine 833 */ 834 engineTimer:function () { 835 if (R.Engine.shuttingDown) { 836 return; 837 } 838 839 if (!R.Engine.running && R.Engine._stepOne == 0) { 840 // Not stepping, done here 841 return; 842 } 843 844 var nextFrame = R.Engine.fpsClock; 845 846 // Update the world 847 if ((R.Engine._stepOne == 1 || R.Engine.running) && R.Engine.getDefaultContext() != null) { 848 R.Engine.vObj = 0; 849 R.Engine.rObjs = 0; 850 //R.Engine.pclRebuilds = 0; 851 852 853 // Render a frame 854 R.Engine.worldTime = R.Engine._stepOne == 1 ? R.Engine._pauseTime : R.now(); 855 R.Engine.lastTime = R.Engine._stepOne == 1 ? R.Engine.worldTime - R.Engine.fpsClock : R.Engine.lastTime; 856 857 var deltaTime = R.Engine.worldTime - R.Engine.lastTime; 858 859 // Tick the game 860 if (R.Engine.$GAME) { 861 R.Engine.$GAME.tick(R.Engine.worldTime, deltaTime); 862 } 863 864 // Pass parent context, world time, delta time 865 R.Engine.getDefaultContext().update(null, R.Engine.worldTime, deltaTime); 866 R.Engine.lastTime = R.Engine.worldTime; 867 R.Engine.frameTime = R.now() - R.Engine.worldTime; 868 869 if (R.Engine._stepOne == 1) { 870 R.Engine._pauseTime += R.Engine.frameTime; 871 } 872 873 R.Engine.liveTime = R.Engine.worldTime - R.Engine.upTime; 874 875 // Count the number of frames generated 876 R.Engine.totalFrames++; 877 878 // Determine when the next frame should draw 879 // If we've gone over the allotted time, wait until the next available frame 880 var f = nextFrame - R.Engine.frameTime; 881 nextFrame = (R.Engine.skipFrames ? (f > 0 ? f : nextFrame) : R.Engine.fpsClock); 882 R.Engine.droppedFrames += (f <= 0 ? Math.round((f * -1) / R.Engine.fpsClock) : 0); 883 884 // Update the metrics display 885 R.Engine.doMetrics(); 886 } 887 888 if (R.Engine._stepOne == 1) { 889 // If stepping, don't re-call the engine timer automatically 890 R.Engine._stepOne = 0; 891 return; 892 } 893 894 // When the process is done, start all over again 895 if (R.Engine.options.nativeAnimationFrame) { 896 R.global.nativeFrame(R.Engine.engineTimer /*, R.Engine.getDefaultContext().getSurface()*/); 897 } else { 898 R.Engine.globalTimer = setTimeout(function _engineTimer() { 899 R.Engine.engineTimer(); 900 }, nextFrame); 901 } 902 }, 903 904 /** 905 * @private 906 */ 907 doMetrics:function () { 908 if (R.debug && R.debug.Metrics) { 909 R.debug.Metrics.doMetrics(); 910 } 911 }, 912 913 // ====================================================== 914 // References to R.engine.Script methods 915 // ====================================================== 916 917 /** 918 * Include a script file. 919 * 920 * @param scriptURL {String} The URL of the script file 921 * @memberOf R.Engine 922 */ 923 include:function (scriptURL) { 924 R.engine.Script.include(scriptURL); 925 }, 926 927 /** 928 * Loads a game's script. This will wait until the specified 929 * <tt>gameObjectName</tt> is available before running it. Doing so will 930 * ensure that all dependencies have been resolved before starting a game. 931 * Also creates the default rendering context for the engine. 932 * <p/> 933 * All games should execute this method to start their processing, rather than 934 * using the script loading mechanism for engine or game scripts. This is used 935 * for the main game script only. Normally it would appear in the game's "index" file. 936 * <pre> 937 * <script type="text/javascript"> 938 * // Load the game script 939 * Engine.loadGame('game.js','Spaceroids'); 940 * </script> 941 * </pre> 942 * 943 * @param gameSource {String} The URL of the game script. 944 * @param gameObjectName {String} The string name of the game object to execute. When 945 * the framework if ready, the <tt>startup()</tt> method of this 946 * object will be called. 947 * @param [gameDisplayName] {String} An optional string to display in the loading dialog 948 * @memberOf R.Engine 949 */ 950 loadGame:function (gameSource, gameObjectName, gameDisplayName) { 951 R.engine.Script.loadGame(gameSource, gameObjectName, gameDisplayName); 952 } 953 954 }, { // Interface 955 /** @private */ 956 globalTimer:null 957 }); 958 959