diff --git a/README.md b/README.md index 1cbf30b8..2993c770 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Significant API changes: * Game no longer pauses if you've forced orientation and change it, also doesn't resize a NO_SCALE game. * All the Debug methods have had the word 'render' removed from the start. So where you did `debug.renderSpriteInfo` before, it's now just `debug.spriteInfo`. * Debug methods that rendered geometry (Rectangle, Circle, Line, Point) have been merged into the single method: `Debug.geom`. +* Animation.looped has been renamed to Animation.loop. It's a boolean you can toggle at run-time to turn on/off animation looping. New features: @@ -136,7 +137,8 @@ New features: * Device.windowsPhone is now tested for. * The Debug panel now works in WebGL mode. Pay attention to the warning at the top of the Debug docs (feature request #499) * You can now create blank Tilemaps and then populate them with data later. - +* A single Animation object now has 3 new events: onStart, onLoop and onComplete. +* Animation.loopCount holds the number of times the animation has looped since it last started. Updates: @@ -163,6 +165,7 @@ Updates: * Tween no longer copies all the object properties into the `_valuesStart` object on creation. * Completely empty Tilemaps can now be created. This allows for dynamic map generation at runtime. * Keyboard.event now stores the most recent DOM keyboard event. +* Animation.stop has a new parameter: dispatchComplete. If true it'll dispatch an Animation.onComplete event. Bug Fixes: diff --git a/examples/animation/animation events.js b/examples/animation/animation events.js new file mode 100644 index 00000000..1d04dbfb --- /dev/null +++ b/examples/animation/animation events.js @@ -0,0 +1,69 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update }); + +function preload() { + + game.load.image('lazur', 'assets/pics/thorn_lazur.png'); + game.load.spritesheet('mummy', 'assets/sprites/metalslug_mummy37x45.png', 37, 45, 18); + +} + +var back; +var mummy; +var anim; +var loopText; + +function create() { + + game.stage.smoothed = false; + + back = game.add.image(0, -400, 'lazur'); + back.scale.set(2); + + mummy = game.add.sprite(200, 360, 'mummy', 5); + mummy.scale.set(4); + + anim = mummy.animations.add('walk'); + + anim.onStart.add(animationStarted, this); + anim.onLoop.add(animationLooped, this); + anim.onComplete.add(animationStopped, this); + + anim.play(10, true); + +} + +function animationStarted(sprite, animation) { + + game.add.text(32, 32, 'Animation started', { fill: 'white' }); + +} + +function animationLooped(sprite, animation) { + + if (animation.loopCount === 1) + { + loopText = game.add.text(32, 64, 'Animation looped', { fill: 'white' }); + } + else + { + loopText.text = 'Animation looped x2'; + animation.loop = false; + } + +} + +function animationStopped(sprite, animation) { + + game.add.text(32, 64+32, 'Animation stopped', { fill: 'white' }); + +} + +function update() { + + if (anim.isPlaying) + { + back.x -= 1; + } + +} \ No newline at end of file diff --git a/examples/assets/pics/havoc-plastic_surgery.png b/examples/assets/pics/havoc-plastic_surgery.png deleted file mode 100644 index 3c67930e..00000000 Binary files a/examples/assets/pics/havoc-plastic_surgery.png and /dev/null differ diff --git a/examples/assets/pics/title_page.png b/examples/assets/pics/title_page.png deleted file mode 100644 index c0210cab..00000000 Binary files a/examples/assets/pics/title_page.png and /dev/null differ diff --git a/examples/display/bitmapdata wobble.js b/examples/display/bitmapdata wobble.js index 81d63513..3bda41a9 100644 --- a/examples/display/bitmapdata wobble.js +++ b/examples/display/bitmapdata wobble.js @@ -42,9 +42,7 @@ function update() { // This creates a simple sine-wave effect running through our BitmapData. // This is then duplicated across all 100 sprites using it, meaning we only have to calculate it and upload it to the GPU once. -function updateWobblyBall() -{ - +function updateWobblyBall() { var s = 0; var copyRect = { x: 0, y: 0, w: wavePixelChunk, h: 32 }; @@ -76,4 +74,5 @@ function updateWobblyBall() { waveDataCounter = 0; } + } diff --git a/examples/wip/animation events.js b/examples/wip/animation events.js new file mode 100644 index 00000000..1d04dbfb --- /dev/null +++ b/examples/wip/animation events.js @@ -0,0 +1,69 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update }); + +function preload() { + + game.load.image('lazur', 'assets/pics/thorn_lazur.png'); + game.load.spritesheet('mummy', 'assets/sprites/metalslug_mummy37x45.png', 37, 45, 18); + +} + +var back; +var mummy; +var anim; +var loopText; + +function create() { + + game.stage.smoothed = false; + + back = game.add.image(0, -400, 'lazur'); + back.scale.set(2); + + mummy = game.add.sprite(200, 360, 'mummy', 5); + mummy.scale.set(4); + + anim = mummy.animations.add('walk'); + + anim.onStart.add(animationStarted, this); + anim.onLoop.add(animationLooped, this); + anim.onComplete.add(animationStopped, this); + + anim.play(10, true); + +} + +function animationStarted(sprite, animation) { + + game.add.text(32, 32, 'Animation started', { fill: 'white' }); + +} + +function animationLooped(sprite, animation) { + + if (animation.loopCount === 1) + { + loopText = game.add.text(32, 64, 'Animation looped', { fill: 'white' }); + } + else + { + loopText.text = 'Animation looped x2'; + animation.loop = false; + } + +} + +function animationStopped(sprite, animation) { + + game.add.text(32, 64+32, 'Animation stopped', { fill: 'white' }); + +} + +function update() { + + if (anim.isPlaying) + { + back.x -= 1; + } + +} \ No newline at end of file diff --git a/examples/wip/tilemap put tile update.js b/examples/wip/tilemap put tile update.js new file mode 100644 index 00000000..fe7f637e --- /dev/null +++ b/examples/wip/tilemap put tile update.js @@ -0,0 +1,89 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); +// var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.tilemap('map', 'assets/tilemaps/maps/collision_test.json', null, Phaser.Tilemap.TILED_JSON); + game.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png'); + game.load.image('walls_1x2', 'assets/tilemaps/tiles/walls_1x2.png'); + game.load.image('tiles2', 'assets/tilemaps/tiles/tiles2.png'); + game.load.image('player', 'assets/sprites/phaser-dude.png'); + game.load.image('box', 'assets/sprites/ufo.png'); + game.load.image('ship', 'assets/sprites/thrust_ship2.png'); + +} + +var ship; +var map; +var tileset; +var layer; +var p; +var b; +var cursors; +var box2; +var dump; + +function create() { + + game.renderer.roundPixels = true; + + // game.stage.backgroundColor = '#787878'; + + map = game.add.tilemap('map'); + + map.addTilesetImage('ground_1x1'); + map.addTilesetImage('walls_1x2'); + map.addTilesetImage('tiles2'); + + layer = map.createLayer('Tile Layer 1'); + + layer.resizeWorld(); + + map.setCollisionBetween(1, 12); + + layer.debug = true; + + // dump = map.generateCollisionData(layer); + + + + box2 = game.add.sprite(200, 200, 'box'); + box2.physicsEnabled = true; + box2.body.fixedRotation = true; + + game.camera.follow(box2); + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function update() { + + if (cursors.left.isDown) + { + box2.body.moveLeft(200); + } + else if (cursors.right.isDown) + { + box2.body.moveRight(200); + } + else + { + box2.body.setZeroVelocity(); + } + + if (cursors.up.isDown) + { + box2.body.moveUp(200); + } + else if (cursors.down.isDown) + { + box2.body.moveDown(200); + } + +} + +function render() { + +} diff --git a/src/animation/Animation.js b/src/animation/Animation.js index b1e22387..e410ed21 100644 --- a/src/animation/Animation.js +++ b/src/animation/Animation.js @@ -16,9 +16,9 @@ * @param {Phaser.FrameData} frameData - The FrameData object that contains all frames used by this Animation. * @param {(Array.|Array.)} frames - An array of numbers or strings indicating which frames to play in which order. * @param {number} delay - The time between each frame of the animation, given in ms. -* @param {boolean} looped - Should this animation loop or play through once. +* @param {boolean} loop - Should this animation loop when it reaches the end or play through once. */ -Phaser.Animation = function (game, parent, name, frameData, frames, delay, looped) { +Phaser.Animation = function (game, parent, name, frameData, frames, delay, loop) { /** * @property {Phaser.Game} game - A reference to the currently running Game. @@ -55,9 +55,14 @@ Phaser.Animation = function (game, parent, name, frameData, frames, delay, loope this.delay = 1000 / delay; /** - * @property {boolean} looped - The loop state of the Animation. + * @property {boolean} loop - The loop state of the Animation. */ - this.looped = looped; + this.loop = loop; + + /** + * @property {number} loopCount - The number of times the animation has looped since it was last started. + */ + this.loopCount = 0; /** * @property {boolean} killOnComplete - Should the parent of this Animation be killed when the animation completes? @@ -116,9 +121,26 @@ Phaser.Animation = function (game, parent, name, frameData, frames, delay, loope */ this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]); + /** + * @property {Phaser.Signal} onStart - This event is dispatched when this Animation starts playback. + */ + this.onStart = new Phaser.Signal(); + + /** + * @property {Phaser.Signal} onComplete - This event is dispatched when this Animation completes playback. If the animation is set to loop this is never fired, listen for onAnimationLoop instead. + */ + this.onComplete = new Phaser.Signal(); + + /** + * @property {Phaser.Signal} onLoop - This event is dispatched when this Animation loops. + */ + this.onLoop = new Phaser.Signal(); + // Set-up some event listeners this.game.onPause.add(this.onPause, this); this.game.onResume.add(this.onResume, this); + + console.log('animation created', this); }; @@ -145,7 +167,7 @@ Phaser.Animation.prototype = { if (typeof loop === 'boolean') { // If they set a new loop value then use it, otherwise use the one set on creation - this.looped = loop; + this.loop = loop; } if (typeof killOnComplete !== 'undefined') @@ -157,6 +179,7 @@ Phaser.Animation.prototype = { this.isPlaying = true; this.isFinished = false; this.paused = false; + this.loopCount = 0; this._timeLastFrame = this.game.time.now; this._timeNextFrame = this.game.time.now + this.delay; @@ -173,10 +196,8 @@ Phaser.Animation.prototype = { this._parent.tilingTexture = false; } - if (this._parent.events) - { - this._parent.events.onAnimationStart.dispatch(this._parent, this); - } + this._parent.events.onAnimationStart.dispatch(this._parent, this); + this.onStart.dispatch(this._parent, this); return this; @@ -193,6 +214,7 @@ Phaser.Animation.prototype = { this.isPlaying = true; this.isFinished = false; this.paused = false; + this.loopCount = 0; this._timeLastFrame = this.game.time.now; this._timeNextFrame = this.game.time.now + this.delay; @@ -201,18 +223,23 @@ Phaser.Animation.prototype = { this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]); + this.onStart.dispatch(this._parent, this); + }, /** * Stops playback of this animation and set it to a finished state. If a resetFrame is provided it will stop playback and set frame to the first in the animation. + * If `dispatchComplete` is true it will dispatch the complete events, otherwise they'll be ignored. * * @method Phaser.Animation#stop * @memberof Phaser.Animation * @param {boolean} [resetFrame=false] - If true after the animation stops the currentFrame value will be set to the first frame in this animation. + * @param {boolean} [dispatchComplete=false] - Dispatch the Animation.onComplete and parent.onAnimationComplete events? */ - stop: function (resetFrame) { + stop: function (resetFrame, dispatchComplete) { if (typeof resetFrame === 'undefined') { resetFrame = false; } + if (typeof dispatchComplete === 'undefined') { dispatchComplete = false; } this.isPlaying = false; this.isFinished = true; @@ -223,6 +250,12 @@ Phaser.Animation.prototype = { this.currentFrame = this._frameData.getFrame(this._frames[0]); } + if (dispatchComplete) + { + this._parent.events.onAnimationComplete.dispatch(this._parent, this); + this.onComplete.dispatch(this._parent, this); + } + }, /** @@ -292,7 +325,7 @@ Phaser.Animation.prototype = { if (this._frameIndex >= this._frames.length) { - if (this.looped) + if (this.loop) { this._frameIndex %= this._frames.length; this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]); @@ -308,11 +341,13 @@ Phaser.Animation.prototype = { } } + this.loopCount++; this._parent.events.onAnimationLoop.dispatch(this._parent, this); + this.onLoop.dispatch(this._parent, this); } else { - this.onComplete(); + this.complete(); } } else @@ -353,27 +388,31 @@ Phaser.Animation.prototype = { this.currentFrame = null; this.isPlaying = false; + this.onStart.destroy(); + this.onLoop.destroy(); + this.onComplete.destroy(); + this.game.onPause.remove(this.onPause, this); this.game.onResume.remove(this.onResume, this); }, /** - * Called internally when the animation finishes playback. Sets the isPlaying and isFinished states and dispatches the onAnimationComplete event if it exists on the parent. + * Called internally when the animation finishes playback. + * Sets the isPlaying and isFinished states and dispatches the onAnimationComplete event if it exists on the parent and local onComplete event. * - * @method Phaser.Animation#onComplete + * @method Phaser.Animation#complete * @memberof Phaser.Animation */ - onComplete: function () { + complete: function () { this.isPlaying = false; this.isFinished = true; this.paused = false; - if (this._parent.events) - { - this._parent.events.onAnimationComplete.dispatch(this._parent, this); - } + this._parent.events.onAnimationComplete.dispatch(this._parent, this); + + this.onComplete.dispatch(this._parent, this); if (this.killOnComplete) { diff --git a/src/gameobjects/Events.js b/src/gameobjects/Events.js index d45d577a..89d7f690 100644 --- a/src/gameobjects/Events.js +++ b/src/gameobjects/Events.js @@ -11,7 +11,7 @@ * * For example to tell when a Sprite has been added to a new group: * -* ```sprite.events.onAddedToGroup.add(yourFunction, this);``` +* `sprite.events.onAddedToGroup.add(yourFunction, this);` * * Where `yourFunction` is the function you want called when this event occurs. *