1 var jaws = (function(jaws) { 2 3 // requestAnim shim layer by Paul Irish 4 window.requestAnimFrame = (function(){ 5 return window.requestAnimationFrame || 6 window.webkitRequestAnimationFrame || 7 window.mozRequestAnimationFrame || 8 window.oRequestAnimationFrame || 9 window.msRequestAnimationFrame || 10 function(/* function */ callback, /* DOMElement */ element){ 11 window.setTimeout(callback, 16.666); 12 }; 13 })(); 14 15 /** 16 * @class A classic game loop forever looping calls to update() / draw() with given framerate. "Field Summary" contains options for the GameLoop()-constructor. 17 * 18 * @property {int} tick_duration duration in ms between the last 2 ticks (often called dt) 19 * @property {int} fps the real fps (as opposed to the target fps), smoothed out with a moving average 20 * @property {int} ticks total amount of ticks since game loops start 21 * 22 * @example 23 * 24 * game = {} 25 * draw: function() { ... your stuff executed every 30 FPS ... } 26 * } 27 * 28 * game_loop = new jaws.GameLoop(game, {fps: 30}) 29 * game_loop.start() 30 * 31 * // You can also use the shortcut jaws.start(), it will: 32 * // 1) Load all assets with jaws.assets.loadAll() 33 * // 2) Create a GameLoop() and start it 34 * jaws.start(MyGameState, {fps: 30}) 35 * 36 */ 37 jaws.GameLoop = function GameLoop(game_object, options, game_state_setup_options) { 38 if( !(this instanceof arguments.callee) ) return new arguments.callee( game_object, options ); 39 40 this.tick_duration = 0 41 this.fps = 0 42 this.ticks = 0 43 44 var update_id 45 var paused = false 46 var stopped = false 47 var that = this 48 var mean_value = new MeanValue(20) // let's have a smooth, non-jittery FPS-value 49 50 /** 51 * returns how game_loop has been active in milliseconds 52 * does currently not factor in pause-time 53 */ 54 this.runtime = function() { 55 return (this.last_tick - this.first_tick) 56 } 57 58 /** Start the game loop by calling setup() once and then loop update()/draw() forever with given FPS */ 59 this.start = function() { 60 jaws.log.info("Game loop start", true) 61 62 this.first_tick = (new Date()).getTime(); 63 this.current_tick = (new Date()).getTime(); 64 this.last_tick = (new Date()).getTime(); 65 66 if(options.setup !== false && game_object.setup) { game_object.setup(game_state_setup_options) } 67 step_delay = 1000 / options.fps; 68 69 if(options.fps == 60) { 70 requestAnimFrame(this.loop) 71 } 72 else { 73 update_id = setInterval(this.loop, step_delay); 74 } 75 } 76 77 /** The core of the game loop. Calculate a mean FPS and call update()/draw() if game loop is not paused */ 78 this.loop = function() { 79 that.current_tick = (new Date()).getTime(); 80 that.tick_duration = that.current_tick - that.last_tick 81 that.fps = mean_value.add(1000/that.tick_duration).get() 82 83 if(!stopped && !paused) { 84 if(game_object.update) { game_object.update() } 85 if(game_object.draw) { game_object.draw() } 86 that.ticks++ 87 } 88 if(options.fps == 60 && !stopped) requestAnimFrame(that.loop); 89 that.last_tick = that.current_tick; 90 } 91 92 /** Pause the game loop. loop() will still get called but not update() / draw() */ 93 this.pause = function() { paused = true } 94 95 /** unpause the game loop */ 96 this.unpause = function() { paused = false } 97 98 /** Stop the game loop */ 99 this.stop = function() { 100 if(update_id) clearInterval(update_id); 101 stopped = true; 102 } 103 } 104 105 /** @ignore */ 106 function MeanValue(size) { 107 this.size = size 108 this.values = new Array(this.size) 109 this.value 110 111 this.add = function(value) { 112 if(this.values.length > this.size) { // is values filled? 113 this.values.splice(0,1) 114 this.value = 0 115 for(var i=0; this.values[i]; i++) { 116 this.value += this.values[i] 117 } 118 this.value = this.value / this.size 119 } 120 this.values.push(value) 121 122 return this 123 } 124 125 this.get = function() { 126 return parseInt(this.value) 127 } 128 129 } 130 131 return jaws; 132 })(jaws || {}); 133 134