From c6c579e6b3aa59d7b0af36daf0f88da3b324a4ed Mon Sep 17 00:00:00 2001 From: photonstorm Date: Thu, 9 Jan 2014 00:59:37 +0000 Subject: [PATCH] Phaser.Timer is now feature complete and fully documented. You can create Phaser.TimerEvents on a Timer and lots of new examples have been provided. --- Gruntfile.js | 1 + README.md | 4 +- build/config.php | 1 + examples/_site/examples.json | 26 ++ examples/_site/view_full.html | 1 + examples/_site/view_lite.html | 1 + examples/time/basic looped event.js | 43 +++ examples/time/basic repeat event.js | 47 +++ examples/time/basic timed event.js | 37 ++ examples/time/remove event.js | 55 +++ examples/time/timed slideshow.js | 94 +++++ examples/wip/timer simple.js | 2 + src/system/Canvas.js | 2 + src/time/Time.js | 528 +++++++++++++--------------- src/time/Timer.js | 231 +++++++++--- src/time/TimerEvent.js | 67 ++++ 16 files changed, 812 insertions(+), 328 deletions(-) create mode 100644 examples/time/basic looped event.js create mode 100644 examples/time/basic repeat event.js create mode 100644 examples/time/basic timed event.js create mode 100644 examples/time/remove event.js create mode 100644 examples/time/timed slideshow.js create mode 100644 src/time/TimerEvent.js diff --git a/Gruntfile.js b/Gruntfile.js index 6349f03fd..0087d9a37 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -99,6 +99,7 @@ module.exports = function (grunt) { 'src/tween/Easing.js', 'src/time/Time.js', 'src/time/Timer.js', + 'src/time/TimerEvent.js', 'src/animation/AnimationManager.js', 'src/animation/Animation.js', 'src/animation/Frame.js', diff --git a/README.md b/README.md index 01acebc7b..53a247062 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ Significant API changes: New features: +* Phaser.Timer is now feature complete and fully documented. You can create Phaser.TimerEvents on a Timer and lots of new examples have been provided. * Gamepad API support has been added with lots of new examples (thanks Karl Macklin) * Phaser.Game constructor can now be passed a single object containing all of your game settings + Stage settings. Useful for advanced configurations. * The width/height given to Phaser.Game can now be percentages, i.e. "100%" will set the width to the maximum window innerWidth. @@ -102,6 +103,7 @@ New Examples: * Particles - Rain by Jens Anders Bakke. * Particles - Snow by Jens Anders Bakke. * Groups - Nested Groups - showing how to embed one Group into another Group. +* Time - Lots of new examples showing how to use the Phaser.Timer class. Updates: @@ -132,7 +134,7 @@ Updates: * Button.clearFrames method has been added. * Device.quirksMode is a boolean that informs you if the page is in strict (false) or quirks (true) mode. * Canvas.getOffset now runs a strict/quirks check and uses document.documentElement when calculating scrollTop and scrollLeft to avoid Chrome console warnings. -* The Time class now has three new methods: addEvent, repeatEvent and loopEvent. See the new Timer examples to show how to use them. +* The Time class now has its own Phaser.Timer which you can access through game.time.events. See the new Timer examples to show how to use them. Bug Fixes: diff --git a/build/config.php b/build/config.php index d845049b6..1767e4387 100644 --- a/build/config.php +++ b/build/config.php @@ -113,6 +113,7 @@ + diff --git a/examples/_site/examples.json b/examples/_site/examples.json index 2bb70eae7..d57152713 100644 --- a/examples/_site/examples.json +++ b/examples/_site/examples.json @@ -350,6 +350,10 @@ "file": "group+transform.js", "title": "group transform" }, + { + "file": "nested+groups.js", + "title": "nested groups" + }, { "file": "recyling.js", "title": "recyling" @@ -797,6 +801,28 @@ "title": "swap tiles" } ], + "time": [ + { + "file": "basic+looped+event.js", + "title": "basic looped event" + }, + { + "file": "basic+repeat+event.js", + "title": "basic repeat event" + }, + { + "file": "basic+timed+event.js", + "title": "basic timed event" + }, + { + "file": "remove+event.js", + "title": "remove event" + }, + { + "file": "timed+slideshow.js", + "title": "timed slideshow" + } + ], "tweens": [ { "file": "bounce.js", diff --git a/examples/_site/view_full.html b/examples/_site/view_full.html index a85f08772..55b3717e2 100644 --- a/examples/_site/view_full.html +++ b/examples/_site/view_full.html @@ -118,6 +118,7 @@ + diff --git a/examples/_site/view_lite.html b/examples/_site/view_lite.html index e43cd752e..f934eb497 100644 --- a/examples/_site/view_lite.html +++ b/examples/_site/view_lite.html @@ -118,6 +118,7 @@ + diff --git a/examples/time/basic looped event.js b/examples/time/basic looped event.js new file mode 100644 index 000000000..67deca0c3 --- /dev/null +++ b/examples/time/basic looped event.js @@ -0,0 +1,43 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, render: render }); + +function preload() { + + game.load.image('ball', 'assets/sprites/pangball.png'); + +} + +var counter = 0; +var text = 0; + +function create() { + + game.stage.backgroundColor = '#6688ee'; + + text = game.add.text(game.world.centerX, game.world.centerY, 'Counter: 0', { font: "64px Arial", fill: "#ffffff", align: "center" }); + text.anchor.setTo(0.5, 0.5); + + // Here we'll create a basic looped event. + // A looped event is like a repeat event but with no limit, it will literally repeat itself forever, or until + + // The first parameter is how long to wait before the event fires. In this case 1 second (you could pass in 1000 as the value as well.) + // The next two parameters are the function to call ('updateCounter') and the context under which that will happen. + + game.time.events.loop(Phaser.Timer.SECOND, updateCounter, this); + +} + +function updateCounter() { + + counter++; + + text.content = 'Counter: ' + counter; + +} + +function render() { + + game.debug.renderText("Time until event: " + game.time.events.duration.toFixed(0), 32, 32); + game.debug.renderText("Next tick: " + game.time.events.next.toFixed(0), 32, 64); + +} diff --git a/examples/time/basic repeat event.js b/examples/time/basic repeat event.js new file mode 100644 index 000000000..63f15bee1 --- /dev/null +++ b/examples/time/basic repeat event.js @@ -0,0 +1,47 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, render: render }); + +function preload() { + + game.load.image('ball', 'assets/sprites/pangball.png'); + +} + +function create() { + + game.stage.backgroundColor = '#6688ee'; + + // Here we'll create a basic repeating event. + + // The way a repeating event works is that it is placed into the queue once, + // and when it runs its 'repeatCounter' is reduced by 1 and it's moved back into the queue again. + // To this end the queue will only ever have 1 event actually in it. + + // The first parameter is how long to wait before the event fires. In this case 2 seconds (you could pass in 2000 as the value as well.) + // The second parameter is how many times the event will run in total. Here we'll run it 10 times. + // The next two parameters are the function to call ('createBall') and the context under which that will happen. + + // Once the event has been called 10 times it will never be called again. + + game.time.events.repeat(Phaser.Timer.SECOND * 2, 10, createBall, this); + +} + +function createBall() { + + // A bouncey ball sprite just to visually see what's going on. + + var ball = game.add.sprite(game.world.randomX, 0, 'ball'); + + ball.body.gravity.y = 200; + ball.body.bounce.y = 0.5; + ball.body.collideWorldBounds = true; + +} + +function render() { + + game.debug.renderText("Time until event: " + game.time.events.duration.toFixed(0), 32, 32); + game.debug.renderText("Next tick: " + game.time.events.next.toFixed(0), 32, 64); + +} diff --git a/examples/time/basic timed event.js b/examples/time/basic timed event.js new file mode 100644 index 000000000..7c1ab20c3 --- /dev/null +++ b/examples/time/basic timed event.js @@ -0,0 +1,37 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, render: render }); + +function preload() { + + game.load.image('bisley', 'assets/pics/alex-bisleys_horsy_5.png'); + +} + +var picture; + +function create() { + + game.stage.backgroundColor = '#6688ee'; + + picture = game.add.sprite(game.world.centerX, game.world.centerY, 'bisley'); + picture.anchor.setTo(0.5, 0.5); + + // Here we'll create a basic timed event. This is a one-off event, it won't repeat or loop: + // The first parameter is how long to wait before the event fires. In this case 4 seconds (you could pass in 4000 as the value as well.) + // The next parameter is the function to call ('fadePicture') and finally the context under which that will happen. + + game.time.events.add(Phaser.Timer.SECOND * 4, fadePicture, this); + +} + +function fadePicture() { + + game.add.tween(picture).to( { alpha: 0 }, 2000, Phaser.Easing.Linear.None, true); + +} + +function render() { + + game.debug.renderText("Time until event: " + game.time.events.duration, 32, 32); + +} diff --git a/examples/time/remove event.js b/examples/time/remove event.js new file mode 100644 index 000000000..ce755721f --- /dev/null +++ b/examples/time/remove event.js @@ -0,0 +1,55 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { create: create, render: render }); + +var counters = []; +var text = []; +var timerEvents = []; +var i = 9; + +function create() { + + game.stage.backgroundColor = '#6688ee'; + + for (var i = 0; i < 10; i++) + { + counters[i] = 0; + text[i] = game.add.text(game.world.centerX, 80 + (40 * i), 'Counter ' + i + ' = 0', { font: "32px Arial", fill: "#ffffff", align: "center" }); + text[i].anchor.setTo(0.5, 0); + + // Here we create our timer events. They will be set to loop at a random value between 250ms and 1000ms + timerEvents[i] = game.time.events.loop(game.rnd.integerInRange(250, 1000), updateCounter, this, i); + } + + // Click to remove + game.input.onDown.add(removeCounter, this); + +} + +function updateCounter(idx) { + + counters[idx]++; + + text[idx].content = 'Counter ' + idx + ' = ' + counters[idx]; + +} + +function removeCounter() { + + if (i >= 0) + { + // Removes the timer, starting with the top one and working down + game.time.events.remove(timerEvents[i]); + + // Just updates the text + text[i].style.fill = '#3344aa'; + text[i].content = 'Counter ' + i + ' removed'; + i--; + } + +} + +function render() { + + game.debug.renderText("Queued events: " + game.time.events.length + ' - click to remove', 32, 32); + +} diff --git a/examples/time/timed slideshow.js b/examples/time/timed slideshow.js new file mode 100644 index 000000000..b8e6b11cc --- /dev/null +++ b/examples/time/timed slideshow.js @@ -0,0 +1,94 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, render: render }); + +function preload() { + + game.load.image('picture1', 'assets/pics/cougar_sanity_train.png'); + game.load.image('picture2', 'assets/pics/cougar-face_of_nature.png'); + game.load.image('picture3', 'assets/pics/destop-rewarding.png'); + game.load.image('picture4', 'assets/pics/destop-unknown.png'); + game.load.image('picture5', 'assets/pics/questar.png'); + game.load.image('picture6', 'assets/pics/seven_seas_andromeda_fairfax.png'); + game.load.image('picture7', 'assets/pics/slayer-sorry_im_the_beast.png'); + +} + +var pictureA; +var pictureB; +var timer; +var current = 3; + +function create() { + + game.stage.backgroundColor = '#000'; + + pictureA = game.add.sprite(game.world.centerX, game.world.centerY, 'picture1'); + pictureA.anchor.setTo(0.5, 0.5); + pictureA.scale.setTo(2, 2); + + pictureB = game.add.sprite(game.world.centerX, game.world.centerY, 'picture2'); + pictureB.anchor.setTo(0.5, 0.5); + pictureB.scale.setTo(2, 2); + pictureB.alpha = 0; + + // Create our Timer + timer = game.time.create(false); + + // Set a TimerEvent to occur after 3 seconds + timer.add(3000, fadePictures, this); + + // Start the timer running - this is important! + // It won't start automatically, allowing you to hook it to button events and the like. + timer.start(); + +} + +function fadePictures() { + + // Cross-fade the two pictures + var tween; + + if (pictureA.alpha === 1) + { + tween = game.add.tween(pictureA).to( { alpha: 0 }, 2000, Phaser.Easing.Linear.None, true); + game.add.tween(pictureB).to( { alpha: 1 }, 2000, Phaser.Easing.Linear.None, true); + } + else + { + game.add.tween(pictureA).to( { alpha: 1 }, 2000, Phaser.Easing.Linear.None, true); + tween = game.add.tween(pictureB).to( { alpha: 0 }, 2000, Phaser.Easing.Linear.None, true); + } + + // When the cross-fade is complete we swap the image being shown by the now hidden picture + tween.onComplete.add(changePicture, this); + +} + +function changePicture() { + + if (pictureA.alpha === 0) + { + pictureA.loadTexture('picture' + current); + } + else + { + pictureB.loadTexture('picture' + current); + } + + current++; + + if (current > 7) + { + current = 1; + } + + // And set a new TimerEvent to occur after 3 seconds + timer.add(3000, fadePictures, this); + +} + +function render() { + + game.debug.renderText("Time until event: " + timer.duration.toFixed(0), 10, 20); + +} diff --git a/examples/wip/timer simple.js b/examples/wip/timer simple.js index e5c69fd3d..f7b854294 100644 --- a/examples/wip/timer simple.js +++ b/examples/wip/timer simple.js @@ -14,6 +14,8 @@ function create() { game.stage.backgroundColor = '#007236'; + + // Every second we will call the addSprite function. This will happen 10 times and then stop. // The final parameter is the one that will be sent to the addSprite function and in this case is the sprite key. game.time.repeatEvent(Phaser.Timer.SECOND, 10, addSprite, this, 'mushroom'); diff --git a/src/system/Canvas.js b/src/system/Canvas.js index fb4fc74ff..76e81e03d 100644 --- a/src/system/Canvas.js +++ b/src/system/Canvas.js @@ -255,9 +255,11 @@ Phaser.Canvas = { */ setImageRenderingCrisp: function (canvas) { + canvas.style['image-rendering'] = 'optimizeSpeed'; canvas.style['image-rendering'] = 'crisp-edges'; canvas.style['image-rendering'] = '-moz-crisp-edges'; canvas.style['image-rendering'] = '-webkit-optimize-contrast'; + canvas.style['image-rendering'] = 'optimize-contrast'; canvas.style.msInterpolationMode = 'nearest-neighbor'; return canvas; diff --git a/src/time/Time.js b/src/time/Time.js index fbcf4d040..f2ef66bfc 100644 --- a/src/time/Time.js +++ b/src/time/Time.js @@ -14,121 +14,132 @@ */ Phaser.Time = function (game) { - /** - * @property {Phaser.Game} game - Local reference to game. - */ - this.game = game; + /** + * @property {Phaser.Game} game - Local reference to game. + */ + this.game = game; - /** - * @property {number} _started - The time at which the Game instance started. - * @private - */ - this._started = 0; + /** + * @property {number} physicsElapsed - The elapsed time calculated for the physics motion updates. + */ + this.physicsElapsed = 0; - /** - * @property {number} _timeLastSecond - The time (in ms) that the last second counter ticked over. - * @private - */ - this._timeLastSecond = 0; + /** + * @property {number} time - Game time counter. + */ + this.time = 0; - /** - * @property {number} _pauseStarted - The time the game started being paused. - * @private - */ - this._pauseStarted = 0; + /** + * @property {number} pausedTime - Records how long the game has been paused for. Is reset each time the game pauses. + */ + this.pausedTime = 0; - /** - * @property {number} physicsElapsed - The elapsed time calculated for the physics motion updates. - */ - this.physicsElapsed = 0; + /** + * @property {number} now - The time right now. + */ + this.now = 0; - /** - * @property {number} time - Game time counter. - */ - this.time = 0; + /** + * @property {number} elapsed - Elapsed time since the last frame (in ms). + */ + this.elapsed = 0; - /** - * @property {number} pausedTime - Records how long the game has been paused for. Is reset each time the game pauses. - */ - this.pausedTime = 0; + /** + * @property {number} fps - Frames per second. + */ + this.fps = 0; - /** - * @property {number} now - The time right now. - */ - this.now = 0; + /** + * @property {number} fpsMin - The lowest rate the fps has dropped to. + */ + this.fpsMin = 1000; - /** - * @property {number} elapsed - Elapsed time since the last frame (in ms). - */ - this.elapsed = 0; + /** + * @property {number} fpsMax - The highest rate the fps has reached (usually no higher than 60fps). + */ + this.fpsMax = 0; - /** - * @property {number} fps - Frames per second. - */ - this.fps = 0; + /** + * @property {number} msMin - The minimum amount of time the game has taken between two frames. + * @default + */ + this.msMin = 1000; - /** - * @property {number} fpsMin - The lowest rate the fps has dropped to. - */ - this.fpsMin = 1000; + /** + * @property {number} msMax - The maximum amount of time the game has taken between two frames. + */ + this.msMax = 0; - /** - * @property {number} fpsMax - The highest rate the fps has reached (usually no higher than 60fps). - */ - this.fpsMax = 0; + /** + * @property {number} frames - The number of frames record in the last second. + */ + this.frames = 0; - /** - * @property {number} msMin - The minimum amount of time the game has taken between two frames. - * @default - */ - this.msMin = 1000; + /** + * @property {number} pauseDuration - Records how long the game was paused for in miliseconds. + */ + this.pauseDuration = 0; - /** - * @property {number} msMax - The maximum amount of time the game has taken between two frames. - */ - this.msMax = 0; + /** + * @property {number} timeToCall - The value that setTimeout needs to work out when to next update + */ + this.timeToCall = 0; - /** - * @property {number} frames - The number of frames record in the last second. - */ - this.frames = 0; + /** + * @property {number} lastTime - Internal value used by timeToCall as part of the setTimeout loop + */ + this.lastTime = 0; - /** - * @property {number} pauseDuration - Records how long the game was paused for in miliseconds. - */ - this.pauseDuration = 0; + /** + * @property {Phaser.Timer} events - This is a Phaser.Timer object bound to the master clock to which you can add timed events. + */ + this.events = new Phaser.Timer(this.game, false); - /** - * @property {number} timeToCall - The value that setTimeout needs to work out when to next update - */ - this.timeToCall = 0; - - /** - * @property {number} lastTime - Internal value used by timeToCall as part of the setTimeout loop - */ - this.lastTime = 0; - - /** - * @property {Phaser.Timer} _timer - Internal Phaser.Timer object. - * @private - */ - this._timer = new Phaser.Timer(this.game, false); - - /** - * @property {boolean} _justResumed - Internal value used to recover from the game pause state. + /** + * @property {number} _started - The time at which the Game instance started. * @private - */ - this._justResumed = false; + */ + this._started = 0; - /** - * @property {array} _timers - Internal store of Phaser.Timer objects. + /** + * @property {number} _timeLastSecond - The time (in ms) that the last second counter ticked over. * @private - */ - this._timers = []; + */ + this._timeLastSecond = 0; - // Listen for game pause/resume events - this.game.onPause.add(this.gamePaused, this); - this.game.onResume.add(this.gameResumed, this); + /** + * @property {number} _pauseStarted - The time the game started being paused. + * @private + */ + this._pauseStarted = 0; + + /** + * @property {boolean} _justResumed - Internal value used to recover from the game pause state. + * @private + */ + this._justResumed = false; + + /** + * @property {array} _timers - Internal store of Phaser.Timer objects. + * @private + */ + this._timers = []; + + /** + * @property {number} _len - Temp. array length variable. + * @private + */ + this._len = 0; + + /** + * @property {number} _i - Temp. array counter variable. + * @private + */ + this._i = 0; + + // Listen for game pause/resume events + this.game.onPause.add(this.gamePaused, this); + this.game.onResume.add(this.gameResumed, this); }; @@ -137,77 +148,29 @@ Phaser.Time.prototype = { /** * @method Phaser.Time#boot */ - boot: function () { + boot: function () { - this._timer.start(); - - }, - - /** - * Adds a new Event to this Timer. The event will fire after the given amount of 'delay' in milliseconds has passed, once the Timer has started running. - * Call Timer.start() once you have added all of the Events you require for this Timer. The delay is in relation to when the Timer starts, not the time it was added. - * @method Phaser.Time#addEvent - * @param {number} delay - The number of milliseconds that should elapse before the Timer will call the given callback. - * @param {function} callback - The callback that will be called when the Timer event occurs. - * @param {object} callbackContext - The context in which the callback will be called. - * @param {...} arguments - The values to be sent to your callback function when it is called. - */ - addEvent: function (delay, callback, callbackContext) { - - this._timer.create(delay, false, 0, callback, callbackContext, Array.prototype.splice.call(arguments, 3)); - - }, - - /** - * Adds a new Event to this Timer that will repeat for the given number of iterations. - * The event will fire after the given amount of 'delay' milliseconds has passed once the Timer has started running. - * Call Timer.start() once you have added all of the Events you require for this Timer. The delay is in relation to when the Timer starts, not the time it was added. - * @method Phaser.Time#repeatEvent - * @param {number} delay - The number of milliseconds that should elapse before the Timer will call the given callback. - * @param {number} repeatCount - The number of times to repeat the event. - * @param {function} callback - The callback that will be called when the Timer event occurs. - * @param {object} callbackContext - The context in which the callback will be called. - * @param {...} arguments - The values to be sent to your callback function when it is called. - */ - repeatEvent: function (delay, repeatCount, callback, callbackContext) { - - this._timer.create(delay, false, repeatCount, callback, callbackContext, Array.prototype.splice.call(arguments, 4)); - - }, - - /** - * Adds a new looped Event to this Timer that will repeat forever or until the Timer is stopped. - * The event will fire after the given amount of 'delay' milliseconds has passed once the Timer has started running. - * Call Timer.start() once you have added all of the Events you require for this Timer. The delay is in relation to when the Timer starts, not the time it was added. - * @method Phaser.Time#loopEvent - * @param {number} delay - The number of milliseconds that should elapse before the Timer will call the given callback. - * @param {function} callback - The callback that will be called when the Timer event occurs. - * @param {object} callbackContext - The context in which the callback will be called. - * @param {...} arguments - The values to be sent to your callback function when it is called. - */ - loopEvent: function (delay, callback, callbackContext) { - - this._timer.create(delay, true, 0, callback, callbackContext, Array.prototype.splice.call(arguments, 3)); + this.events.start(); }, /** * Creates a new stand-alone Phaser.Timer object. * @method Phaser.Time#create - * @param {boolean} [autoDestroy=true] - A Timer that is set to automatically destroy itself will do so after all of its events have been dispatched (assuming no looping events). - * @return {Phaser.Timer} The Timer object that was created. + * @param {boolean} [autoDestroy=true] - A Timer that is set to automatically destroy itself will do so after all of its events have been dispatched (assuming no looping events). + * @return {Phaser.Timer} The Timer object that was created. */ - create: function (autoDestroy) { + create: function (autoDestroy) { - if (typeof autoDestroy === 'undefined') { autoDestroy = true; } + if (typeof autoDestroy === 'undefined') { autoDestroy = true; } - var timer = new Phaser.Timer(this.game, autoDestroy); + var timer = new Phaser.Timer(this.game, autoDestroy); - this._timers.push(timer); + this._timers.push(timer); - return timer; + return timer; - }, + }, /** * Remove all Timer objects, regardless of their state. @@ -215,147 +178,164 @@ Phaser.Time.prototype = { */ removeAll: function () { - for (var i = 0; i < this._timers.length; i++) - { - this._timers[i].destroy(); - } + for (var i = 0; i < this._timers.length; i++) + { + this._timers[i].destroy(); + } - this._timers = []; + this._timers = []; }, - /** - * Updates the game clock and calculate the fps. This is called automatically by Phaser.Game. - * @method Phaser.Time#update - * @param {number} time - The current timestamp, either performance.now or Date.now depending on the browser. - */ - update: function (time) { + /** + * Updates the game clock and calculate the fps. This is called automatically by Phaser.Game. + * @method Phaser.Time#update + * @param {number} time - The current timestamp, either performance.now or Date.now depending on the browser. + */ + update: function (time) { - this.now = time; + this.now = time; - if (this._justResumed) - { - this.time = this.now; - this._justResumed = false; - } - - this.timeToCall = this.game.math.max(0, 16 - (time - this.lastTime)); - - this.elapsed = this.now - this.time; - - this.msMin = this.game.math.min(this.msMin, this.elapsed); - this.msMax = this.game.math.max(this.msMax, this.elapsed); - - this.frames++; - - if (this.now > this._timeLastSecond + 1000) - { - this.fps = Math.round((this.frames * 1000) / (this.now - this._timeLastSecond)); - this.fpsMin = this.game.math.min(this.fpsMin, this.fps); - this.fpsMax = this.game.math.max(this.fpsMax, this.fps); - this._timeLastSecond = this.now; - this.frames = 0; - } - - this.time = this.now; - this.lastTime = time + this.timeToCall; - this.physicsElapsed = 1.0 * (this.elapsed / 1000); - - // Clamp the delta - if (this.physicsElapsed > 1) - { - this.physicsElapsed = 1; - } - - // Paused? - if (this.game.paused) - { - this.pausedTime = this.now - this._pauseStarted; - } - - // Our internal timer - this._timer.update(this.now); - - var i = 0; - var len = this._timers.length; - - while (i < len) + if (this._justResumed) { - if (this._timers[i].update(this.now)) - { - i++; - } - else - { - this._timers.splice(i, 1); + this.time = this.now; + this._justResumed = false; + + this.events.resume(); - len--; + for (var i = 0; i < this._timers.length; i++) + { + this._timers[i].resume(); } } - }, + this.timeToCall = this.game.math.max(0, 16 - (time - this.lastTime)); - /** - * Called when the game enters a paused state. - * @method Phaser.Time#gamePaused - * @private - */ - gamePaused: function () { - - this._pauseStarted = this.now; + this.elapsed = this.now - this.time; - }, + this.msMin = this.game.math.min(this.msMin, this.elapsed); + this.msMax = this.game.math.max(this.msMax, this.elapsed); - /** - * Called when the game resumes from a paused state. - * @method Phaser.Time#gameResumed - * @private - */ - gameResumed: function () { + this.frames++; - // Level out the elapsed timer to avoid spikes - this.time = Date.now(); - this.pauseDuration = this.pausedTime; - this._justResumed = true; + if (this.now > this._timeLastSecond + 1000) + { + this.fps = Math.round((this.frames * 1000) / (this.now - this._timeLastSecond)); + this.fpsMin = this.game.math.min(this.fpsMin, this.fps); + this.fpsMax = this.game.math.max(this.fpsMax, this.fps); + this._timeLastSecond = this.now; + this.frames = 0; + } - }, + this.time = this.now; + this.lastTime = time + this.timeToCall; + this.physicsElapsed = 1.0 * (this.elapsed / 1000); - /** - * The number of seconds that have elapsed since the game was started. - * @method Phaser.Time#totalElapsedSeconds - * @return {number} - */ - totalElapsedSeconds: function() { - return (this.now - this._started) * 0.001; - }, + // Clamp the delta + if (this.physicsElapsed > 1) + { + this.physicsElapsed = 1; + } - /** - * How long has passed since the given time. - * @method Phaser.Time#elapsedSince - * @param {number} since - The time you want to measure against. - * @return {number} The difference between the given time and now. - */ - elapsedSince: function (since) { - return this.now - since; - }, + // Paused? + if (this.game.paused) + { + this.pausedTime = this.now - this._pauseStarted; + } + else + { + // Our internal Phaser.Timer + this.events.update(this.now); - /** - * How long has passed since the given time (in seconds). - * @method Phaser.Time#elapsedSecondsSince - * @param {number} since - The time you want to measure (in seconds). - * @return {number} Duration between given time and now (in seconds). - */ - elapsedSecondsSince: function (since) { - return (this.now - since) * 0.001; - }, + // Any game level timers + this._i = 0; + this._len = this._timers.length; - /** - * Resets the private _started value to now. - * @method Phaser.Time#reset - */ - reset: function () { - this._started = this.now; - } + while (this._i < this._len) + { + if (this._timers[this._i].update(this.now)) + { + this._i++; + } + else + { + this._timers.splice(this._i, 1); + + this._len--; + } + } + } + + }, + + /** + * Called when the game enters a paused state. + * @method Phaser.Time#gamePaused + * @private + */ + gamePaused: function () { + + this._pauseStarted = this.now; + + this.events.pause(); + + for (var i = 0; i < this._timers.length; i++) + { + this._timers[i].pause(); + } + + }, + + /** + * Called when the game resumes from a paused state. + * @method Phaser.Time#gameResumed + * @private + */ + gameResumed: function () { + + // Level out the elapsed timer to avoid spikes + this.time = Date.now(); + this.pauseDuration = this.pausedTime; + this._justResumed = true; + + }, + + /** + * The number of seconds that have elapsed since the game was started. + * @method Phaser.Time#totalElapsedSeconds + * @return {number} + */ + totalElapsedSeconds: function() { + return (this.now - this._started) * 0.001; + }, + + /** + * How long has passed since the given time. + * @method Phaser.Time#elapsedSince + * @param {number} since - The time you want to measure against. + * @return {number} The difference between the given time and now. + */ + elapsedSince: function (since) { + return this.now - since; + }, + + /** + * How long has passed since the given time (in seconds). + * @method Phaser.Time#elapsedSecondsSince + * @param {number} since - The time you want to measure (in seconds). + * @return {number} Duration between given time and now (in seconds). + */ + elapsedSecondsSince: function (since) { + return (this.now - since) * 0.001; + }, + + /** + * Resets the private _started value to now. + * @method Phaser.Time#reset + */ + reset: function () { + this._started = this.now; + } }; diff --git a/src/time/Timer.js b/src/time/Timer.js index e84b8102c..c1abbfd41 100644 --- a/src/time/Timer.js +++ b/src/time/Timer.js @@ -25,24 +25,11 @@ Phaser.Timer = function (game, autoDestroy) { this.game = game; /** - * @property {number} _started - The time at which this Timer instance started running. - * @private - * @default - */ - this._started = 0; - - /** - * @property {boolean} running - True if the Timer is actively running. + * @property {boolean} running - True if the Timer is actively running. Do not switch this boolean, if you wish to pause the timer then use Timer.pause() instead. * @default */ this.running = false; - /** - * @property {boolean} pauseWithGame - If true then the timer will update itself automatically if the game pauses, otherwise it will carry on dispatching regardless. - * @default - */ - this.pauseWithGame = true; - /** * @property {boolean} autoDestroy - A Timer that is set to automatically destroy itself will do so after all of its events have been dispatched (assuming no looping events). */ @@ -50,12 +37,13 @@ Phaser.Timer = function (game, autoDestroy) { /** * @property {boolean} expired - An expired Timer is one in which all of its events have been dispatched and none are pending. + * @readonly * @default */ this.expired = false; /** - * @property {array} events - An array holding the event data. + * @property {array} events - An array holding all of this timers Phaser.TimerEvent objects. Use the methods add, repeat and loop to populate it. */ this.events = []; @@ -65,17 +53,50 @@ Phaser.Timer = function (game, autoDestroy) { this.onComplete = new Phaser.Signal(); /** - * @property {number} nextTick - The time the next tick will occur. Do not set this value directly. + * @property {number} nextTick - The time the next tick will occur. + * @readonly * @protected */ this.nextTick = 0; + /** + * @property {boolean} paused - The paused state of the Timer. You can pause the timer by calling Timer.pause() and Timer.resume() or by the game pausing. + * @readonly + * @default + */ + this.paused = false; + + /** + * @property {number} _started - The time at which this Timer instance started running. + * @private + * @default + */ + this._started = 0; + + /** + * @property {number} _pauseStarted - The time the game started being paused. + * @private + */ + this._pauseStarted = 0; + /** * @property {number} _now - The current start-time adjusted time. - * @protected + * @private */ this._now = 0; + /** + * @property {number} _len - Temp. array length variable. + * @private + */ + this._len = 0; + + /** + * @property {number} _i - Temp. array counter variable. + * @private + */ + this._i = 0; + }; /** @@ -105,8 +126,8 @@ Phaser.Timer.QUARTER = 250; Phaser.Timer.prototype = { /** - * Creates a new Event on this Timer. - * @method Phaser.Timer#_create + * Creates a new TimerEvent on this Timer. Use the methods add, repeat or loop instead of this. + * @method Phaser.Timer#create * @private * @param {number} delay - The number of milliseconds that should elapse before the Timer will call the given callback. * @param {boolean} loop - Should the event loop or not? @@ -114,9 +135,10 @@ Phaser.Timer.prototype = { * @param {function} callback - The callback that will be called when the Timer event occurs. * @param {object} callbackContext - The context in which the callback will be called. * @param {array} arguments - The values to be sent to your callback function when it is called. + * @return {Phaser.TimerEvent} The Phaser.TimerEvent object that was created. */ create: function (delay, loop, repeatCount, callback, callbackContext, args) { -console.log(arguments); + var tick = delay; if (this.running) @@ -124,20 +146,16 @@ console.log(arguments); tick += this._now; } - this.events.push({ - delay: delay, - tick: tick, - repeatCount: repeatCount - 1, - loop: loop, - callback: callback, - callbackContext: callbackContext, - args: args - }); + var event = new Phaser.TimerEvent(this, delay, tick, repeatCount, loop, callback, callbackContext, args); + + this.events.push(event); this.order(); this.expired = false; + return event; + }, /** @@ -149,10 +167,11 @@ console.log(arguments); * @param {function} callback - The callback that will be called when the Timer event occurs. * @param {object} callbackContext - The context in which the callback will be called. * @param {...} arguments - The values to be sent to your callback function when it is called. + * @return {Phaser.TimerEvent} The Phaser.TimerEvent object that was created. */ add: function (delay, callback, callbackContext) { - this.create(delay, false, 0, callback, callbackContext, Array.prototype.splice.call(arguments, 3)); + return this.create(delay, false, 0, callback, callbackContext, Array.prototype.splice.call(arguments, 3)); }, @@ -167,10 +186,11 @@ console.log(arguments); * @param {function} callback - The callback that will be called when the Timer event occurs. * @param {object} callbackContext - The context in which the callback will be called. * @param {...} arguments - The values to be sent to your callback function when it is called. + * @return {Phaser.TimerEvent} The Phaser.TimerEvent object that was created. */ repeat: function (delay, repeatCount, callback, callbackContext) { - this.create(delay, false, repeatCount, callback, callbackContext, Array.prototype.splice.call(arguments, 4)); + return this.create(delay, false, repeatCount, callback, callbackContext, Array.prototype.splice.call(arguments, 4)); }, @@ -184,10 +204,11 @@ console.log(arguments); * @param {function} callback - The callback that will be called when the Timer event occurs. * @param {object} callbackContext - The context in which the callback will be called. * @param {...} arguments - The values to be sent to your callback function when it is called. + * @return {Phaser.TimerEvent} The Phaser.TimerEvent object that was created. */ loop: function (delay, callback, callbackContext) { - this.create(delay, true, 0, callback, callbackContext, Array.prototype.splice.call(arguments, 3)); + return this.create(delay, true, 0, callback, callbackContext, Array.prototype.splice.call(arguments, 3)); }, @@ -214,7 +235,27 @@ console.log(arguments); }, /** - * Orders the events on this Timer so they are in tick order. + * Removes a pending TimerEvent from the queue. + * @param {Phaser.TimerEvent} event - The event to remove from the queue. + * @method Phaser.Timer#remove + */ + remove: function(event) { + + for (var i = 0; i < this.events.length; i++) + { + if (this.events[i] === event) + { + this.events.splice(i, 1); + return true; + } + } + + return false; + + }, + + /** + * Orders the events on this Timer so they are in tick order. This is called automatically when new events are created. * @method Phaser.Timer#order */ order: function () { @@ -258,37 +299,42 @@ console.log(arguments); */ update: function(time) { + if (this.paused) + { + return true; + } + this._now = time - this._started; - var len = this.events.length; + this._len = this.events.length; - if (this.running && this._now >= this.nextTick && len > 0) + if (this.running && this._now >= this.nextTick && this._len > 0) { - var i = 0; + this._i = 0; - while (i < len) + while (this._i < this._len) { - if (this._now >= this.events[i].tick) + if (this._now >= this.events[this._i].tick) { - if (this.events[i].loop) + if (this.events[this._i].loop === true) { - this.events[i].tick += this.events[i].delay - (this._now - this.events[i].tick); - this.events[i].callback.apply(this.events[i].callbackContext, this.events[i].args); + this.events[this._i].tick += this.events[this._i].delay - (this._now - this.events[this._i].tick); + this.events[this._i].callback.apply(this.events[this._i].callbackContext, this.events[this._i].args); } - else if (this.events[i].repeatCount > 0) + else if (this.events[this._i].repeatCount > 0) { - this.events[i].repeatCount--; - this.events[i].tick += this.events[i].delay - (this._now - this.events[i].tick); - this.events[i].callback.apply(this.events[i].callbackContext, this.events[i].args); + this.events[this._i].repeatCount--; + this.events[this._i].tick += this.events[this._i].delay - (this._now - this.events[this._i].tick); + this.events[this._i].callback.apply(this.events[this._i].callbackContext, this.events[this._i].args); } else { - this.events[i].callback.apply(this.events[i].callbackContext, this.events[i].args); - this.events.splice(i, 1); - len--; + this.events[this._i].callback.apply(this.events[this._i].callbackContext, this.events[this._i].args); + this.events.splice(this._i, 1); + this._len--; } - i++; + this._i++; } else { @@ -296,15 +342,15 @@ console.log(arguments); } } - // There are no events left + // Are there any events left? if (this.events.length > 0) { - this.expired = true; - this.onComplete.dispatch(this); + this.order(); } else { - this.order(); + this.expired = true; + this.onComplete.dispatch(this); } } @@ -319,6 +365,37 @@ console.log(arguments); }, + /** + * Pauses the Timer and all events in the queue. + * @method Phaser.Timer#pause + */ + pause: function () { + + this._pauseStarted = this.game.time.now; + + this.paused = true; + + }, + + /** + * Resumes the Timer and updates all pending events. + * @method Phaser.Timer#resume + */ + resume: function () { + + var pauseDuration = this.game.time.now - this._pauseStarted; + + for (var i = 0; i < this.events.length; i++) + { + this.events[i].tick += pauseDuration; + } + + this.nextTick += pauseDuration; + + this.paused = false; + + }, + /** * Destroys this Timer. Events are not dispatched. * @method Phaser.Timer#destroy @@ -333,6 +410,54 @@ console.log(arguments); }; +/** +* @name Phaser.Timer#next +* @property {number} next - The time at which the next event will occur. +* @readonly +*/ +Object.defineProperty(Phaser.Timer.prototype, "next", { + + get: function () { + return this.nextTick; + } + +}); + +/** +* @name Phaser.Timer#duration +* @property {number} duration - The duration in ms remaining until the next event will occur. +* @readonly +*/ +Object.defineProperty(Phaser.Timer.prototype, "duration", { + + get: function () { + + if (this.running && this.nextTick > this._now) + { + return this.nextTick - this._now; + } + else + { + return 0; + } + + } + +}); + +/** +* @name Phaser.Timer#length +* @property {number} length - The number of pending events in the queue. +* @readonly +*/ +Object.defineProperty(Phaser.Timer.prototype, "length", { + + get: function () { + return this.events.length; + } + +}); + /** * @name Phaser.Timer#ms * @property {number} ms - The duration in milliseconds that this Timer has been running for. diff --git a/src/time/TimerEvent.js b/src/time/TimerEvent.js new file mode 100644 index 000000000..83178444c --- /dev/null +++ b/src/time/TimerEvent.js @@ -0,0 +1,67 @@ +/** +* @author Richard Davey +* @copyright 2013 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* A TimerEvent is a single event that is processed by a Phaser.Timer. It consists of a delay, which is a value in milliseconds after which the event will fire. +* It can call a specific callback, passing in optional parameters. +* +* @class Phaser.TimerEvent +* @classdesc A TimerEvent is a single event that is processed by a Phaser.Timer. It consists of a delay, which is a value in milliseconds after which the event will fire. +* @constructor +* @param {Phaser.Timer} timer - The Timer object that this TimerEvent belongs to. +* @param {number} delay - The delay in ms at which this TimerEvent fires. +* @param {number} tick - The tick is the next game clock time that this event will fire at. +* @param {number} repeatCount - If this TimerEvent repeats it will do so this many times. +* @param {boolean} loop - True if this TimerEvent loops, otherwise false. +* @param {function} callback - The callback that will be called when the TimerEvent occurs. +* @param {object} callbackContext - The context in which the callback will be called. +* @param {array} arguments - The values to be passed to the callback. +*/ +Phaser.TimerEvent = function (timer, delay, tick, repeatCount, loop, callback, callbackContext, args) { + + /** + * @property {Phaser.Timer} timer - The Timer object that this TimerEvent belongs to. + */ + this.timer = timer; + + /** + * @property {number} delay - The delay in ms at which this TimerEvent fires. + */ + this.delay = delay; + + /** + * @property {number} tick - The tick is the next game clock time that this event will fire at. + */ + this.tick = tick; + + /** + * @property {number} repeatCount - If this TimerEvent repeats it will do so this many times. + */ + this.repeatCount = repeatCount - 1; + + /** + * @property {boolean} loop - True if this TimerEvent loops, otherwise false. + */ + this.loop = loop; + + /** + * @property {function} callback - The callback that will be called when the TimerEvent occurs. + */ + this.callback = callback; + + /** + * @property {object} callbackContext - The context in which the callback will be called. + */ + this.callbackContext = callbackContext; + + /** + * @property {array} arguments - The values to be passed to the callback. + */ + this.args = args; + +}; + +Phaser.TimerEvent.prototype.constructor = Phaser.TimerEvent;