Evolving the Tween engine. TweenMax string syntax now supported.

This commit is contained in:
photonstorm 2014-11-19 21:11:07 +00:00
parent ab114cc4a1
commit 4220382376
5 changed files with 264 additions and 140 deletions

View file

@ -755,6 +755,8 @@ Phaser.Game.prototype = {
this.debug.preUpdate();
}
this.tweens.update(timeStep);
this.world.camera.preUpdate();
this.physics.preUpdate();
this.state.preUpdate(timeStep);
@ -798,7 +800,7 @@ Phaser.Game.prototype = {
// update tweens once every frame along with the render logic (to keep them smooth in slowMotion scenarios)
if (!this._paused && !this.pendingStep)
{
this.tweens.update(elapsedTime);
// this.tweens.update(elapsedTime);
}
if (this.renderType !== Phaser.HEADLESS)

View file

@ -563,3 +563,8 @@ Phaser.Easing = {
};
Phaser.Easing.Default = Phaser.Easing.Linear.None;
Phaser.Easing.Power0 = Phaser.Easing.Linear.None;
Phaser.Easing.Power1 = Phaser.Easing.Quadratic.Out;
Phaser.Easing.Power2 = Phaser.Easing.Cubic.Out;
Phaser.Easing.Power3 = Phaser.Easing.Quartic.Out;
Phaser.Easing.Power4 = Phaser.Easing.Quintic.Out;

View file

@ -9,7 +9,7 @@
*
* @class Phaser.Tween
* @constructor
* @param {object} target - The Target object that will be affected by this tween.
* @param {object} target - The target object, such as a Phaser.Sprite or property like Phaser.Sprite.scale.
* @param {Phaser.Game} game - Current game instance.
* @param {Phaser.TweenManager} manager - The TweenManager responsible for looking after this Tween.
*/
@ -21,70 +21,48 @@ Phaser.Tween = function (target, game, manager) {
this.game = game;
/**
* @property {object} target - Reference to the target object.
* @property {object} target - The target object, such as a Phaser.Sprite or property like Phaser.Sprite.scale.
*/
this.target = target;
/**
* @property {object} parent - Reference to the parent tween if part of a chained tween.
*/
this.parent = null;
// this.parent = null;
/**
* @property {Phaser.TweenManager} manager - Reference to the TweenManager.
* @property {Phaser.TweenManager} manager - Reference to the TweenManager responsible for updating this Tween.
*/
this.manager = manager;
/**
* @property {Array} timeline - An Array of TweenData objects that comprise the different parts of this Tween.
*/
this.timeline = [];
/**
* @property {number} duration - The duration of the tween in ms.
* @readOnly
* @property {boolean} reverse - If set to `true` the current tween will play in reverse. If the tween hasn't yet started this has no effect. If there are child tweens then all child tweens will play in reverse from the current point.
* @default
*/
this.duration = 1000;
this.reverse = false;
/**
* @property {number} percent - A value between 0 and 1 that represents how far through the duration this tween is.
* @readOnly
* @property {number} speed - The speed at which the tweens will run. A value of 1 means it will match the game frame rate. 0.5 will run at half the frame rate. 2 at double the frame rate and so on.
* @default
*/
this.percent = 0;
this.speed = 1;
/**
* @property {number} repeatCounter - If the Tween is set to repeat this contains the current repeat count.
* @property {number} repeatCounter - If the Tween and any child tweens are set to repeat this contains the current repeat count.
*/
this.repeatCounter = 0;
/**
* @property {boolean} yoyo - True if the Tween is set to yoyo, otherwise false.
* @default
*/
this.yoyo = false;
/**
* @property {boolean} inReverse - When a Tween is yoyoing this value holds if it's currently playing forwards (false) or in reverse (true).
* @default
*/
this.inReverse = false;
/**
* @property {number} _delay - Private delay counter.
* @private
* @default
*/
this._delay = 0;
/**
* @property {number} startTime - The time the Tween started or null if it hasn't yet started.
*/
this.startTime = null;
/**
* @property {boolean} _onStartCallbackFired - Private flag.
* @private
* @default
*/
this._onStartCallbackFired = false;
// this._onStartCallbackFired = false;
/**
* @property {function} _onUpdateCallback - An onUpdate callback.
@ -123,16 +101,24 @@ Phaser.Tween = function (target, game, manager) {
/**
* @property {boolean} pendingDelete - True if this Tween is ready to be deleted by the TweenManager.
* @default
* @readOnly
*/
this.pendingDelete = false;
// Move all of these to TweenManager?
/**
* @property {Phaser.Signal} onStart - The onStart event is fired when the Tween begins.
* @property {Phaser.Signal} onStart - The onStart event is fired when the Tween begins. If there is a delay before the tween starts then onStart fires after the delay is finished.
*/
this.onStart = new Phaser.Signal();
/**
* @property {Phaser.Signal} onLoop - The onLoop event is fired if the Tween loops.
* @property {Phaser.Signal} onStart - The onStart event is fired when the Tween begins. If there is a delay before the tween starts then onStart fires after the delay is finished.
*/
this.onChildStart = new Phaser.Signal();
/**
* @property {Phaser.Signal} onLoop - The onLoop event is fired if the Tween loops. If there are chained tweens it fires after all the child tweens have completed.
*/
this.onLoop = new Phaser.Signal();
@ -147,6 +133,11 @@ Phaser.Tween = function (target, game, manager) {
*/
this.isRunning = false;
/**
* @property {number} current - The current Tween child being run.
* @default
* @readOnly
*/
this.current = 0;
};
@ -156,83 +147,83 @@ Phaser.Tween.prototype = {
/**
* Sets this tween to be a `to` tween on the properties given. A `to` tween starts at the current value and tweens to the destination value given.
* For example a Sprite with an `x` coordinate of 100 could be tweened to `x` 200 by giving a properties object of `{ x: 200 }`.
* The ease function allows you define the rate of change. You can pass either a function such as Phaser.Easing.Circular.Out or a string such as "Circ".
* ".easeIn", ".easeOut" and "easeInOut" variants are all supported for all ease types.
*
* @method Phaser.Tween#to
* @param {object} properties - The properties you want to tween, such as `Sprite.x` or `Sound.volume`. Given as a JavaScript object.
* @param {object} properties - An object containing the properties you want to tween., such as `Sprite.x` or `Sound.volume`. Given as a JavaScript object.
* @param {number} [duration=1000] - Duration of this tween in ms.
* @param {function} [ease=null] - Easing function. If not set it will default to Phaser.Easing.Default, which is Phaser.Easing.Linear.None by default but can be over-ridden at will.
* @param {boolean} [autoStart=false] - Whether this tween will start automatically or not.
* @param {number} [delay=0] - Delay before this tween will start, defaults to 0 (no delay). Value given is in ms.
* @param {number} [repeat=0] - Should the tween automatically restart once complete? If you want it to run forever set as -1. This ignores any chained tweens.
* @param {function|string} [ease=null] - Easing function. If not set it will default to Phaser.Easing.Default, which is Phaser.Easing.Linear.None by default but can be over-ridden.
* @param {boolean} [autoStart=false] - Set to `true` to allow this tween to start automatically. Otherwise call Tween.start().
* @param {number} [delay=0] - Delay before this tween will start in milliseconds. Defaults to 0, no delay.
* @param {number} [repeat=0] - Should the tween automatically restart once complete? If you want it to run forever set as -1. This only effects this induvidual tween, not any chained tweens.
* @param {boolean} [yoyo=false] - A tween that yoyos will reverse itself and play backwards automatically. A yoyo'd tween doesn't fire the Tween.onComplete event, so listen for Tween.onLoop instead.
* @return {Phaser.Tween} This Tween object.
*/
to: function (properties, duration, ease, autoStart, delay, repeat, yoyo) {
if (typeof duration === 'undefined') { duration = 1000; }
if (typeof ease === 'undefined') { ease = this.easingFunction; }
if (typeof ease === 'undefined') { ease = Phaser.Easing.Default; }
if (typeof autoStart === 'undefined') { autoStart = false; }
if (typeof delay === 'undefined') { delay = 0; }
if (typeof repeat === 'undefined') { repeat = 0; }
if (typeof yoyo === 'undefined') { yoyo = false; }
if (yoyo && repeat === 0)
if (typeof ease === 'string' && this.manager.easeMap[ease])
{
repeat = 1;
ease = this.manager.easeMap[ease];
}
this.timeline.push(new Phaser.TweenData(this).to(properties, duration, ease, autoStart, delay, repeat, yoyo));
if (autoStart)
{
return this.start();
}
else
{
return this;
this.start();
}
return this;
},
/**
* Sets this tween to be a `from` tween on the properties given. A `from` tween starts at the given value and tweens to the current values.
* For example a Sprite with an `x` coordinate of 100 could be tweened from `x: 200` by giving a properties object of `{ x: 200 }`.
* The ease function allows you define the rate of change. You can pass either a function such as Phaser.Easing.Circular.Out or a string such as "Circ".
* ".easeIn", ".easeOut" and "easeInOut" variants are all supported for all ease types.
*
* @method Phaser.Tween#from
* @param {object} properties - Properties you want to tween from.
* @param {object} properties - An object containing the properties you want to tween., such as `Sprite.x` or `Sound.volume`. Given as a JavaScript object.
* @param {number} [duration=1000] - Duration of this tween in ms.
* @param {function} [ease=null] - Easing function. If not set it will default to Phaser.Easing.Linear.None.
* @param {boolean} [autoStart=false] - Whether this tween will start automatically or not.
* @param {number} [delay=0] - Delay before this tween will start, defaults to 0 (no delay). Value given is in ms.
* @param {number} [repeat=0] - Should the tween automatically restart once complete? If you want it to run forever set as Number.MAX_VALUE. This ignores any chained tweens.
* @param {function|string} [ease=null] - Easing function. If not set it will default to Phaser.Easing.Default, which is Phaser.Easing.Linear.None by default but can be over-ridden.
* @param {boolean} [autoStart=false] - Set to `true` to allow this tween to start automatically. Otherwise call Tween.start().
* @param {number} [delay=0] - Delay before this tween will start in milliseconds. Defaults to 0, no delay.
* @param {number} [repeat=0] - Should the tween automatically restart once complete? If you want it to run forever set as -1. This only effects this induvidual tween, not any chained tweens.
* @param {boolean} [yoyo=false] - A tween that yoyos will reverse itself and play backwards automatically. A yoyo'd tween doesn't fire the Tween.onComplete event, so listen for Tween.onLoop instead.
* @return {Phaser.Tween} This Tween object.
*/
from: function(properties, duration, ease, autoStart, delay, repeat, yoyo) {
if (typeof duration === 'undefined') { duration = 1000; }
if (typeof ease === 'undefined') { ease = this.easingFunction; }
if (typeof ease === 'undefined') { ease = Phaser.Easing.Default; }
if (typeof autoStart === 'undefined') { autoStart = false; }
if (typeof delay === 'undefined') { delay = 0; }
if (typeof repeat === 'undefined') { repeat = 0; }
if (typeof yoyo === 'undefined') { yoyo = false; }
if (yoyo && repeat === 0)
if (typeof ease === 'string' && this.manager.easeMap[ease])
{
repeat = 1;
ease = this.manager.easeMap[ease];
}
this.timeline.push(new Phaser.TweenData(this).from(properties, duration, ease, autoStart, delay, repeat, yoyo));
if (autoStart)
{
return this.start();
}
else
{
return this;
this.start();
}
return this;
},
/**
@ -241,7 +232,7 @@ Phaser.Tween.prototype = {
* If the Tween has a delay set then nothing will start tweening until that delay has expired.
*
* @method Phaser.Tween#start
* @param {number} [index=0] - If this Tween contains chained child tweens you can specify which one to start from. The default is zero, i.e. the first tween created.
* @param {number} [index=0] - If this Tween contains child tweens you can specify which one to start from. The default is zero, i.e. the first tween created.
* @return {Phaser.Tween} This tween. Useful for method chaining.
*/
start: function (index) {

View file

@ -24,19 +24,28 @@ Phaser.TweenData = function (parent) {
this.game = parent.game;
/**
* @property {object} valuesStart - An object containing the values at the start of the tween.
* @property {object} vStart - An object containing the values at the start of the tween.
* @private
*/
this.valuesStart = {};
this.vStart = {};
/**
* @property {object} valuesStartRepeat - Private value object.
* @property {object} vStartCache - Cached starting values.
* @private
*/
this.valuesStartRepeat = {};
this.vStartCache = {};
/**
* @property {object} valuesEnd - An object containing the values at the end of the tween.
* @property {object} vEnd - An object containing the values at the end of the tween.
* @private
*/
this.valuesEnd = {};
this.vEnd = {};
/**
* @property {object} vEnd - Cached ending values.
* @private
*/
this.vEndCache = {};
/**
* @property {number} duration - The duration of the tween in ms.
@ -79,6 +88,11 @@ Phaser.TweenData = function (parent) {
*/
this.delay = 0;
/**
* @property {number} dt - Current time value.
*/
this.dt = 0;
/**
* @property {number} startTime - The time the Tween started or null if it hasn't yet started.
*/
@ -103,8 +117,23 @@ Phaser.TweenData = function (parent) {
* @type {number}
*/
Phaser.TweenData.PENDING = 0;
/**
* @constant
* @type {number}
*/
Phaser.TweenData.RUNNING = 1;
/**
* @constant
* @type {number}
*/
Phaser.TweenData.LOOPED = 2;
/**
* @constant
* @type {number}
*/
Phaser.TweenData.COMPLETE = 3;
Phaser.TweenData.prototype = {
@ -121,11 +150,13 @@ Phaser.TweenData.prototype = {
* @param {number} [delay=0] - Delay before this tween will start, defaults to 0 (no delay). Value given is in ms.
* @param {number} [repeat=0] - Should the tween automatically restart once complete? If you want it to run forever set as -1. This ignores any chained tweens.
* @param {boolean} [yoyo=false] - A tween that yoyos will reverse itself and play backwards automatically. A yoyo'd tween doesn't fire the Tween.onComplete event, so listen for Tween.onLoop instead.
* @return {Phaser.Tween} This Tween object.
* @return {Phaser.TweenData} This Tween object.
*/
to: function (properties, duration, ease, autoStart, delay, repeat, yoyo) {
this.valuesEnd = properties;
console.log('TweenData to', arguments);
this.vEnd = properties;
this.duration = duration;
this.easingFunction = ease;
this.delay = delay;
@ -139,59 +170,87 @@ Phaser.TweenData.prototype = {
},
/**
* Starts the Tween running.
*
* @method Phaser.TweenData#start
* @return {Phaser.TweenData} This Tween object.
*/
start: function () {
this.startTime = this.game.time.time + this.delay;
if (this.delay === 0)
{
this.loadStartValues();
this.loadValues();
}
return this;
},
loadStartValues: function () {
/**
* Loads the values from the target object into this Tween.
*
* @private
* @method Phaser.TweenData#loadValues
* @return {Phaser.TweenData} This Tween object.
*/
loadValues: function () {
this.started = true;
this.time = 0;
this.dt = 0;
this.yoyoCounter = 0;
for (var property in this.valuesEnd)
for (var property in this.vEnd)
{
// Check if an Array was provided as property value
if (Array.isArray(this.valuesEnd[property]))
if (Array.isArray(this.vEnd[property]))
{
if (this.valuesEnd[property].length === 0)
if (this.vEnd[property].length === 0)
{
continue;
}
// Create a local copy of the Array with the start value at the front
this.valuesEnd[property] = [this.parent.target[property]].concat(this.valuesEnd[property]);
this.vEnd[property] = [this.parent.target[property]].concat(this.vEnd[property]);
}
this.valuesStart[property] = this.parent.target[property];
this.vStart[property] = this.parent.target[property] || 0;
if (!Array.isArray(this.valuesStart[property]))
if (!Array.isArray(this.vStart[property]))
{
this.valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings
this.vStart[property] *= 1.0; // Ensures we're using numbers, not strings
}
this.valuesStartRepeat[property] = this.valuesStart[property] || 0;
if (typeof this.vEnd[property] === 'string')
{
// Parses relative end values with start as base (e.g.: +10, -3)
this.vEnd[property] = this.vStart[property] + parseFloat(this.vEnd[property], 10);
}
this.vStartCache[property] = this.vStart[property];
this.vEndCache[property] = this.vEnd[property];
}
console.log('loadStartValues');
return this;
},
update: function (time) {
/**
* Updates this Tween. This is called automatically by Phaser.Tween.
*
* @protected
* @method Phaser.TweenData#update
* @return {number} The current status of this Tween. One of the Phaser.TweenData constants: PENDING, RUNNING, LOOPED or COMPLETE.
*/
update: function () {
if (!this.started)
{
if (this.game.time.time >= this.startTime)
{
this.loadStartValues();
this.loadValues();
}
else
{
@ -199,90 +258,102 @@ Phaser.TweenData.prototype = {
}
}
this.percent = (time - this.startTime) / (this.duration * this.game.time.slowMotion);
if (this.percent > 1)
if (this.parent.reverse)
{
this.percent = 1;
this.dt -= (this.game.time.physicsElapsed * 1000) * this.parent.speed;
this.dt = Math.max(this.dt, 0);
}
else
{
this.dt += (this.game.time.physicsElapsed * 1000) * this.parent.speed;
this.dt = Math.min(this.dt, this.duration);
}
this.percent = this.dt / this.duration;
this.value = this.easingFunction(this.percent);
for (var property in this.valuesEnd)
for (var property in this.vEnd)
{
var start = this.valuesStart[property] || 0;
var end = this.valuesEnd[property];
var start = this.vStart[property];
var end = this.vEnd[property];
if (end instanceof Array)
if (Array.isArray(end))
{
this.parent.target[property] = this.interpolationFunction(end, this.value);
}
else
{
// Parses relative end values with start as base (e.g.: +10, -3)
if (typeof(end) === 'string')
{
end = start + parseFloat(end, 10);
}
// protect against non numeric properties.
if (typeof(end) === 'number')
{
this.parent.target[property] = start + ( end - start ) * this.value;
}
this.parent.target[property] = start + (end - start) * this.value;
}
}
if (this.percent === 1)
if ((!this.parent.reverse && this.percent === 1) || (this.parent.reverse && this.percent === 0))
{
if (this.repeatCounter > 0 || this.repeatCounter === -1)
return this.repeat();
}
return Phaser.TweenData.RUNNING;
},
/**
* Checks if this Tween is meant to repeat or yoyo and handles doing so.
*
* @private
* @method Phaser.TweenData#repeat
* @return {number} Either Phaser.TweenData.LOOPED or Phaser.TweenData.COMPLETE.
*/
repeat: function () {
// If not a yoyo and repeatCounter = 0 then we're done
if (this.yoyo)
{
// We're already in reverse mode, which means the yoyo has finished and there's no repeats, so end
if (this.inReverse && this.repeatCounter === 0)
{
return this.repeat(time);
return Phaser.TweenData.COMPLETE;
}
else
this.inReverse = !this.inReverse;
}
else
{
if (this.repeatCounter === 0)
{
return Phaser.TweenData.COMPLETE;
}
}
if (this.inReverse)
{
// If inReverse we're going from vEnd to vStartCache
for (var property in this.vStartCache)
{
this.vStart[property] = this.vEndCache[property];
this.vEnd[property] = this.vStartCache[property];
}
}
else
{
return Phaser.TweenData.RUNNING;
}
},
complete: function (time) {
// -1 means repeat forever, otherwise decrement the repeatCounter
if (this.repeatCounter > -1)
{
this.repeatCounter--;
}
// Reassign starting values, restart by making startTime = now
for (var property in this.valuesStartRepeat)
{
if (typeof(this.valuesEnd[property]) === 'string')
// If not inReverse we're just repopulating the cache again
for (var property in this.vStartCache)
{
this.valuesStartRepeat[property] = this.valuesStartRepeat[property] + parseFloat(this.valuesEnd[property], 10);
this.vStart[property] = this.vStartCache[property];
this.vEnd[property] = this.vEndCache[property];
}
if (this.yoyo)
// -1 means repeat forever, otherwise decrement the repeatCounter
// We only decrement this counter if the tween isn't doing a yoyo, as that doesn't count towards the repeat total
if (this.repeatCounter > 0)
{
var tmp = this.valuesStartRepeat[property];
this.valuesStartRepeat[property] = this.valuesEnd[property];
this.valuesEnd[property] = tmp;
this.repeatCounter--;
}
this._valuesStart[property] = this.valuesStartRepeat[property];
}
if (this.yoyo)
{
this.inReverse = !this.inReverse;
}
this.startTime = this.game.time.time + this.delay;
this.startTime = time + this._delay;
this.dt = 0;
return Phaser.TweenData.LOOPED;

View file

@ -36,6 +36,61 @@ Phaser.TweenManager = function (game) {
*/
this._add = [];
this.easeMap = {
"Power0": Phaser.Easing.Power0,
"Power1": Phaser.Easing.Power1,
"Power2": Phaser.Easing.Power2,
"Power3": Phaser.Easing.Power3,
"Power4": Phaser.Easing.Power4,
"Linear": Phaser.Easing.Linear.None,
"Quad": Phaser.Easing.Quadratic.Out,
"Cubic": Phaser.Easing.Cubic.Out,
"Quart": Phaser.Easing.Quartic.Out,
"Quint": Phaser.Easing.Quintic.Out,
"Sine": Phaser.Easing.Sinusoidal.Out,
"Expo": Phaser.Easing.Exponential.Out,
"Circ": Phaser.Easing.Circular.Out,
"Elastic": Phaser.Easing.Elastic.Out,
"Back": Phaser.Easing.Back.Out,
"Bounce": Phaser.Easing.Bounce.Out,
"Quad.easeIn": Phaser.Easing.Quadratic.In,
"Cubic.easeIn": Phaser.Easing.Cubic.In,
"Quart.easeIn": Phaser.Easing.Quartic.In,
"Quint.easeIn": Phaser.Easing.Quintic.In,
"Sine.easeIn": Phaser.Easing.Sinusoidal.In,
"Expo.easeIn": Phaser.Easing.Exponential.In,
"Circ.easeIn": Phaser.Easing.Circular.In,
"Elastic.easeIn": Phaser.Easing.Elastic.In,
"Back.easeIn": Phaser.Easing.Back.In,
"Bounce.easeIn": Phaser.Easing.Bounce.In,
"Quad.easeOut": Phaser.Easing.Quadratic.Out,
"Cubic.easeOut": Phaser.Easing.Cubic.Out,
"Quart.easeOut": Phaser.Easing.Quartic.Out,
"Quint.easeOut": Phaser.Easing.Quintic.Out,
"Sine.easeOut": Phaser.Easing.Sinusoidal.Out,
"Expo.easeOut": Phaser.Easing.Exponential.Out,
"Circ.easeOut": Phaser.Easing.Circular.Out,
"Elastic.easeOut": Phaser.Easing.Elastic.Out,
"Back.easeOut": Phaser.Easing.Back.Out,
"Bounce.easeOut": Phaser.Easing.Bounce.Out,
"Quad.easeInOut": Phaser.Easing.Quadratic.InOut,
"Cubic.easeInOut": Phaser.Easing.Cubic.InOut,
"Quart.easeInOut": Phaser.Easing.Quartic.InOut,
"Quint.easeInOut": Phaser.Easing.Quintic.InOut,
"Sine.easeInOut": Phaser.Easing.Sinusoidal.InOut,
"Expo.easeInOut": Phaser.Easing.Exponential.InOut,
"Circ.easeInOut": Phaser.Easing.Circular.InOut,
"Elastic.easeInOut": Phaser.Easing.Elastic.InOut,
"Back.easeInOut": Phaser.Easing.Back.InOut,
"Bounce.easeInOut": Phaser.Easing.Bounce.InOut
};
this.game.onPause.add(this._pauseAll, this);
this.game.onResume.add(this._resumeAll, this);