/
Sprite.js
476 lines (450 loc) · 18.3 KB
/
Sprite.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2013-2023 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var AnimationState = require('../../animations/AnimationState');
var Class = require('../../utils/Class');
var Components = require('../components');
var GameObject = require('../GameObject');
var SpriteRender = require('./SpriteRender');
/**
* @classdesc
* A Sprite Game Object.
*
* A Sprite Game Object is used for the display of both static and animated images in your game.
* Sprites can have input events and physics bodies. They can also be tweened, tinted, scrolled
* and animated.
*
* The main difference between a Sprite and an Image Game Object is that you cannot animate Images.
* As such, Sprites take a fraction longer to process and have a larger API footprint due to the Animation
* Component. If you do not require animation then you can safely use Images to replace Sprites in all cases.
*
* @class Sprite
* @extends Phaser.GameObjects.GameObject
* @memberof Phaser.GameObjects
* @constructor
* @since 3.0.0
*
* @extends Phaser.GameObjects.Components.Alpha
* @extends Phaser.GameObjects.Components.BlendMode
* @extends Phaser.GameObjects.Components.Depth
* @extends Phaser.GameObjects.Components.Flip
* @extends Phaser.GameObjects.Components.GetBounds
* @extends Phaser.GameObjects.Components.Mask
* @extends Phaser.GameObjects.Components.Origin
* @extends Phaser.GameObjects.Components.Pipeline
* @extends Phaser.GameObjects.Components.PostPipeline
* @extends Phaser.GameObjects.Components.ScrollFactor
* @extends Phaser.GameObjects.Components.Size
* @extends Phaser.GameObjects.Components.TextureCrop
* @extends Phaser.GameObjects.Components.Tint
* @extends Phaser.GameObjects.Components.Transform
* @extends Phaser.GameObjects.Components.Visible
*
* @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time.
* @param {number} x - The horizontal position of this Game Object in the world.
* @param {number} y - The vertical position of this Game Object in the world.
* @param {(string|Phaser.Textures.Texture)} texture - The key, or instance of the Texture this Game Object will use to render with, as stored in the Texture Manager.
* @param {(string|number)} [frame] - An optional frame from the Texture this Game Object is rendering with.
*/
var Sprite = new Class({
Extends: GameObject,
Mixins: [
Components.Alpha,
Components.BlendMode,
Components.Depth,
Components.Flip,
Components.GetBounds,
Components.Mask,
Components.Origin,
Components.Pipeline,
Components.PostPipeline,
Components.ScrollFactor,
Components.Size,
Components.TextureCrop,
Components.Tint,
Components.Transform,
Components.Visible,
SpriteRender
],
initialize:
function Sprite (scene, x, y, texture, frame)
{
GameObject.call(this, scene, 'Sprite');
/**
* The internal crop data object, as used by `setCrop` and passed to the `Frame.setCropUVs` method.
*
* @name Phaser.GameObjects.Sprite#_crop
* @type {object}
* @private
* @since 3.11.0
*/
this._crop = this.resetCropObject();
/**
* The Animation State component of this Sprite.
*
* This component provides features to apply animations to this Sprite.
* It is responsible for playing, loading, queuing animations for later playback,
* mixing between animations and setting the current animation frame to this Sprite.
*
* @name Phaser.GameObjects.Sprite#anims
* @type {Phaser.Animations.AnimationState}
* @since 3.0.0
*/
this.anims = new AnimationState(this);
this.setTexture(texture, frame);
this.setPosition(x, y);
this.setSizeToFrame();
this.setOriginFromFrame();
this.initPipeline();
this.initPostPipeline(true);
},
// Overrides Game Object method
addedToScene: function ()
{
this.scene.sys.updateList.add(this);
},
// Overrides Game Object method
removedFromScene: function ()
{
this.scene.sys.updateList.remove(this);
},
/**
* Update this Sprite's animations.
*
* @method Phaser.GameObjects.Sprite#preUpdate
* @protected
* @since 3.0.0
*
* @param {number} time - The current timestamp.
* @param {number} delta - The delta time, in ms, elapsed since the last frame.
*/
preUpdate: function (time, delta)
{
this.anims.update(time, delta);
},
/**
* 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.GameObjects.Sprite#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 an animation is already playing then ignore this call.
*
* @return {this} This Game Object.
*/
play: function (key, ignoreIfPlaying)
{
return this.anims.play(key, ignoreIfPlaying);
},
/**
* 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.GameObjects.Sprite#playReverse
* @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 {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call.
*
* @return {this} This Game Object.
*/
playReverse: function (key, ignoreIfPlaying)
{
return this.anims.playReverse(key, ignoreIfPlaying);
},
/**
* 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.
*
* 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.
*
* Prior to Phaser 3.50 this method was called 'delayedPlay'.
*
* @method Phaser.GameObjects.Sprite#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 {this} This Game Object.
*/
playAfterDelay: function (key, delay)
{
return this.anims.playAfterDelay(key, delay);
},
/**
* 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.
*
* 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.
*
* @method Phaser.GameObjects.Sprite#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 {this} This Game Object.
*/
playAfterRepeat: function (key, repeatCount)
{
return this.anims.playAfterRepeat(key, repeatCount);
},
/**
* Sets an animation, or an array of animations, to be played immediately 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 the `stop` method called directly on it.
*
* An animation set to repeat forever will never enter a completed state.
*
* 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 animation they're playing.
*
* Call this method with no arguments to reset all currently chained animations.
*
* 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.
*
* @method Phaser.GameObjects.Sprite#chain
* @since 3.50.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 {this} This Game Object.
*/
chain: function (key)
{
return this.anims.chain(key);
},
/**
* Immediately stops the current animation from playing and dispatches the `ANIMATION_STOP` events.
*
* If no animation is playing, no event will be dispatched.
*
* If there is another animation queued (via the `chain` method) then it will start playing immediately.
*
* @method Phaser.GameObjects.Sprite#stop
* @fires Phaser.Animations.Events#ANIMATION_STOP
* @since 3.50.0
*
* @return {this} This Game Object.
*/
stop: function ()
{
return this.anims.stop();
},
/**
* 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.GameObjects.Sprite#stopAfterDelay
* @fires Phaser.Animations.Events#ANIMATION_STOP
* @since 3.50.0
*
* @param {number} delay - The number of milliseconds to wait before stopping this animation.
*
* @return {this} This Game Object.
*/
stopAfterDelay: function (delay)
{
return this.anims.stopAfterDelay(delay);
},
/**
* Stops the current animation from playing after the given number of 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.
*
* @method Phaser.GameObjects.Sprite#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 {this} This Game Object.
*/
stopAfterRepeat: function (repeatCount)
{
return this.anims.stopAfterRepeat(repeatCount);
},
/**
* 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.GameObjects.Sprite#stopOnFrame
* @fires Phaser.Animations.Events#ANIMATION_STOP
* @since 3.50.0
*
* @param {Phaser.Animations.AnimationFrame} frame - The frame to check before stopping this animation.
*
* @return {this} This Game Object.
*/
stopOnFrame: function (frame)
{
return this.anims.stopOnFrame(frame);
},
/**
* Build a JSON representation of this Sprite.
*
* @method Phaser.GameObjects.Sprite#toJSON
* @since 3.0.0
*
* @return {Phaser.Types.GameObjects.JSONGameObject} A JSON representation of the Game Object.
*/
toJSON: function ()
{
return Components.ToJSON(this);
},
/**
* Handles the pre-destroy step for the Sprite, which removes the Animation component.
*
* @method Phaser.GameObjects.Sprite#preDestroy
* @private
* @since 3.14.0
*/
preDestroy: function ()
{
this.anims.destroy();
this.anims = undefined;
}
});
module.exports = Sprite;