phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
577 lines (491 loc) • 15.6 kB
JavaScript
/**
* @author Richard Davey <rich@phaser.io>
* @copyright 2013-2025 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var ArrayRemove = require('../../utils/array/Remove');
var BaseTween = require('./BaseTween');
var Class = require('../../utils/Class');
var Events = require('../events');
var GameObjectCreator = require('../../gameobjects/GameObjectCreator');
var GameObjectFactory = require('../../gameobjects/GameObjectFactory');
var TWEEN_CONST = require('./const');
/**
* @classdesc
* A TweenChain is a special type of Tween that allows you to create a sequence of Tweens, chained to one-another,
* and add them to the Tween Manager.
*
* The tweens are played in order, from start to finish. You can optionally set the chain
* to repeat as many times as you like. Once the chain has finished playing, or repeating if set,
* all tweens in the chain will be destroyed automatically. To override this, set the 'persist'
* argument to 'true'.
*
* Playback will start immediately unless the _first_ Tween has been configured to be paused.
*
* Please note that Tweens will not manipulate any target property that begins with an underscore.
*
* @class TweenChain
* @memberof Phaser.Tweens
* @extends Phaser.Tweens.BaseTween
* @constructor
* @since 3.60.0
*
* @param {(Phaser.Tweens.TweenManager|Phaser.Tweens.TweenChain)} parent - A reference to the Tween Manager, or TweenChain, that owns this TweenChain.
*/
var TweenChain = new Class({
Extends: BaseTween,
initialize:
function TweenChain (parent)
{
BaseTween.call(this, parent);
/**
* A reference to the Tween that this TweenChain is currently playing.
*
* @name Phaser.Tweens.TweenChain#currentTween
* @type {Phaser.Tweens.Tween}
* @since 3.60.0
*/
this.currentTween = null;
/**
* A reference to the data array index of the currently playing tween.
*
* @name Phaser.Tweens.TweenChain#currentIndex
* @type {number}
* @since 3.60.0
*/
this.currentIndex = 0;
},
/**
* Prepares this TweenChain for playback.
*
* Called automatically by the TweenManager. Should not be called directly.
*
* @method Phaser.Tweens.TweenChain#init
* @fires Phaser.Tweens.Events#TWEEN_ACTIVE
* @since 3.60.0
*
* @return {this} This TweenChain instance.
*/
init: function ()
{
this.loopCounter = (this.loop === -1) ? TWEEN_CONST.MAX : this.loop;
this.setCurrentTween(0);
if (this.startDelay > 0 && !this.isStartDelayed())
{
this.setStartDelayState();
}
else
{
this.setActiveState();
}
return this;
},
/**
* Create a sequence of Tweens, chained to one-another, and add them to this Tween Manager.
*
* The tweens are played in order, from start to finish. You can optionally set the chain
* to repeat as many times as you like. Once the chain has finished playing, or repeating if set,
* all tweens in the chain will be destroyed automatically. To override this, set the 'persist'
* argument to 'true'.
*
* Playback will start immediately unless the _first_ Tween has been configured to be paused.
*
* Please note that Tweens will not manipulate any target property that begins with an underscore.
*
* @method Phaser.Tweens.TweenChain#add
* @since 3.60.0
*
* @param {Phaser.Types.Tweens.TweenBuilderConfig[]|object[]} tweens - An array of Tween configuration objects for the Tweens in this chain.
*
* @return {this} This TweenChain instance.
*/
add: function (tweens)
{
var newTweens = this.parent.create(tweens);
if (!Array.isArray(newTweens))
{
newTweens = [ newTweens ];
}
var data = this.data;
for (var i = 0; i < newTweens.length; i++)
{
var tween = newTweens[i];
tween.parent = this;
data.push(tween.reset());
}
this.totalData = data.length;
return this;
},
/**
* Removes the given Tween from this Tween Chain.
*
* The removed tween is _not_ destroyed. It is just removed from this Tween Chain.
*
* If the given Tween is currently playing then the chain will automatically move
* to the next tween in the chain. If there are no more tweens, this chain will complete.
*
* @method Phaser.Tweens.TweenChain#remove
* @since 3.60.0
* @override
*
* @param {Phaser.Tweens.Tween} tween - The Tween to be removed.
*
* @return {this} This Tween Chain instance.
*/
remove: function (tween)
{
// Remove it immediately
ArrayRemove(this.data, tween);
tween.setRemovedState();
if (tween === this.currentTween)
{
this.nextTween();
}
this.totalData = this.data.length;
return this;
},
/**
* See if any of the tweens in this Tween Chain is currently acting upon the given target.
*
* @method Phaser.Tweens.TweenChain#hasTarget
* @since 3.60.0
*
* @param {object} target - The target to check against this TweenChain.
*
* @return {boolean} `true` if the given target is a target of this TweenChain, otherwise `false`.
*/
hasTarget: function (target)
{
var data = this.data;
for (var i = 0; i < this.totalData; i++)
{
if (data[i].hasTarget(target))
{
return true;
}
}
return false;
},
/**
* Restarts the TweenChain from the beginning.
*
* If this TweenChain was configured to have a loop, or start delay, those
* are reset to their initial values as well. It will also dispatch the
* `onActive` callback and event again.
*
* @method Phaser.Tweens.TweenChain#restart
* @since 3.60.0
*
* @return {this} This TweenChain instance.
*/
restart: function ()
{
if (this.isDestroyed())
{
console.warn('Cannot restart destroyed TweenChain', this);
return this;
}
if (this.isRemoved())
{
this.parent.makeActive(this);
}
this.resetTweens();
this.paused = false;
return this.init();
},
/**
* Resets the given Tween.
*
* It will seek to position 0 and playback will start on the next frame.
*
* @method Phaser.Tweens.TweenChain#reset
* @since 3.60.0
*
* @param {Phaser.Tweens.Tween} tween - The Tween to be reset.
*
* @return {this} This TweenChain instance.
*/
reset: function (tween)
{
tween.seek();
tween.setActiveState();
return this;
},
/**
* Re-initialises the given Tween and sets it to the Active state.
*
* @method Phaser.Tweens.TweenChain#makeActive
* @since 3.60.0
* @override
*
* @param {Phaser.Tweens.Tween} tween - The Tween to check.
*
* @return {this} This TweenChain instance.
*/
makeActive: function (tween)
{
tween.reset();
tween.setActiveState();
return this;
},
/**
* Internal method that advances to the next state of the TweenChain playback.
*
* @method Phaser.Tweens.TweenChain#nextState
* @fires Phaser.Tweens.Events#TWEEN_COMPLETE
* @fires Phaser.Tweens.Events#TWEEN_LOOP
* @since 3.60.0
*
* @return {boolean} `true` if this TweenChain has completed, otherwise `false`.
*/
nextState: function ()
{
if (this.loopCounter > 0)
{
this.loopCounter--;
this.resetTweens();
if (this.loopDelay > 0)
{
this.countdown = this.loopDelay;
this.setLoopDelayState();
}
else
{
this.setActiveState();
this.dispatchEvent(Events.TWEEN_LOOP, 'onLoop');
}
}
else if (this.completeDelay > 0)
{
this.countdown = this.completeDelay;
this.setCompleteDelayState();
}
else
{
this.onCompleteHandler();
return true;
}
return false;
},
/**
* Starts this TweenChain playing.
*
* You only need to call this method if you have configured this TweenChain to be paused on creation.
*
* If the TweenChain is already playing, calling this method again will have no effect. If you wish to
* restart the chain, use `TweenChain.restart` instead.
*
* Calling this method after the TweenChain has completed will start the chain playing again from the beginning.
*
* @method Phaser.Tweens.TweenChain#play
* @since 3.60.0
*
* @return {this} This TweenChain instance.
*/
play: function ()
{
if (this.isDestroyed())
{
console.warn('Cannot play destroyed TweenChain', this);
return this;
}
if (this.isPendingRemove() || this.isPending())
{
this.resetTweens();
}
this.paused = false;
if (this.startDelay > 0 && !this.isStartDelayed())
{
this.setStartDelayState();
}
else
{
this.setActiveState();
}
return this;
},
/**
* Internal method that resets all of the Tweens and the current index pointer.
*
* @method Phaser.Tweens.TweenChain#resetTweens
* @since 3.60.0
*/
resetTweens: function ()
{
var data = this.data;
var total = this.totalData;
for (var i = 0; i < total; i++)
{
data[i].reset(false);
}
this.setCurrentTween(0);
},
/**
* Internal method that advances the TweenChain based on the time values.
*
* @method Phaser.Tweens.TweenChain#update
* @fires Phaser.Tweens.Events#TWEEN_COMPLETE
* @fires Phaser.Tweens.Events#TWEEN_LOOP
* @fires Phaser.Tweens.Events#TWEEN_START
* @since 3.60.0
*
* @param {number} delta - The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
*
* @return {boolean} Returns `true` if this TweenChain has finished and should be removed from the Tween Manager, otherwise returns `false`.
*/
update: function (delta)
{
if (this.isPendingRemove() || this.isDestroyed())
{
return true;
}
else if (this.isFinished() || this.paused)
{
return false;
}
// The TweenChain.timeScale is applied within Tween.update, so doesn't need including here
delta *= this.parent.timeScale;
if (this.isLoopDelayed())
{
this.updateLoopCountdown(delta);
return false;
}
else if (this.isCompleteDelayed())
{
this.updateCompleteDelay(delta);
return false;
}
else if (!this.hasStarted)
{
this.startDelay -= delta;
if (this.startDelay <= 0)
{
this.hasStarted = true;
this.dispatchEvent(Events.TWEEN_START, 'onStart');
// Reset the delta so we always start progress from zero
delta = 0;
}
}
var remove = false;
if (this.isActive() && this.currentTween)
{
if (this.currentTween.update(delta))
{
// This tween has finished playback, so move to the next one
if (this.nextTween())
{
this.nextState();
}
}
// If nextState called onCompleteHandler then it is ready to be removed, unless persist is set to true
remove = this.isPendingRemove();
if (remove && this.persist)
{
this.setFinishedState();
remove = false;
}
}
return remove;
},
/**
* Immediately advances to the next Tween in the chain.
*
* This is typically called internally, but can be used if you need to
* advance playback for some reason.
*
* @method Phaser.Tweens.TweenChain#nextTween
* @since 3.60.0
*
* @return {boolean} `true` if there are no more Tweens in the chain, otherwise `false`.
*/
nextTween: function ()
{
this.currentIndex++;
if (this.currentIndex === this.totalData)
{
return true;
}
else
{
this.setCurrentTween(this.currentIndex);
}
return false;
},
/**
* Sets the current active Tween to the given index, based on its
* entry in the TweenChain data array.
*
* @method Phaser.Tweens.TweenChain#setCurrentTween
* @since 3.60.0
*
* @param {number} index - The index of the Tween to be made current.
*/
setCurrentTween: function (index)
{
this.currentIndex = index;
this.currentTween = this.data[index];
this.currentTween.setActiveState();
},
/**
* Internal method that will emit a TweenChain based Event and invoke the given callback.
*
* @method Phaser.Tweens.TweenChain#dispatchEvent
* @since 3.60.0
*
* @param {Phaser.Types.Tweens.Event} event - The Event to be dispatched.
* @param {Phaser.Types.Tweens.TweenCallbackTypes} [callback] - The name of the callback to be invoked. Can be `null` or `undefined` to skip invocation.
*/
dispatchEvent: function (event, callback)
{
this.emit(event, this);
var handler = this.callbacks[callback];
if (handler)
{
handler.func.apply(this.callbackScope, [ this ].concat(handler.params));
}
},
/**
* Immediately destroys this TweenChain, nulling of all its references.
*
* @method Phaser.Tweens.TweenChain#destroy
* @since 3.60.0
*/
destroy: function ()
{
BaseTween.prototype.destroy.call(this);
this.currentTween = null;
}
});
/**
* Creates a new TweenChain object and adds it to the Tween Manager.
*
* Note: This method will only be available if Tweens have been built into Phaser.
*
* @method Phaser.GameObjects.GameObjectFactory#tweenchain
* @since 3.60.0
*
* @param {Phaser.Types.Tweens.TweenBuilderConfig|object} config - The TweenChain configuration.
*
* @return {Phaser.Tweens.TweenChain} The TweenChain that was created.
*/
GameObjectFactory.register('tweenchain', function (config)
{
return this.scene.sys.tweens.chain(config);
});
/**
* Creates a new TweenChain object and returns it, without adding it to the Tween Manager.
*
* Note: This method will only be available if Tweens have been built into Phaser.
*
* @method Phaser.GameObjects.GameObjectCreator#tweenchain
* @since 3.60.0
*
* @param {Phaser.Types.Tweens.TweenBuilderConfig|object} config - The TweenChain configuration.
*
* @return {Phaser.Tweens.TweenChain} The TweenChain that was created.
*/
GameObjectCreator.register('tweenchain', function (config)
{
return this.scene.sys.tweens.create(config);
});
module.exports = TweenChain;