/** * @author Richard Davey * @copyright 2018 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ var Animation = require('./Animation'); var Class = require('../utils/Class'); var CustomMap = require('../structs/Map'); var EventEmitter = require('eventemitter3'); var GetValue = require('../utils/object/GetValue'); var Pad = require('../utils/string/Pad'); /** * @typedef {object} JSONAnimationManager * * @property {JSONAnimation[]} anims - [description] * @property {number} globalTimeScale - [description] */ /** * @classdesc * The Animation Manager. * * Animations are managed by the global Animation Manager. This is a singleton class that is * responsible for creating and delivering animations and their corresponding data to all Game Objects. * Unlike plugins it is owned by the Game instance, not the Scene. * * Sprites and other Game Objects get the data they need from the AnimationManager. * * @class AnimationManager * @extends Phaser.Events.EventEmitter * @memberOf Phaser.Animations * @constructor * @since 3.0.0 * * @param {Phaser.Game} game - [description] */ var AnimationManager = new Class({ Extends: EventEmitter, initialize: function AnimationManager (game) { EventEmitter.call(this); /** * [description] * * @name Phaser.Animations.AnimationManager#game * @type {Phaser.Game} * @protected * @since 3.0.0 */ this.game = game; /** * [description] * * @name Phaser.Animations.AnimationManager#textureManager * @type {Phaser.Textures.TextureManager} * @protected * @since 3.0.0 */ this.textureManager = null; /** * [description] * * @name Phaser.Animations.AnimationManager#globalTimeScale * @type {number} * @default 1 * @since 3.0.0 */ this.globalTimeScale = 1; /** * [description] * * @name Phaser.Animations.AnimationManager#anims * @type {Phaser.Structs.Map.} * @protected * @since 3.0.0 */ this.anims = new CustomMap(); /** * [description] * * @name Phaser.Animations.AnimationManager#paused * @type {boolean} * @default false * @since 3.0.0 */ this.paused = false; /** * [description] * * @name Phaser.Animations.AnimationManager#name * @type {string} * @since 3.0.0 */ this.name = 'AnimationManager'; game.events.once('boot', this.boot, this); }, /** * [description] * * @method Phaser.Animations.AnimationManager#boot * @since 3.0.0 */ boot: function () { this.textureManager = this.game.textures; this.game.events.once('destroy', this.destroy, this); }, /** * [description] * * @method Phaser.Animations.AnimationManager#add * @fires AddAnimationEvent * @since 3.0.0 * * @param {string} key - [description] * @param {Phaser.Animations.Animation} animation - [description] * * @return {Phaser.Animations.AnimationManager} This Animation Manager. */ add: function (key, animation) { if (this.anims.has(key)) { console.warn('Animation with key', key, 'already exists'); return; } animation.key = key; this.anims.set(key, animation); this.emit('add', key, animation); return this; }, /** * [description] * * @method Phaser.Animations.AnimationManager#create * @fires AddAnimationEvent * @since 3.0.0 * * @param {AnimationConfig} config - [description] * * @return {Phaser.Animations.Animation} The Animation that was created. */ create: function (config) { var key = config.key; if (!key || this.anims.has(key)) { console.warn('Invalid Animation Key, or Key already in use: ' + key); return; } var anim = new Animation(this, key, config); this.anims.set(key, anim); this.emit('add', key, anim); return anim; }, /** * [description] * * @method Phaser.Animations.AnimationManager#fromJSON * @since 3.0.0 * * @param {(string|JSONAnimationManager|JSONAnimation)} data - [description] * @param {boolean} [clearCurrentAnimations=false] - [description] * * @return {Phaser.Animations.Animation[]} An array containing all of the Animation objects that were created as a result of this call. */ fromJSON: function (data, clearCurrentAnimations) { if (clearCurrentAnimations === undefined) { clearCurrentAnimations = false; } if (clearCurrentAnimations) { this.anims.clear(); } // Do we have a String (i.e. from JSON, or an Object?) if (typeof data === 'string') { data = JSON.parse(data); } var output = []; // Array of animations, or a single animation? if (data.hasOwnProperty('anims') && Array.isArray(data.anims)) { for (var i = 0; i < data.anims.length; i++) { output.push(this.create(data.anims[i])); } if (data.hasOwnProperty('globalTimeScale')) { this.globalTimeScale = data.globalTimeScale; } } else if (data.hasOwnProperty('key') && data.type === 'frame') { output.push(this.create(data)); } return output; }, /** * @typedef {object} GenerateFrameNamesConfig * * @property {string} [prefix=''] - [description] * @property {integer} [start=0] - [description] * @property {integer} [end=0] - [description] * @property {string} [suffix=''] - [description] * @property {integer} [zeroPad=0] - [description] * @property {AnimationFrameConfig[]} [outputArray=[]] - [description] * @property {boolean} [frames=false] - [description] */ /** * [description] * * @method Phaser.Animations.AnimationManager#generateFrameNames * @since 3.0.0 * * @param {string} key - [description] * @param {GenerateFrameNamesConfig} [config] - [description] * * @return {AnimationFrameConfig[]} [description] */ generateFrameNames: function (key, config) { var prefix = GetValue(config, 'prefix', ''); var start = GetValue(config, 'start', 0); var end = GetValue(config, 'end', 0); var suffix = GetValue(config, 'suffix', ''); var zeroPad = GetValue(config, 'zeroPad', 0); var out = GetValue(config, 'outputArray', []); var frames = GetValue(config, 'frames', false); var texture = this.textureManager.get(key); if (!texture) { return out; } var diff = (start < end) ? 1 : -1; // Adjust because we use i !== end in the for loop end += diff; var i; var frame; if (!config) { // Use every frame in the atlas? frames = texture.getFrameNames(); for (i = 0; i < frames.length; i++) { out.push({ key: key, frame: frames[i] }); } } else if (Array.isArray(frames)) { // Have they provided their own custom frame sequence array? for (i = 0; i < frames.length; i++) { frame = prefix + Pad(frames[i], zeroPad, '0', 1) + suffix; if (texture.has(frame)) { out.push({ key: key, frame: frame }); } } } else { for (i = start; i !== end; i += diff) { frame = prefix + Pad(i, zeroPad, '0', 1) + suffix; if (texture.has(frame)) { out.push({ key: key, frame: frame }); } } } return out; }, /** * @typedef {object} GenerateFrameNumbersConfig * * @property {integer} [start=0] - [description] * @property {integer} [end=-1] - [description] * @property {boolean} [first=false] - [description] * @property {AnimationFrameConfig[]} [outputArray=[]] - [description] * @property {boolean} [frames=false] - [description] */ /** * [description] * * @method Phaser.Animations.AnimationManager#generateFrameNumbers * @since 3.0.0 * * @param {string} key - [description] * @param {GenerateFrameNumbersConfig} config - [description] * * @return {AnimationFrameConfig[]} [description] */ generateFrameNumbers: function (key, config) { var startFrame = GetValue(config, 'start', 0); var endFrame = GetValue(config, 'end', -1); var firstFrame = GetValue(config, 'first', false); var out = GetValue(config, 'outputArray', []); var frames = GetValue(config, 'frames', false); var texture = this.textureManager.get(key); if (!texture) { return out; } if (firstFrame && texture.has(firstFrame)) { out.push({ key: key, frame: firstFrame }); } var i; // Have they provided their own custom frame sequence array? if (Array.isArray(frames)) { for (i = 0; i < frames.length; i++) { if (texture.has(frames[i])) { out.push({ key: key, frame: frames[i] }); } } } else { // No endFrame then see if we can get it if (endFrame === -1) { endFrame = texture.frameTotal; } for (i = startFrame; i <= endFrame; i++) { if (texture.has(i)) { out.push({ key: key, frame: i }); } } } return out; }, /** * [description] * * @method Phaser.Animations.AnimationManager#get * @since 3.0.0 * * @param {string} key - [description] * * @return {Phaser.Animations.Animation} [description] */ get: function (key) { return this.anims.get(key); }, /** * Load an Animation into a Game Objects Animation Component. * * @method Phaser.Animations.AnimationManager#load * @since 3.0.0 * * @param {Phaser.GameObjects.GameObject} child - [description] * @param {string} key - [description] * @param {(string|integer)} [startFrame] - [description] * * @return {Phaser.GameObjects.GameObject} [description] */ load: function (child, key, startFrame) { var anim = this.get(key); if (anim) { anim.load(child, startFrame); } return child; }, /** * [description] * * @method Phaser.Animations.AnimationManager#pauseAll * @fires PauseAllAnimationEvent * @since 3.0.0 * * @return {Phaser.Animations.AnimationManager} This Animation Manager. */ pauseAll: function () { if (!this.paused) { this.paused = true; this.emit('pauseall'); } return this; }, /** * [description] * * @method Phaser.Animations.AnimationManager#play * @since 3.0.0 * * @param {string} key - [description] * @param {Phaser.GameObjects.GameObject} child - [description] * * @return {Phaser.Animations.AnimationManager} This Animation Manager. */ play: function (key, child) { if (!Array.isArray(child)) { child = [ child ]; } var anim = this.get(key); if (!anim) { return; } for (var i = 0; i < child.length; i++) { child[i].anims.play(key); } return this; }, /** * [description] * * @method Phaser.Animations.AnimationManager#remove * @fires RemoveAnimationEvent * @since 3.0.0 * * @param {string} key - [description] * * @return {Phaser.Animations.Animation} [description] */ remove: function (key) { var anim = this.get(key); if (anim) { this.emit('remove', key, anim); this.anims.delete(key); } return anim; }, /** * [description] * * @method Phaser.Animations.AnimationManager#resumeAll * @fires ResumeAllAnimationEvent * @since 3.0.0 * * @return {Phaser.Animations.AnimationManager} This Animation Manager. */ resumeAll: function () { if (this.paused) { this.paused = false; this.emit('resumeall'); } return this; }, /** * Takes an array of Game Objects that have the Animation Component and then * starts the given animation playing on them, each one offset by the * `stagger` amount given to this method. * * @method Phaser.Animations.AnimationManager#staggerPlay * @since 3.0.0 * * @generic {Phaser.GameObjects.GameObject[]} G - [items,$return] * * @param {string} key - The key of the animation to play on the Game Objects. * @param {Phaser.GameObjects.GameObject[]} children - An array of Game Objects to play the animation on. They must have the Animation Component. * @param {number} [stagger=0] - The amount of time, in milliseconds, to offset each play time by. * * @return {Phaser.Animations.AnimationManager} This Animation Manager. */ staggerPlay: function (key, children, stagger) { if (stagger === undefined) { stagger = 0; } if (!Array.isArray(children)) { children = [ children ]; } var anim = this.get(key); if (!anim) { return; } for (var i = 0; i < children.length; i++) { children[i].anims.delayedPlay(stagger * i, key); } return this; }, /** * [description] * * @method Phaser.Animations.AnimationManager#toJSON * @since 3.0.0 * * @param {string} key - [description] * * @return {JSONAnimationManager} [description] */ toJSON: function (key) { if (key !== undefined && key !== '') { return this.anims.get(key).toJSON(); } else { var output = { anims: [], globalTimeScale: this.globalTimeScale }; this.anims.each(function (animationKey, animation) { output.anims.push(animation.toJSON()); }); return output; } }, /** * [description] * * @method Phaser.Animations.AnimationManager#destroy * @since 3.0.0 */ destroy: function () { this.anims.clear(); this.textureManager = null; this.game = null; } }); module.exports = AnimationManager;