mirror of
https://github.com/photonstorm/phaser
synced 2025-01-10 20:28:56 +00:00
2007 lines
66 KiB
JavaScript
2007 lines
66 KiB
JavaScript
/**
|
|
* @author Richard Davey <rich@phaser.io>
|
|
* @copyright 2013-2024 Phaser Studio Inc.
|
|
* @license {@link https://opensource.org/licenses/MIT|MIT License}
|
|
*/
|
|
|
|
var Animation = require('./Animation');
|
|
var Between = require('../math/Between');
|
|
var Class = require('../utils/Class');
|
|
var CustomMap = require('../structs/Map');
|
|
var Events = require('./events');
|
|
var GetFastValue = require('../utils/object/GetFastValue');
|
|
|
|
/**
|
|
* @classdesc
|
|
* The Animation State Component.
|
|
*
|
|
* This component provides features to apply animations to Game Objects. It is responsible for
|
|
* loading, queuing animations for later playback, mixing between animations and setting
|
|
* the current animation frame to the Game Object that owns this component.
|
|
*
|
|
* This component lives as an instance within any Game Object that has it defined, such as Sprites.
|
|
*
|
|
* You can access its properties and methods via the `anims` property, i.e. `Sprite.anims`.
|
|
*
|
|
* As well as playing animations stored in the global Animation Manager, this component
|
|
* can also create animations that are stored locally within it. See the `create` method
|
|
* for more details.
|
|
*
|
|
* Prior to Phaser 3.50 this component was called just `Animation` and lived in the
|
|
* `Phaser.GameObjects.Components` namespace. It was renamed to `AnimationState`
|
|
* in 3.50 to help better identify its true purpose when browsing the documentation.
|
|
*
|
|
* @class AnimationState
|
|
* @memberof Phaser.Animations
|
|
* @constructor
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.GameObjects.GameObject} parent - The Game Object to which this animation component belongs.
|
|
*/
|
|
var AnimationState = new Class({
|
|
|
|
initialize:
|
|
|
|
function AnimationState (parent)
|
|
{
|
|
/**
|
|
* The Game Object to which this animation component belongs.
|
|
*
|
|
* You can typically access this component from the Game Object
|
|
* via the `this.anims` property.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#parent
|
|
* @type {Phaser.GameObjects.GameObject}
|
|
* @since 3.0.0
|
|
*/
|
|
this.parent = parent;
|
|
|
|
/**
|
|
* A reference to the global Animation Manager.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#animationManager
|
|
* @type {Phaser.Animations.AnimationManager}
|
|
* @since 3.0.0
|
|
*/
|
|
this.animationManager = parent.scene.sys.anims;
|
|
|
|
this.animationManager.on(Events.REMOVE_ANIMATION, this.globalRemove, this);
|
|
|
|
/**
|
|
* A reference to the Texture Manager.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#textureManager
|
|
* @type {Phaser.Textures.TextureManager}
|
|
* @protected
|
|
* @since 3.50.0
|
|
*/
|
|
this.textureManager = this.animationManager.textureManager;
|
|
|
|
/**
|
|
* The Animations stored locally in this Animation component.
|
|
*
|
|
* Do not modify the contents of this Map directly, instead use the
|
|
* `add`, `create` and `remove` methods of this class instead.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#anims
|
|
* @type {Phaser.Structs.Map.<string, Phaser.Animations.Animation>}
|
|
* @protected
|
|
* @since 3.50.0
|
|
*/
|
|
this.anims = null;
|
|
|
|
/**
|
|
* Is an animation currently playing or not?
|
|
*
|
|
* @name Phaser.Animations.AnimationState#isPlaying
|
|
* @type {boolean}
|
|
* @default false
|
|
* @since 3.0.0
|
|
*/
|
|
this.isPlaying = false;
|
|
|
|
/**
|
|
* Has the current animation started playing, or is it waiting for a delay to expire?
|
|
*
|
|
* @name Phaser.Animations.AnimationState#hasStarted
|
|
* @type {boolean}
|
|
* @default false
|
|
* @since 3.50.0
|
|
*/
|
|
this.hasStarted = false;
|
|
|
|
/**
|
|
* The current Animation loaded into this Animation component.
|
|
*
|
|
* Will by `null` if no animation is yet loaded.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#currentAnim
|
|
* @type {?Phaser.Animations.Animation}
|
|
* @default null
|
|
* @since 3.0.0
|
|
*/
|
|
this.currentAnim = null;
|
|
|
|
/**
|
|
* The current AnimationFrame being displayed by this Animation component.
|
|
*
|
|
* Will by `null` if no animation is yet loaded.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#currentFrame
|
|
* @type {?Phaser.Animations.AnimationFrame}
|
|
* @default null
|
|
* @since 3.0.0
|
|
*/
|
|
this.currentFrame = null;
|
|
|
|
/**
|
|
* The key, instance, or config of the next Animation to be loaded into this Animation component
|
|
* when the current animation completes.
|
|
*
|
|
* Will by `null` if no animation has been queued.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#nextAnim
|
|
* @type {?(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)}
|
|
* @default null
|
|
* @since 3.16.0
|
|
*/
|
|
this.nextAnim = null;
|
|
|
|
/**
|
|
* A queue of Animations to be loaded into this Animation component when the current animation completes.
|
|
*
|
|
* Populate this queue via the `chain` method.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#nextAnimsQueue
|
|
* @type {array}
|
|
* @since 3.24.0
|
|
*/
|
|
this.nextAnimsQueue = [];
|
|
|
|
/**
|
|
* The Time Scale factor.
|
|
*
|
|
* You can adjust this value to modify the passage of time for the animation that is currently
|
|
* playing. For example, setting it to 2 will make the animation play twice as fast. Or setting
|
|
* it to 0.5 will slow the animation down.
|
|
*
|
|
* You can change this value at run-time, or set it via the `PlayAnimationConfig`.
|
|
*
|
|
* Prior to Phaser 3.50 this property was private and called `_timeScale`.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#timeScale
|
|
* @type {number}
|
|
* @default 1
|
|
* @since 3.50.0
|
|
*/
|
|
this.timeScale = 1;
|
|
|
|
/**
|
|
* The frame rate of playback, of the current animation, in frames per second.
|
|
*
|
|
* This value is set when a new animation is loaded into this component and should
|
|
* be treated as read-only, as changing it once playback has started will not alter
|
|
* the animation. To change the frame rate, provide a new value in the `PlayAnimationConfig` object.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#frameRate
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.0.0
|
|
*/
|
|
this.frameRate = 0;
|
|
|
|
/**
|
|
* The duration of the current animation, in milliseconds.
|
|
*
|
|
* This value is set when a new animation is loaded into this component and should
|
|
* be treated as read-only, as changing it once playback has started will not alter
|
|
* the animation. To change the duration, provide a new value in the `PlayAnimationConfig` object.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#duration
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.0.0
|
|
*/
|
|
this.duration = 0;
|
|
|
|
/**
|
|
* The number of milliseconds per frame, not including frame specific modifiers that may be present in the
|
|
* Animation data.
|
|
*
|
|
* This value is calculated when a new animation is loaded into this component and should
|
|
* be treated as read-only. Changing it will not alter playback speed.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#msPerFrame
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.0.0
|
|
*/
|
|
this.msPerFrame = 0;
|
|
|
|
/**
|
|
* Skip frames if the time lags, or always advanced anyway?
|
|
*
|
|
* @name Phaser.Animations.AnimationState#skipMissedFrames
|
|
* @type {boolean}
|
|
* @default true
|
|
* @since 3.0.0
|
|
*/
|
|
this.skipMissedFrames = true;
|
|
|
|
/**
|
|
* Start playback of this animation from a random frame?
|
|
*
|
|
* @name Phaser.Animations.AnimationState#randomFrame
|
|
* @type {boolean}
|
|
* @default false
|
|
* @since 3.60.0
|
|
*/
|
|
this.randomFrame = false;
|
|
|
|
/**
|
|
* The delay before starting playback of the current animation, in milliseconds.
|
|
*
|
|
* This value is set when a new animation is loaded into this component and should
|
|
* be treated as read-only, as changing it once playback has started will not alter
|
|
* the animation. To change the delay, provide a new value in the `PlayAnimationConfig` object.
|
|
*
|
|
* Prior to Phaser 3.50 this property was private and called `_delay`.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#delay
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.50.0
|
|
*/
|
|
this.delay = 0;
|
|
|
|
/**
|
|
* The number of times to repeat playback of the current animation.
|
|
*
|
|
* If -1, it means the animation will repeat forever.
|
|
*
|
|
* This value is set when a new animation is loaded into this component and should
|
|
* be treated as read-only, as changing it once playback has started will not alter
|
|
* the animation. To change the number of repeats, provide a new value in the `PlayAnimationConfig` object.
|
|
*
|
|
* Prior to Phaser 3.50 this property was private and called `_repeat`.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#repeat
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.50.0
|
|
*/
|
|
this.repeat = 0;
|
|
|
|
/**
|
|
* The number of milliseconds to wait before starting the repeat playback of the current animation.
|
|
*
|
|
* This value is set when a new animation is loaded into this component, but can also be modified
|
|
* at run-time.
|
|
*
|
|
* You can change the repeat delay by providing a new value in the `PlayAnimationConfig` object.
|
|
*
|
|
* Prior to Phaser 3.50 this property was private and called `_repeatDelay`.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#repeatDelay
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.0.0
|
|
*/
|
|
this.repeatDelay = 0;
|
|
|
|
/**
|
|
* Should the current animation yoyo? An animation that yoyos will play in reverse, from the end
|
|
* to the start, before then repeating or completing. An animation that does not yoyo will just
|
|
* play from the start to the end.
|
|
*
|
|
* This value is set when a new animation is loaded into this component, but can also be modified
|
|
* at run-time.
|
|
*
|
|
* You can change the yoyo by providing a new value in the `PlayAnimationConfig` object.
|
|
*
|
|
* Prior to Phaser 3.50 this property was private and called `_yoyo`.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#yoyo
|
|
* @type {boolean}
|
|
* @default false
|
|
* @since 3.50.0
|
|
*/
|
|
this.yoyo = false;
|
|
|
|
/**
|
|
* If the animation has a delay set, before playback will begin, this
|
|
* controls when the first frame is set on the Sprite. If this property
|
|
* is 'false' then the frame is set only after the delay has expired.
|
|
* This is the default behavior.
|
|
*
|
|
* If this property is 'true' then the first frame of this animation
|
|
* is set immediately, and then when the delay expires, playback starts.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#showBeforeDelay
|
|
* @type {boolean}
|
|
* @since 3.60.0
|
|
*/
|
|
this.showBeforeDelay = false;
|
|
|
|
/**
|
|
* Should the GameObject's `visible` property be set to `true` when the animation starts to play?
|
|
*
|
|
* This will happen _after_ any delay that may have been set.
|
|
*
|
|
* This value is set when a new animation is loaded into this component, but can also be modified
|
|
* at run-time, assuming the animation is currently delayed.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#showOnStart
|
|
* @type {boolean}
|
|
* @since 3.50.0
|
|
*/
|
|
this.showOnStart = false;
|
|
|
|
/**
|
|
* Should the GameObject's `visible` property be set to `false` when the animation completes?
|
|
*
|
|
* This value is set when a new animation is loaded into this component, but can also be modified
|
|
* at run-time, assuming the animation is still actively playing.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#hideOnComplete
|
|
* @type {boolean}
|
|
* @since 3.50.0
|
|
*/
|
|
this.hideOnComplete = false;
|
|
|
|
/**
|
|
* Is the playhead moving forwards (`true`) or in reverse (`false`) ?
|
|
*
|
|
* @name Phaser.Animations.AnimationState#forward
|
|
* @type {boolean}
|
|
* @default true
|
|
* @since 3.0.0
|
|
*/
|
|
this.forward = true;
|
|
|
|
/**
|
|
* An internal trigger that tells the component if it should plays the animation
|
|
* in reverse mode ('true') or not ('false'). This is used because `forward` can
|
|
* be changed by the `yoyo` feature.
|
|
*
|
|
* Prior to Phaser 3.50 this property was private and called `_reverse`.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#inReverse
|
|
* @type {boolean}
|
|
* @default false
|
|
* @since 3.50.0
|
|
*/
|
|
this.inReverse = false;
|
|
|
|
/**
|
|
* Internal time overflow accumulator.
|
|
*
|
|
* This has the `delta` time added to it as part of the `update` step.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#accumulator
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.0.0
|
|
*/
|
|
this.accumulator = 0;
|
|
|
|
/**
|
|
* The time point at which the next animation frame will change.
|
|
*
|
|
* This value is compared against the `accumulator` as part of the `update` step.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#nextTick
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.0.0
|
|
*/
|
|
this.nextTick = 0;
|
|
|
|
/**
|
|
* A counter keeping track of how much delay time, in milliseconds, is left before playback begins.
|
|
*
|
|
* This is set via the `playAfterDelay` method, although it can be modified at run-time
|
|
* if required, as long as the animation has not already started playing.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#delayCounter
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.50.0
|
|
*/
|
|
this.delayCounter = 0;
|
|
|
|
/**
|
|
* A counter that keeps track of how many repeats are left to run.
|
|
*
|
|
* This value is set when a new animation is loaded into this component, but can also be modified
|
|
* at run-time.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#repeatCounter
|
|
* @type {number}
|
|
* @default 0
|
|
* @since 3.0.0
|
|
*/
|
|
this.repeatCounter = 0;
|
|
|
|
/**
|
|
* An internal flag keeping track of pending repeats.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#pendingRepeat
|
|
* @type {boolean}
|
|
* @default false
|
|
* @since 3.0.0
|
|
*/
|
|
this.pendingRepeat = false;
|
|
|
|
/**
|
|
* Is the Animation paused?
|
|
*
|
|
* @name Phaser.Animations.AnimationState#_paused
|
|
* @type {boolean}
|
|
* @private
|
|
* @default false
|
|
* @since 3.0.0
|
|
*/
|
|
this._paused = false;
|
|
|
|
/**
|
|
* Was the animation previously playing before being paused?
|
|
*
|
|
* @name Phaser.Animations.AnimationState#_wasPlaying
|
|
* @type {boolean}
|
|
* @private
|
|
* @default false
|
|
* @since 3.0.0
|
|
*/
|
|
this._wasPlaying = false;
|
|
|
|
/**
|
|
* Internal property tracking if this Animation is waiting to stop.
|
|
*
|
|
* 0 = No
|
|
* 1 = Waiting for ms to pass
|
|
* 2 = Waiting for repeat
|
|
* 3 = Waiting for specific frame
|
|
*
|
|
* @name Phaser.Animations.AnimationState#_pendingStop
|
|
* @type {number}
|
|
* @private
|
|
* @since 3.4.0
|
|
*/
|
|
this._pendingStop = 0;
|
|
|
|
/**
|
|
* Internal property used by _pendingStop.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#_pendingStopValue
|
|
* @type {any}
|
|
* @private
|
|
* @since 3.4.0
|
|
*/
|
|
this._pendingStopValue;
|
|
},
|
|
|
|
/**
|
|
* Sets an animation, or an array of animations, to be played in the future, after the current one completes or stops.
|
|
*
|
|
* The current animation must enter a 'completed' state for this to happen, i.e. finish all of its repeats, delays, etc,
|
|
* or have one of the `stop` methods called.
|
|
*
|
|
* An animation set to repeat forever will never enter a completed state unless stopped.
|
|
*
|
|
* You can chain a new animation at any point, including before the current one starts playing, during it, or when it ends (via its `animationcomplete` event).
|
|
*
|
|
* Chained animations are specific to a Game Object, meaning different Game Objects can have different chained animations without impacting the global animation they're playing.
|
|
*
|
|
* Call this method with no arguments to reset all currently chained animations.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#chain
|
|
* @since 3.16.0
|
|
*
|
|
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig|string[]|Phaser.Animations.Animation[]|Phaser.Types.Animations.PlayAnimationConfig[])} [key] - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object, or an array of them.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
chain: function (key)
|
|
{
|
|
var parent = this.parent;
|
|
|
|
if (key === undefined)
|
|
{
|
|
this.nextAnimsQueue.length = 0;
|
|
this.nextAnim = null;
|
|
|
|
return parent;
|
|
}
|
|
|
|
if (!Array.isArray(key))
|
|
{
|
|
key = [ key ];
|
|
}
|
|
|
|
for (var i = 0; i < key.length; i++)
|
|
{
|
|
var anim = key[i];
|
|
|
|
if (!this.nextAnim)
|
|
{
|
|
this.nextAnim = anim;
|
|
}
|
|
else
|
|
{
|
|
this.nextAnimsQueue.push(anim);
|
|
}
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Returns the key of the animation currently loaded into this component.
|
|
*
|
|
* Prior to Phaser 3.50 this method was called `getCurrentKey`.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#getName
|
|
* @since 3.50.0
|
|
*
|
|
* @return {string} The key of the Animation currently loaded into this component, or an empty string if none loaded.
|
|
*/
|
|
getName: function ()
|
|
{
|
|
return (this.currentAnim) ? this.currentAnim.key : '';
|
|
},
|
|
|
|
/**
|
|
* Returns the key of the animation frame currently displayed by this component.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#getFrameName
|
|
* @since 3.50.0
|
|
*
|
|
* @return {string} The key of the Animation Frame currently displayed by this component, or an empty string if no animation has been loaded.
|
|
*/
|
|
getFrameName: function ()
|
|
{
|
|
return (this.currentFrame) ? this.currentFrame.textureFrame : '';
|
|
},
|
|
|
|
/**
|
|
* Internal method used to load an animation into this component.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#load
|
|
* @protected
|
|
* @since 3.0.0
|
|
*
|
|
* @param {(string|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or a `PlayAnimationConfig` object.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
load: function (key)
|
|
{
|
|
if (this.isPlaying)
|
|
{
|
|
this.stop();
|
|
}
|
|
|
|
var manager = this.animationManager;
|
|
var animKey = (typeof key === 'string') ? key : GetFastValue(key, 'key', null);
|
|
|
|
// Get the animation, first from the local map and, if not found, from the Animation Manager
|
|
var anim = (this.exists(animKey)) ? this.get(animKey) : manager.get(animKey);
|
|
|
|
if (!anim)
|
|
{
|
|
console.warn('Missing animation: ' + animKey);
|
|
}
|
|
else
|
|
{
|
|
this.currentAnim = anim;
|
|
|
|
// And now override the animation values, if set in the config.
|
|
|
|
var totalFrames = anim.getTotalFrames();
|
|
var frameRate = GetFastValue(key, 'frameRate', anim.frameRate);
|
|
var duration = GetFastValue(key, 'duration', anim.duration);
|
|
|
|
anim.calculateDuration(this, totalFrames, duration, frameRate);
|
|
|
|
this.delay = GetFastValue(key, 'delay', anim.delay);
|
|
this.repeat = GetFastValue(key, 'repeat', anim.repeat);
|
|
this.repeatDelay = GetFastValue(key, 'repeatDelay', anim.repeatDelay);
|
|
this.yoyo = GetFastValue(key, 'yoyo', anim.yoyo);
|
|
this.showBeforeDelay = GetFastValue(key, 'showBeforeDelay', anim.showBeforeDelay);
|
|
this.showOnStart = GetFastValue(key, 'showOnStart', anim.showOnStart);
|
|
this.hideOnComplete = GetFastValue(key, 'hideOnComplete', anim.hideOnComplete);
|
|
this.skipMissedFrames = GetFastValue(key, 'skipMissedFrames', anim.skipMissedFrames);
|
|
this.randomFrame = GetFastValue(key, 'randomFrame', anim.randomFrame);
|
|
|
|
this.timeScale = GetFastValue(key, 'timeScale', this.timeScale);
|
|
|
|
var startFrame = GetFastValue(key, 'startFrame', 0);
|
|
|
|
if (startFrame > totalFrames)
|
|
{
|
|
startFrame = 0;
|
|
}
|
|
|
|
if (this.randomFrame)
|
|
{
|
|
startFrame = Between(0, totalFrames - 1);
|
|
}
|
|
|
|
var frame = anim.frames[startFrame];
|
|
|
|
if (startFrame === 0 && !this.forward)
|
|
{
|
|
frame = anim.getLastFrame();
|
|
}
|
|
|
|
this.currentFrame = frame;
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Pause the current animation and set the `isPlaying` property to `false`.
|
|
* You can optionally pause it at a specific frame.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#pause
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.Animations.AnimationFrame} [atFrame] - An optional frame to set after pausing the animation.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
pause: function (atFrame)
|
|
{
|
|
if (!this._paused)
|
|
{
|
|
this._paused = true;
|
|
this._wasPlaying = this.isPlaying;
|
|
this.isPlaying = false;
|
|
}
|
|
|
|
if (atFrame !== undefined)
|
|
{
|
|
this.setCurrentFrame(atFrame);
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Resumes playback of a paused animation and sets the `isPlaying` property to `true`.
|
|
* You can optionally tell it to start playback from a specific frame.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#resume
|
|
* @since 3.0.0
|
|
*
|
|
* @param {Phaser.Animations.AnimationFrame} [fromFrame] - An optional frame to set before restarting playback.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
resume: function (fromFrame)
|
|
{
|
|
if (this._paused)
|
|
{
|
|
this._paused = false;
|
|
this.isPlaying = this._wasPlaying;
|
|
}
|
|
|
|
if (fromFrame !== undefined)
|
|
{
|
|
this.setCurrentFrame(fromFrame);
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Waits for the specified delay, in milliseconds, then starts playback of the given animation.
|
|
*
|
|
* If the animation _also_ has a delay value set in its config, it will be **added** to the delay given here.
|
|
*
|
|
* If an animation is already running and a new animation is given to this method, it will wait for
|
|
* the given delay before starting the new animation.
|
|
*
|
|
* If no animation is currently running, the given one begins after the delay.
|
|
*
|
|
* Prior to Phaser 3.50 this method was called 'delayedPlay' and the parameters were in the reverse order.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#playAfterDelay
|
|
* @fires Phaser.Animations.Events#ANIMATION_START
|
|
* @since 3.50.0
|
|
*
|
|
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
|
|
* @param {number} delay - The delay, in milliseconds, to wait before starting the animation playing.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
playAfterDelay: function (key, delay)
|
|
{
|
|
if (!this.isPlaying)
|
|
{
|
|
this.delayCounter = delay;
|
|
|
|
this.play(key, true);
|
|
}
|
|
else
|
|
{
|
|
// If we've got a nextAnim, move it to the queue
|
|
var nextAnim = this.nextAnim;
|
|
var queue = this.nextAnimsQueue;
|
|
|
|
if (nextAnim)
|
|
{
|
|
queue.unshift(nextAnim);
|
|
}
|
|
|
|
this.nextAnim = key;
|
|
|
|
this._pendingStop = 1;
|
|
this._pendingStopValue = delay;
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Waits for the current animation to complete the `repeatCount` number of repeat cycles, then starts playback
|
|
* of the given animation.
|
|
*
|
|
* You can use this to ensure there are no harsh jumps between two sets of animations, i.e. going from an
|
|
* idle animation to a walking animation, by making them blend smoothly into each other.
|
|
*
|
|
* If no animation is currently running, the given one will start immediately.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#playAfterRepeat
|
|
* @fires Phaser.Animations.Events#ANIMATION_START
|
|
* @since 3.50.0
|
|
*
|
|
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
|
|
* @param {number} [repeatCount=1] - How many times should the animation repeat before the next one starts?
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
playAfterRepeat: function (key, repeatCount)
|
|
{
|
|
if (repeatCount === undefined) { repeatCount = 1; }
|
|
|
|
if (!this.isPlaying)
|
|
{
|
|
this.play(key);
|
|
}
|
|
else
|
|
{
|
|
// If we've got a nextAnim, move it to the queue
|
|
var nextAnim = this.nextAnim;
|
|
var queue = this.nextAnimsQueue;
|
|
|
|
if (nextAnim)
|
|
{
|
|
queue.unshift(nextAnim);
|
|
}
|
|
|
|
if (this.repeatCounter !== -1 && repeatCount > this.repeatCounter)
|
|
{
|
|
repeatCount = this.repeatCounter;
|
|
}
|
|
|
|
this.nextAnim = key;
|
|
|
|
this._pendingStop = 2;
|
|
this._pendingStopValue = repeatCount;
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Start playing the given animation on this Sprite.
|
|
*
|
|
* Animations in Phaser can either belong to the global Animation Manager, or specifically to this Sprite.
|
|
*
|
|
* The benefit of a global animation is that multiple Sprites can all play the same animation, without
|
|
* having to duplicate the data. You can just create it once and then play it on any Sprite.
|
|
*
|
|
* The following code shows how to create a global repeating animation. The animation will be created
|
|
* from all of the frames within the sprite sheet that was loaded with the key 'muybridge':
|
|
*
|
|
* ```javascript
|
|
* var config = {
|
|
* key: 'run',
|
|
* frames: 'muybridge',
|
|
* frameRate: 15,
|
|
* repeat: -1
|
|
* };
|
|
*
|
|
* // This code should be run from within a Scene:
|
|
* this.anims.create(config);
|
|
* ```
|
|
*
|
|
* However, if you wish to create an animation that is unique to this Sprite, and this Sprite alone,
|
|
* you can call the `Animation.create` method instead. It accepts the exact same parameters as when
|
|
* creating a global animation, however the resulting data is kept locally in this Sprite.
|
|
*
|
|
* With the animation created, either globally or locally, you can now play it on this Sprite:
|
|
*
|
|
* ```javascript
|
|
* this.add.sprite(x, y).play('run');
|
|
* ```
|
|
*
|
|
* Alternatively, if you wish to run it at a different frame rate, for example, you can pass a config
|
|
* object instead:
|
|
*
|
|
* ```javascript
|
|
* this.add.sprite(x, y).play({ key: 'run', frameRate: 24 });
|
|
* ```
|
|
*
|
|
* When playing an animation on a Sprite it will first check to see if it can find a matching key
|
|
* locally within the Sprite. If it can, it will play the local animation. If not, it will then
|
|
* search the global Animation Manager and look for it there.
|
|
*
|
|
* If you need a Sprite to be able to play both local and global animations, make sure they don't
|
|
* have conflicting keys.
|
|
*
|
|
* See the documentation for the `PlayAnimationConfig` config object for more details about this.
|
|
*
|
|
* Also, see the documentation in the Animation Manager for further details on creating animations.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#play
|
|
* @fires Phaser.Animations.Events#ANIMATION_START
|
|
* @since 3.0.0
|
|
*
|
|
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
|
|
* @param {boolean} [ignoreIfPlaying=false] - If this animation is already playing then ignore this call.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
play: function (key, ignoreIfPlaying)
|
|
{
|
|
if (ignoreIfPlaying === undefined) { ignoreIfPlaying = false; }
|
|
|
|
var currentAnim = this.currentAnim;
|
|
var parent = this.parent;
|
|
|
|
// Must be either an Animation instance, or a PlayAnimationConfig object
|
|
var animKey = (typeof key === 'string') ? key : key.key;
|
|
|
|
if (ignoreIfPlaying && this.isPlaying && currentAnim.key === animKey)
|
|
{
|
|
return parent;
|
|
}
|
|
|
|
// Are we mixing?
|
|
if (currentAnim && this.isPlaying)
|
|
{
|
|
var mix = this.animationManager.getMix(currentAnim.key, key);
|
|
|
|
if (mix > 0)
|
|
{
|
|
return this.playAfterDelay(key, mix);
|
|
}
|
|
}
|
|
|
|
this.forward = true;
|
|
this.inReverse = false;
|
|
|
|
this._paused = false;
|
|
this._wasPlaying = true;
|
|
|
|
return this.startAnimation(key);
|
|
},
|
|
|
|
/**
|
|
* Start playing the given animation on this Sprite, in reverse.
|
|
*
|
|
* Animations in Phaser can either belong to the global Animation Manager, or specifically to this Sprite.
|
|
*
|
|
* The benefit of a global animation is that multiple Sprites can all play the same animation, without
|
|
* having to duplicate the data. You can just create it once and then play it on any Sprite.
|
|
*
|
|
* The following code shows how to create a global repeating animation. The animation will be created
|
|
* from all of the frames within the sprite sheet that was loaded with the key 'muybridge':
|
|
*
|
|
* ```javascript
|
|
* var config = {
|
|
* key: 'run',
|
|
* frames: 'muybridge',
|
|
* frameRate: 15,
|
|
* repeat: -1
|
|
* };
|
|
*
|
|
* // This code should be run from within a Scene:
|
|
* this.anims.create(config);
|
|
* ```
|
|
*
|
|
* However, if you wish to create an animation that is unique to this Sprite, and this Sprite alone,
|
|
* you can call the `Animation.create` method instead. It accepts the exact same parameters as when
|
|
* creating a global animation, however the resulting data is kept locally in this Sprite.
|
|
*
|
|
* With the animation created, either globally or locally, you can now play it on this Sprite:
|
|
*
|
|
* ```javascript
|
|
* this.add.sprite(x, y).playReverse('run');
|
|
* ```
|
|
*
|
|
* Alternatively, if you wish to run it at a different frame rate, for example, you can pass a config
|
|
* object instead:
|
|
*
|
|
* ```javascript
|
|
* this.add.sprite(x, y).playReverse({ key: 'run', frameRate: 24 });
|
|
* ```
|
|
*
|
|
* When playing an animation on a Sprite it will first check to see if it can find a matching key
|
|
* locally within the Sprite. If it can, it will play the local animation. If not, it will then
|
|
* search the global Animation Manager and look for it there.
|
|
*
|
|
* If you need a Sprite to be able to play both local and global animations, make sure they don't
|
|
* have conflicting keys.
|
|
*
|
|
* See the documentation for the `PlayAnimationConfig` config object for more details about this.
|
|
*
|
|
* Also, see the documentation in the Animation Manager for further details on creating animations.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#playReverse
|
|
* @fires Phaser.Animations.Events#ANIMATION_START
|
|
* @since 3.12.0
|
|
*
|
|
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
|
|
* @param {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
playReverse: function (key, ignoreIfPlaying)
|
|
{
|
|
if (ignoreIfPlaying === undefined) { ignoreIfPlaying = false; }
|
|
|
|
// Must be either an Animation instance, or a PlayAnimationConfig object
|
|
var animKey = (typeof key === 'string') ? key : key.key;
|
|
|
|
if (ignoreIfPlaying && this.isPlaying && this.currentAnim.key === animKey)
|
|
{
|
|
return this.parent;
|
|
}
|
|
|
|
this.forward = false;
|
|
this.inReverse = true;
|
|
|
|
this._paused = false;
|
|
this._wasPlaying = true;
|
|
|
|
return this.startAnimation(key);
|
|
},
|
|
|
|
/**
|
|
* Load the animation based on the key and set-up all of the internal values
|
|
* needed for playback to start. If there is no delay, it will also fire the start events.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#startAnimation
|
|
* @fires Phaser.Animations.Events#ANIMATION_START
|
|
* @since 3.50.0
|
|
*
|
|
* @param {(string|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or a `PlayAnimationConfig` object.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
startAnimation: function (key)
|
|
{
|
|
this.load(key);
|
|
|
|
var anim = this.currentAnim;
|
|
var gameObject = this.parent;
|
|
|
|
if (!anim)
|
|
{
|
|
return gameObject;
|
|
}
|
|
|
|
// Should give us 9,007,199,254,740,991 safe repeats
|
|
this.repeatCounter = (this.repeat === -1) ? Number.MAX_VALUE : this.repeat;
|
|
|
|
anim.getFirstTick(this);
|
|
|
|
this.isPlaying = true;
|
|
this.pendingRepeat = false;
|
|
this.hasStarted = false;
|
|
|
|
this._pendingStop = 0;
|
|
this._pendingStopValue = 0;
|
|
this._paused = false;
|
|
|
|
// Add any delay the animation itself may have had as well
|
|
this.delayCounter += this.delay;
|
|
|
|
if (this.delayCounter === 0)
|
|
{
|
|
this.handleStart();
|
|
}
|
|
else if (this.showBeforeDelay)
|
|
{
|
|
// We have a delay, but still need to set the frame
|
|
this.setCurrentFrame(this.currentFrame);
|
|
}
|
|
|
|
return gameObject;
|
|
},
|
|
|
|
/**
|
|
* Handles the start of an animation playback.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#handleStart
|
|
* @private
|
|
* @since 3.50.0
|
|
*/
|
|
handleStart: function ()
|
|
{
|
|
if (this.showOnStart)
|
|
{
|
|
this.parent.setVisible(true);
|
|
}
|
|
|
|
this.setCurrentFrame(this.currentFrame);
|
|
|
|
this.hasStarted = true;
|
|
|
|
this.emitEvents(Events.ANIMATION_START);
|
|
},
|
|
|
|
/**
|
|
* Handles the repeat of an animation.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#handleRepeat
|
|
* @private
|
|
* @since 3.50.0
|
|
*/
|
|
handleRepeat: function ()
|
|
{
|
|
this.pendingRepeat = false;
|
|
|
|
this.emitEvents(Events.ANIMATION_REPEAT);
|
|
},
|
|
|
|
/**
|
|
* Handles the stop of an animation playback.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#handleStop
|
|
* @private
|
|
* @since 3.50.0
|
|
*/
|
|
handleStop: function ()
|
|
{
|
|
this._pendingStop = 0;
|
|
|
|
this.isPlaying = false;
|
|
|
|
this.emitEvents(Events.ANIMATION_STOP);
|
|
},
|
|
|
|
/**
|
|
* Handles the completion of an animation playback.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#handleComplete
|
|
* @private
|
|
* @since 3.50.0
|
|
*/
|
|
handleComplete: function ()
|
|
{
|
|
this._pendingStop = 0;
|
|
|
|
this.isPlaying = false;
|
|
|
|
if (this.hideOnComplete)
|
|
{
|
|
this.parent.setVisible(false);
|
|
}
|
|
|
|
this.emitEvents(Events.ANIMATION_COMPLETE, Events.ANIMATION_COMPLETE_KEY);
|
|
},
|
|
|
|
/**
|
|
* Fires the given animation event.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#emitEvents
|
|
* @private
|
|
* @since 3.50.0
|
|
*
|
|
* @param {string} event - The Animation Event to dispatch.
|
|
*/
|
|
emitEvents: function (event, keyEvent)
|
|
{
|
|
var anim = this.currentAnim;
|
|
|
|
if (anim)
|
|
{
|
|
var frame = this.currentFrame;
|
|
|
|
var gameObject = this.parent;
|
|
|
|
var frameKey = frame.textureFrame;
|
|
|
|
gameObject.emit(event, anim, frame, gameObject, frameKey);
|
|
|
|
if (keyEvent)
|
|
{
|
|
gameObject.emit(keyEvent + anim.key, anim, frame, gameObject, frameKey);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Reverse the Animation that is already playing on the Game Object.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#reverse
|
|
* @since 3.12.0
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
reverse: function ()
|
|
{
|
|
if (this.isPlaying)
|
|
{
|
|
this.inReverse = !this.inReverse;
|
|
|
|
this.forward = !this.forward;
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Returns a value between 0 and 1 indicating how far this animation is through, ignoring repeats and yoyos.
|
|
*
|
|
* The value is based on the current frame and how far that is in the animation, it is not based on
|
|
* the duration of the animation.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#getProgress
|
|
* @since 3.4.0
|
|
*
|
|
* @return {number} The progress of the current animation in frames, between 0 and 1.
|
|
*/
|
|
getProgress: function ()
|
|
{
|
|
var frame = this.currentFrame;
|
|
|
|
if (!frame)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
var p = frame.progress;
|
|
|
|
if (this.inReverse)
|
|
{
|
|
p *= -1;
|
|
}
|
|
|
|
return p;
|
|
},
|
|
|
|
/**
|
|
* Takes a value between 0 and 1 and uses it to set how far this animation is through playback.
|
|
*
|
|
* Does not factor in repeats or yoyos, but does handle playing forwards or backwards.
|
|
*
|
|
* The value is based on the current frame and how far that is in the animation, it is not based on
|
|
* the duration of the animation.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#setProgress
|
|
* @since 3.4.0
|
|
*
|
|
* @param {number} [value=0] - The progress value, between 0 and 1.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
setProgress: function (value)
|
|
{
|
|
if (!this.forward)
|
|
{
|
|
value = 1 - value;
|
|
}
|
|
|
|
this.setCurrentFrame(this.currentAnim.getFrameByProgress(value));
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Sets the number of times that the animation should repeat after its first play through.
|
|
* For example, if repeat is 1, the animation will play a total of twice: the initial play plus 1 repeat.
|
|
*
|
|
* To repeat indefinitely, use -1.
|
|
* The value should always be an integer.
|
|
*
|
|
* Calling this method only works if the animation is already running. Otherwise, any
|
|
* value specified here will be overwritten when the next animation loads in. To avoid this,
|
|
* use the `repeat` property of the `PlayAnimationConfig` object instead.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#setRepeat
|
|
* @since 3.4.0
|
|
*
|
|
* @param {number} value - The number of times that the animation should repeat.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
setRepeat: function (value)
|
|
{
|
|
this.repeatCounter = (value === -1) ? Number.MAX_VALUE : value;
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Handle the removal of an animation from the Animation Manager.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#globalRemove
|
|
* @since 3.50.0
|
|
*
|
|
* @param {string} [key] - The key of the removed Animation.
|
|
* @param {Phaser.Animations.Animation} [animation] - The removed Animation.
|
|
*/
|
|
globalRemove: function (key, animation)
|
|
{
|
|
if (animation === undefined) { animation = this.currentAnim; }
|
|
|
|
if (this.isPlaying && animation.key === this.currentAnim.key)
|
|
{
|
|
this.stop();
|
|
|
|
this.setCurrentFrame(this.currentAnim.frames[0]);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Restarts the current animation from its beginning.
|
|
*
|
|
* You can optionally reset the delay and repeat counters as well.
|
|
*
|
|
* Calling this will fire the `ANIMATION_RESTART` event immediately.
|
|
*
|
|
* If you `includeDelay` then it will also fire the `ANIMATION_START` event once
|
|
* the delay has expired, otherwise, playback will just begin immediately.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#restart
|
|
* @fires Phaser.Animations.Events#ANIMATION_RESTART
|
|
* @since 3.0.0
|
|
*
|
|
* @param {boolean} [includeDelay=false] - Whether to include the delay value of the animation when restarting.
|
|
* @param {boolean} [resetRepeats=false] - Whether to reset the repeat counter or not?
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
restart: function (includeDelay, resetRepeats)
|
|
{
|
|
if (includeDelay === undefined) { includeDelay = false; }
|
|
if (resetRepeats === undefined) { resetRepeats = false; }
|
|
|
|
var anim = this.currentAnim;
|
|
var gameObject = this.parent;
|
|
|
|
if (!anim)
|
|
{
|
|
return gameObject;
|
|
}
|
|
|
|
if (resetRepeats)
|
|
{
|
|
this.repeatCounter = (this.repeat === -1) ? Number.MAX_VALUE : this.repeat;
|
|
}
|
|
|
|
anim.getFirstTick(this);
|
|
|
|
this.emitEvents(Events.ANIMATION_RESTART);
|
|
|
|
this.isPlaying = true;
|
|
this.pendingRepeat = false;
|
|
|
|
// Set this to `true` if there is no delay to include, so it skips the `hasStarted` check in `update`.
|
|
this.hasStarted = !includeDelay;
|
|
|
|
this._pendingStop = 0;
|
|
this._pendingStopValue = 0;
|
|
this._paused = false;
|
|
|
|
this.setCurrentFrame(anim.frames[0]);
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* The current animation has completed. This dispatches the `ANIMATION_COMPLETE` event.
|
|
*
|
|
* This method is called by the Animation instance and should not usually be invoked directly.
|
|
*
|
|
* If no animation is loaded, no events will be dispatched.
|
|
*
|
|
* If another animation has been queued for playback, it will be started after the events fire.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#complete
|
|
* @fires Phaser.Animations.Events#ANIMATION_COMPLETE
|
|
* @since 3.50.0
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
complete: function ()
|
|
{
|
|
this._pendingStop = 0;
|
|
|
|
this.isPlaying = false;
|
|
|
|
if (this.currentAnim)
|
|
{
|
|
this.handleComplete();
|
|
}
|
|
|
|
if (this.nextAnim)
|
|
{
|
|
var key = this.nextAnim;
|
|
|
|
this.nextAnim = (this.nextAnimsQueue.length > 0) ? this.nextAnimsQueue.shift() : null;
|
|
|
|
this.play(key);
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Immediately stops the current animation from playing and dispatches the `ANIMATION_STOP` event.
|
|
*
|
|
* If no animation is running, no events will be dispatched.
|
|
*
|
|
* If there is another animation in the queue (set via the `chain` method) then it will start playing.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#stop
|
|
* @fires Phaser.Animations.Events#ANIMATION_STOP
|
|
* @since 3.0.0
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
stop: function ()
|
|
{
|
|
this._pendingStop = 0;
|
|
|
|
this.isPlaying = false;
|
|
|
|
this.delayCounter = 0;
|
|
|
|
if (this.currentAnim)
|
|
{
|
|
this.handleStop();
|
|
}
|
|
|
|
if (this.nextAnim)
|
|
{
|
|
var key = this.nextAnim;
|
|
|
|
this.nextAnim = this.nextAnimsQueue.shift();
|
|
|
|
this.play(key);
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Stops the current animation from playing after the specified time delay, given in milliseconds.
|
|
*
|
|
* It then dispatches the `ANIMATION_STOP` event.
|
|
*
|
|
* If no animation is running, no events will be dispatched.
|
|
*
|
|
* If there is another animation in the queue (set via the `chain` method) then it will start playing,
|
|
* when the current one stops.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#stopAfterDelay
|
|
* @fires Phaser.Animations.Events#ANIMATION_STOP
|
|
* @since 3.4.0
|
|
*
|
|
* @param {number} delay - The number of milliseconds to wait before stopping this animation.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
stopAfterDelay: function (delay)
|
|
{
|
|
this._pendingStop = 1;
|
|
this._pendingStopValue = delay;
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Stops the current animation from playing when it next repeats.
|
|
*
|
|
* It then dispatches the `ANIMATION_STOP` event.
|
|
*
|
|
* If no animation is running, no events will be dispatched.
|
|
*
|
|
* If there is another animation in the queue (set via the `chain` method) then it will start playing,
|
|
* when the current one stops.
|
|
*
|
|
* Prior to Phaser 3.50 this method was called `stopOnRepeat` and had no parameters.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#stopAfterRepeat
|
|
* @fires Phaser.Animations.Events#ANIMATION_STOP
|
|
* @since 3.50.0
|
|
*
|
|
* @param {number} [repeatCount=1] - How many times should the animation repeat before stopping?
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
stopAfterRepeat: function (repeatCount)
|
|
{
|
|
if (repeatCount === undefined) { repeatCount = 1; }
|
|
|
|
if (this.repeatCounter !== -1 && repeatCount > this.repeatCounter)
|
|
{
|
|
repeatCount = this.repeatCounter;
|
|
}
|
|
|
|
this._pendingStop = 2;
|
|
this._pendingStopValue = repeatCount;
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Stops the current animation from playing when it next sets the given frame.
|
|
* If this frame doesn't exist within the animation it will not stop it from playing.
|
|
*
|
|
* It then dispatches the `ANIMATION_STOP` event.
|
|
*
|
|
* If no animation is running, no events will be dispatched.
|
|
*
|
|
* If there is another animation in the queue (set via the `chain` method) then it will start playing,
|
|
* when the current one stops.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#stopOnFrame
|
|
* @fires Phaser.Animations.Events#ANIMATION_STOP
|
|
* @since 3.4.0
|
|
*
|
|
* @param {Phaser.Animations.AnimationFrame} frame - The frame to check before stopping this animation.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
|
|
*/
|
|
stopOnFrame: function (frame)
|
|
{
|
|
this._pendingStop = 3;
|
|
this._pendingStopValue = frame;
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Returns the total number of frames in this animation, or returns zero if no
|
|
* animation has been loaded.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#getTotalFrames
|
|
* @since 3.4.0
|
|
*
|
|
* @return {number} The total number of frames in the current animation, or zero if no animation has been loaded.
|
|
*/
|
|
getTotalFrames: function ()
|
|
{
|
|
return (this.currentAnim) ? this.currentAnim.getTotalFrames() : 0;
|
|
},
|
|
|
|
/**
|
|
* The internal update loop for the AnimationState Component.
|
|
*
|
|
* This is called automatically by the `Sprite.preUpdate` method.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#update
|
|
* @since 3.0.0
|
|
*
|
|
* @param {number} time - The current timestamp.
|
|
* @param {number} delta - The delta time, in ms, elapsed since the last frame.
|
|
*/
|
|
update: function (time, delta)
|
|
{
|
|
var anim = this.currentAnim;
|
|
|
|
if (!this.isPlaying || !anim || anim.paused)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.accumulator += delta * this.timeScale * this.animationManager.globalTimeScale;
|
|
|
|
if (this._pendingStop === 1)
|
|
{
|
|
this._pendingStopValue -= delta;
|
|
|
|
if (this._pendingStopValue <= 0)
|
|
{
|
|
return this.stop();
|
|
}
|
|
}
|
|
|
|
if (!this.hasStarted)
|
|
{
|
|
if (this.accumulator >= this.delayCounter)
|
|
{
|
|
this.accumulator -= this.delayCounter;
|
|
|
|
this.handleStart();
|
|
}
|
|
}
|
|
else if (this.accumulator >= this.nextTick)
|
|
{
|
|
// Process one frame advance as standard
|
|
|
|
if (this.forward)
|
|
{
|
|
anim.nextFrame(this);
|
|
}
|
|
else
|
|
{
|
|
anim.previousFrame(this);
|
|
}
|
|
|
|
// And only do more if we're skipping frames and have time left
|
|
if (this.isPlaying && this._pendingStop === 0 && this.skipMissedFrames && this.accumulator > this.nextTick)
|
|
{
|
|
var safetyNet = 0;
|
|
|
|
do
|
|
{
|
|
if (this.forward)
|
|
{
|
|
anim.nextFrame(this);
|
|
}
|
|
else
|
|
{
|
|
anim.previousFrame(this);
|
|
}
|
|
|
|
safetyNet++;
|
|
|
|
} while (this.isPlaying && this.accumulator > this.nextTick && safetyNet < 60);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets the given Animation Frame as being the current frame
|
|
* and applies it to the parent Game Object, adjusting size and origin as needed.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#setCurrentFrame
|
|
* @fires Phaser.Animations.Events#ANIMATION_UPDATE
|
|
* @fires Phaser.Animations.Events#ANIMATION_STOP
|
|
* @since 3.4.0
|
|
*
|
|
* @param {Phaser.Animations.AnimationFrame} animationFrame - The animation frame to change to.
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object this Animation Component belongs to.
|
|
*/
|
|
setCurrentFrame: function (animationFrame)
|
|
{
|
|
var gameObject = this.parent;
|
|
|
|
this.currentFrame = animationFrame;
|
|
|
|
gameObject.texture = animationFrame.frame.texture;
|
|
gameObject.frame = animationFrame.frame;
|
|
|
|
if (gameObject.isCropped)
|
|
{
|
|
gameObject.frame.updateCropUVs(gameObject._crop, gameObject.flipX, gameObject.flipY);
|
|
}
|
|
|
|
if (animationFrame.setAlpha)
|
|
{
|
|
gameObject.alpha = animationFrame.alpha;
|
|
}
|
|
|
|
gameObject.setSizeToFrame();
|
|
|
|
if (gameObject._originComponent)
|
|
{
|
|
if (animationFrame.frame.customPivot)
|
|
{
|
|
gameObject.setOrigin(animationFrame.frame.pivotX, animationFrame.frame.pivotY);
|
|
}
|
|
else
|
|
{
|
|
gameObject.updateDisplayOrigin();
|
|
}
|
|
}
|
|
|
|
if (this.isPlaying && this.hasStarted)
|
|
{
|
|
this.emitEvents(Events.ANIMATION_UPDATE);
|
|
|
|
if (this._pendingStop === 3 && this._pendingStopValue === animationFrame)
|
|
{
|
|
this.stop();
|
|
}
|
|
}
|
|
|
|
return gameObject;
|
|
},
|
|
|
|
/**
|
|
* Advances the animation to the next frame, regardless of the time or animation state.
|
|
* If the animation is set to repeat, or yoyo, this will still take effect.
|
|
*
|
|
* Calling this does not change the direction of the animation. I.e. if it was currently
|
|
* playing in reverse, calling this method doesn't then change the direction to forwards.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#nextFrame
|
|
* @since 3.16.0
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object this Animation Component belongs to.
|
|
*/
|
|
nextFrame: function ()
|
|
{
|
|
if (this.currentAnim)
|
|
{
|
|
this.currentAnim.nextFrame(this);
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Advances the animation to the previous frame, regardless of the time or animation state.
|
|
* If the animation is set to repeat, or yoyo, this will still take effect.
|
|
*
|
|
* Calling this does not change the direction of the animation. I.e. if it was currently
|
|
* playing in forwards, calling this method doesn't then change the direction to backwards.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#previousFrame
|
|
* @since 3.16.0
|
|
*
|
|
* @return {Phaser.GameObjects.GameObject} The Game Object this Animation Component belongs to.
|
|
*/
|
|
previousFrame: function ()
|
|
{
|
|
if (this.currentAnim)
|
|
{
|
|
this.currentAnim.previousFrame(this);
|
|
}
|
|
|
|
return this.parent;
|
|
},
|
|
|
|
/**
|
|
* Get an Animation instance that has been created locally on this Sprite.
|
|
*
|
|
* See the `create` method for more details.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#get
|
|
* @since 3.50.0
|
|
*
|
|
* @param {string} key - The key of the Animation to retrieve.
|
|
*
|
|
* @return {Phaser.Animations.Animation} The Animation, or `null` if the key is invalid.
|
|
*/
|
|
get: function (key)
|
|
{
|
|
return (this.anims) ? this.anims.get(key) : null;
|
|
},
|
|
|
|
/**
|
|
* Checks to see if the given key is already used locally within the animations stored on this Sprite.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#exists
|
|
* @since 3.50.0
|
|
*
|
|
* @param {string} key - The key of the Animation to check.
|
|
*
|
|
* @return {boolean} `true` if the Animation exists locally, or `false` if the key is available, or there are no local animations.
|
|
*/
|
|
exists: function (key)
|
|
{
|
|
return (this.anims) ? this.anims.has(key) : false;
|
|
},
|
|
|
|
/**
|
|
* Creates a new Animation that is local specifically to this Sprite.
|
|
*
|
|
* When a Sprite owns an animation, it is kept out of the global Animation Manager, which means
|
|
* you're free to use keys that may be already defined there. Unless you specifically need a Sprite
|
|
* to have a unique animation, you should favor using global animations instead, as they allow for
|
|
* the same animation to be used across multiple Sprites, saving on memory. However, if this Sprite
|
|
* is the only one to use this animation, it's sensible to create it here.
|
|
*
|
|
* If an invalid key is given this method will return `false`.
|
|
*
|
|
* If you pass the key of an animation that already exists locally, that animation will be returned.
|
|
*
|
|
* A brand new animation is only created if the key is valid and not already in use by this Sprite.
|
|
*
|
|
* If you wish to re-use an existing key, call the `remove` method first, then this method.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#create
|
|
* @since 3.50.0
|
|
*
|
|
* @param {Phaser.Types.Animations.Animation} config - The configuration settings for the Animation.
|
|
*
|
|
* @return {(Phaser.Animations.Animation|false)} The Animation that was created, or `false` if the key is already in use.
|
|
*/
|
|
create: function (config)
|
|
{
|
|
var key = config.key;
|
|
|
|
var anim = false;
|
|
|
|
if (key)
|
|
{
|
|
anim = this.get(key);
|
|
|
|
if (!anim)
|
|
{
|
|
anim = new Animation(this, key, config);
|
|
|
|
if (!this.anims)
|
|
{
|
|
this.anims = new CustomMap();
|
|
}
|
|
|
|
this.anims.set(key, anim);
|
|
}
|
|
else
|
|
{
|
|
console.warn('Animation key already exists: ' + key);
|
|
}
|
|
}
|
|
|
|
return anim;
|
|
},
|
|
|
|
/**
|
|
* Create one, or more animations from a loaded Aseprite JSON file.
|
|
*
|
|
* Aseprite is a powerful animated sprite editor and pixel art tool.
|
|
*
|
|
* You can find more details at https://www.aseprite.org/
|
|
*
|
|
* To export a compatible JSON file in Aseprite, please do the following:
|
|
*
|
|
* 1. Go to "File - Export Sprite Sheet"
|
|
*
|
|
* 2. On the **Layout** tab:
|
|
* 2a. Set the "Sheet type" to "Packed"
|
|
* 2b. Set the "Constraints" to "None"
|
|
* 2c. Check the "Merge Duplicates" checkbox
|
|
*
|
|
* 3. On the **Sprite** tab:
|
|
* 3a. Set "Layers" to "Visible layers"
|
|
* 3b. Set "Frames" to "All frames", unless you only wish to export a sub-set of tags
|
|
*
|
|
* 4. On the **Borders** tab:
|
|
* 4a. Check the "Trim Sprite" and "Trim Cells" options
|
|
* 4b. Ensure "Border Padding", "Spacing" and "Inner Padding" are all > 0 (1 is usually enough)
|
|
*
|
|
* 5. On the **Output** tab:
|
|
* 5a. Check "Output File", give your image a name and make sure you choose "png files" as the file type
|
|
* 5b. Check "JSON Data" and give your json file a name
|
|
* 5c. The JSON Data type can be either a Hash or Array, Phaser doesn't mind.
|
|
* 5d. Make sure "Tags" is checked in the Meta options
|
|
* 5e. In the "Item Filename" input box, make sure it says just "{frame}" and nothing more.
|
|
*
|
|
* 6. Click export
|
|
*
|
|
* This was tested with Aseprite 1.2.25.
|
|
*
|
|
* This will export a png and json file which you can load using the Aseprite Loader, i.e.:
|
|
*
|
|
* ```javascript
|
|
* function preload ()
|
|
* {
|
|
* this.load.path = 'assets/animations/aseprite/';
|
|
* this.load.aseprite('paladin', 'paladin.png', 'paladin.json');
|
|
* }
|
|
* ```
|
|
*
|
|
* Once loaded, you can call this method on a Sprite with the 'atlas' key:
|
|
*
|
|
* ```javascript
|
|
* const sprite = this.add.sprite(400, 300);
|
|
*
|
|
* sprite.anims.createFromAseprite('paladin');
|
|
* ```
|
|
*
|
|
* Any animations defined in the JSON will now be available to use on this Sprite and you play them
|
|
* via their Tag name. For example, if you have an animation called 'War Cry' on your Aseprite timeline,
|
|
* you can play it on the Sprite using that Tag name:
|
|
*
|
|
* ```javascript
|
|
* const sprite = this.add.sprite(400, 300);
|
|
*
|
|
* sprite.anims.createFromAseprite('paladin');
|
|
*
|
|
* sprite.play('War Cry');
|
|
* ```
|
|
*
|
|
* When calling this method you can optionally provide an array of tag names, and only those animations
|
|
* will be created. For example:
|
|
*
|
|
* ```javascript
|
|
* sprite.anims.createFromAseprite('paladin', [ 'step', 'War Cry', 'Magnum Break' ]);
|
|
* ```
|
|
*
|
|
* This will only create the 3 animations defined. Note that the tag names are case-sensitive.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#createFromAseprite
|
|
* @since 3.60.0
|
|
*
|
|
* @param {string} key - The key of the loaded Aseprite atlas. It must have been loaded prior to calling this method.
|
|
* @param {string[]} [tags] - An array of Tag names. If provided, only animations found in this array will be created.
|
|
*
|
|
* @return {Phaser.Animations.Animation[]} An array of Animation instances that were successfully created.
|
|
*/
|
|
createFromAseprite: function (key, tags)
|
|
{
|
|
return this.animationManager.createFromAseprite(key, tags, this.parent);
|
|
},
|
|
|
|
/**
|
|
* Generate an array of {@link Phaser.Types.Animations.AnimationFrame} objects from a texture key and configuration object.
|
|
*
|
|
* Generates objects with string based frame names, as configured by the given {@link Phaser.Types.Animations.GenerateFrameNames}.
|
|
*
|
|
* It's a helper method, designed to make it easier for you to extract all of the frame names from texture atlases.
|
|
* If you're working with a sprite sheet, see the `generateFrameNumbers` method instead.
|
|
*
|
|
* Example:
|
|
*
|
|
* If you have a texture atlases loaded called `gems` and it contains 6 frames called `ruby_0001`, `ruby_0002`, and so on,
|
|
* then you can call this method using: `this.anims.generateFrameNames('gems', { prefix: 'ruby_', end: 6, zeroPad: 4 })`.
|
|
*
|
|
* The `end` value tells it to look for 6 frames, incrementally numbered, all starting with the prefix `ruby_`. The `zeroPad`
|
|
* value tells it how many zeroes pad out the numbers. To create an animation using this method, you can do:
|
|
*
|
|
* ```javascript
|
|
* this.anims.create({
|
|
* key: 'ruby',
|
|
* repeat: -1,
|
|
* frames: this.anims.generateFrameNames('gems', {
|
|
* prefix: 'ruby_',
|
|
* end: 6,
|
|
* zeroPad: 4
|
|
* })
|
|
* });
|
|
* ```
|
|
*
|
|
* Please see the animation examples for further details.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#generateFrameNames
|
|
* @since 3.50.0
|
|
*
|
|
* @param {string} key - The key for the texture containing the animation frames.
|
|
* @param {Phaser.Types.Animations.GenerateFrameNames} [config] - The configuration object for the animation frame names.
|
|
*
|
|
* @return {Phaser.Types.Animations.AnimationFrame[]} The array of {@link Phaser.Types.Animations.AnimationFrame} objects.
|
|
*/
|
|
generateFrameNames: function (key, config)
|
|
{
|
|
return this.animationManager.generateFrameNames(key, config);
|
|
},
|
|
|
|
/**
|
|
* Generate an array of {@link Phaser.Types.Animations.AnimationFrame} objects from a texture key and configuration object.
|
|
*
|
|
* Generates objects with numbered frame names, as configured by the given {@link Phaser.Types.Animations.GenerateFrameNumbers}.
|
|
*
|
|
* If you're working with a texture atlas, see the `generateFrameNames` method instead.
|
|
*
|
|
* It's a helper method, designed to make it easier for you to extract frames from sprite sheets.
|
|
* If you're working with a texture atlas, see the `generateFrameNames` method instead.
|
|
*
|
|
* Example:
|
|
*
|
|
* If you have a sprite sheet loaded called `explosion` and it contains 12 frames, then you can call this method using:
|
|
* `this.anims.generateFrameNumbers('explosion', { start: 0, end: 11 })`.
|
|
*
|
|
* The `end` value tells it to stop after 12 frames. To create an animation using this method, you can do:
|
|
*
|
|
* ```javascript
|
|
* this.anims.create({
|
|
* key: 'boom',
|
|
* frames: this.anims.generateFrameNumbers('explosion', {
|
|
* start: 0,
|
|
* end: 11
|
|
* })
|
|
* });
|
|
* ```
|
|
*
|
|
* Note that `start` is optional and you don't need to include it if the animation starts from frame 0.
|
|
*
|
|
* To specify an animation in reverse, swap the `start` and `end` values.
|
|
*
|
|
* If the frames are not sequential, you may pass an array of frame numbers instead, for example:
|
|
*
|
|
* `this.anims.generateFrameNumbers('explosion', { frames: [ 0, 1, 2, 1, 2, 3, 4, 0, 1, 2 ] })`
|
|
*
|
|
* Please see the animation examples and `GenerateFrameNumbers` config docs for further details.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#generateFrameNumbers
|
|
* @since 3.50.0
|
|
*
|
|
* @param {string} key - The key for the texture containing the animation frames.
|
|
* @param {Phaser.Types.Animations.GenerateFrameNumbers} [config] - The configuration object for the animation frames.
|
|
*
|
|
* @return {Phaser.Types.Animations.AnimationFrame[]} The array of {@link Phaser.Types.Animations.AnimationFrame} objects.
|
|
*/
|
|
generateFrameNumbers: function (key, config)
|
|
{
|
|
return this.animationManager.generateFrameNumbers(key, config);
|
|
},
|
|
|
|
/**
|
|
* Removes a locally created Animation from this Sprite, based on the given key.
|
|
*
|
|
* Once an Animation has been removed, this Sprite cannot play it again without re-creating it.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#remove
|
|
* @since 3.50.0
|
|
*
|
|
* @param {string} key - The key of the animation to remove.
|
|
*
|
|
* @return {Phaser.Animations.Animation} The Animation instance that was removed from this Sprite, if the key was valid.
|
|
*/
|
|
remove: function (key)
|
|
{
|
|
var anim = this.get(key);
|
|
|
|
if (anim)
|
|
{
|
|
if (this.currentAnim === anim)
|
|
{
|
|
this.stop();
|
|
}
|
|
|
|
this.anims.delete(key);
|
|
}
|
|
|
|
return anim;
|
|
},
|
|
|
|
/**
|
|
* Destroy this Animation component.
|
|
*
|
|
* Unregisters event listeners and cleans up its references.
|
|
*
|
|
* @method Phaser.Animations.AnimationState#destroy
|
|
* @since 3.0.0
|
|
*/
|
|
destroy: function ()
|
|
{
|
|
this.animationManager.off(Events.REMOVE_ANIMATION, this.globalRemove, this);
|
|
|
|
if (this.anims)
|
|
{
|
|
this.anims.clear();
|
|
}
|
|
|
|
this.animationManager = null;
|
|
this.parent = null;
|
|
this.nextAnim = null;
|
|
this.nextAnimsQueue.length = 0;
|
|
|
|
this.currentAnim = null;
|
|
this.currentFrame = null;
|
|
},
|
|
|
|
/**
|
|
* `true` if the current animation is paused, otherwise `false`.
|
|
*
|
|
* @name Phaser.Animations.AnimationState#isPaused
|
|
* @readonly
|
|
* @type {boolean}
|
|
* @since 3.4.0
|
|
*/
|
|
isPaused: {
|
|
|
|
get: function ()
|
|
{
|
|
return this._paused;
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
module.exports = AnimationState;
|