@melonjs/spine-plugin 1.5.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js DELETED
@@ -1,459 +0,0 @@
1
- import { Math, Renderable, Vector2d, plugin } from "melonjs";
2
- import * as spineWebGL from "@esotericsoftware/spine-webgl";
3
- import * as spineCanvas from "@esotericsoftware/spine-canvas";
4
- import { Vector2 } from "@esotericsoftware/spine-core";
5
-
6
- import SkeletonRenderer from "./SkeletonRenderer.js";
7
- import { SpinePlugin } from "./SpinePlugin.js";
8
-
9
- export { SpinePlugin } from "./SpinePlugin.js";
10
-
11
- // a temporary array used for skeleton.getBounds();
12
- let tempArray = [];
13
-
14
- /**
15
- * @classdesc
16
- * An renderable object to render Spine animated skeleton.
17
- * @augments Renderable
18
- */
19
- export default class Spine extends Renderable {
20
- runtime;
21
- skeleton;
22
- plugin;
23
- renderer;
24
- animationState;
25
- skeletonRenderer;
26
- root;
27
- boneOffset;
28
- boneSize;
29
- isSpineFlipped = {
30
- x : false,
31
- y : false
32
- };
33
-
34
- /**
35
- * Stores settings and other state for the playback of the current animation (if any).
36
- * @type {TrackEntry}
37
- * @see http://en.esotericsoftware.com/spine-api-reference#TrackEntry
38
- * @see setAnimation
39
- * @default undefined
40
- * @example
41
- * // set a default animation to "run"
42
- * this.setAnimation(0, "run", true);
43
- * ...
44
- * ...
45
- * // pause the animation
46
- * this.currentTrack.timeScale = 0;
47
- * ...
48
- * ...
49
- * // resume the animation
50
- * this.currentTrack.timeScale = 1;
51
- */
52
- currentTrack;
53
-
54
- /**
55
- * @param {number} x - the x coordinates of the Spine object
56
- * @param {number} y - the y coordinates of the Spine object
57
- * @param {object} settings - Configuration parameters for the Spine object
58
- * @param {number} [settings.atlasFile] - the name of the atlasFile to be used to create this spine animation
59
- * @param {number} [settings.jsonFile] - the name of the atlasFile to be used to create this spine animation
60
- * @param {number} [settings.mixTime = 0.2] - the default mix duration to use when no mix duration has been defined between two animations.
61
- * @example
62
- * import * as Spine from '@melonjs/spine-plugin';
63
- * import * as me from 'melonjs';
64
- *
65
- * // prepare/declare assets for the preloader
66
- * const DataManifest = [
67
- * {
68
- * "name": "alien-ess.json",
69
- * "type": "spine",
70
- * "src": "data/spine/alien-ess.json"
71
- * },
72
- * {
73
- * "name": "alien.atlas",
74
- * "type": "spine",
75
- * "src": "data/spine/alien.atlas"
76
- * },
77
- * ]
78
- *
79
- * // create a new Spine Renderable
80
- * let spineAlien = new Spine(100, 100, {atlasFile: "alien.atlas", jsonFile: "alien-ess.json"});
81
- *
82
- * // set default animation
83
- * spineAlien.setAnimation(0, "death", true);
84
- *
85
- * // add it to the game world
86
- * me.game.world.addChild(spineAlien);
87
- */
88
- constructor(x, y, settings) {
89
- super(x, y, settings.width, settings.height);
90
-
91
- // ensure plugin was properly registered
92
- this.plugin = plugin.get(SpinePlugin);
93
- if (typeof this.plugin === "undefined") {
94
- throw "Spine plugin: plugin needs to be registered first using plugin.register";
95
- }
96
- this.renderer = this.plugin.app.renderer;
97
-
98
- if (this.renderer.WebGLVersion >= 1) {
99
- this.runtime = spineWebGL;
100
- } else {
101
- this.runtime = spineCanvas;
102
- }
103
-
104
- this.skeletonRenderer = new SkeletonRenderer(this.runtime);
105
-
106
- // force anchorPoint to 0,0
107
- this.anchorPoint.set(0, 0);
108
-
109
- // displaying order
110
- if (typeof settings.z !== "undefined") {
111
- this.pos.z = settings.z;
112
- }
113
-
114
- // use internally when calulcating bounds
115
- this.boneOffset = new Vector2();
116
- this.boneSize = new Vector2();
117
-
118
- // default mixTime
119
- this.mixTime = typeof settings.mixTime !== "undefined" ? settings.mixTime : 0.2;
120
-
121
-
122
- if (settings.jsonFile) {
123
- this.jsonFile = settings.jsonFile;
124
- this.atlasFile = settings.atlasFile;
125
- this.setSkeleton(this.atlasFile, this.jsonFile);
126
- }
127
- }
128
-
129
- /**
130
- * Whether to enabler the debug mode when rendering the spine object
131
- * @default false
132
- * @type {boolean}
133
- */
134
- get debugRendering() {
135
- return this.skeletonRenderer.debugRendering;
136
- }
137
-
138
- set debugRendering(value) {
139
- this.skeletonRenderer.debugRendering = value;
140
- }
141
-
142
- /**
143
- * set and load the given skeleton atlas and json definition files
144
- * (use this if you did not specify any json or atlas through the constructor)
145
- * @param {number} [atlasFile] - the name of the atlasFile to be used to create this spine animation
146
- * @param {number} [jsonFile] - the name of the atlasFile to be used to create this spine animation
147
- * @example
148
- * // create a new Spine Renderable
149
- * let spineAlien = new Spine(100, 100);
150
- *
151
- * // set the skeleton
152
- * spineAlien.setSkeleton("alien.atlas", "alien-ess.json");
153
- *
154
- * // set default animation
155
- * spineAlien.setAnimation(0, "death", true);
156
- *
157
- * // add it to the game world
158
- * me.game.world.addChild(spineAlien);
159
- */
160
- setSkeleton(atlasFile, jsonFile) {
161
- // Create the texture atlas and skeleton data.
162
- let atlas = this.plugin.assetManager.require(atlasFile);
163
- let atlasLoader = new this.runtime.AtlasAttachmentLoader(atlas);
164
- let skeletonJson = new this.runtime.SkeletonJson(atlasLoader);
165
- let skeletonData = skeletonJson.readSkeletonData(this.plugin.assetManager.require(jsonFile));
166
-
167
- // Instantiate a new skeleton based on the atlas and skeleton data.
168
- this.skeleton = new this.runtime.Skeleton(skeletonData);
169
-
170
- this.setToSetupPose();
171
-
172
- // Setup an animation state with a default mix of 0.2 seconds.
173
- var animationStateData = new this.runtime.AnimationStateData(this.skeleton.data);
174
- animationStateData.defaultMix = this.mixTime;
175
- this.animationState = new this.runtime.AnimationState(animationStateData);
176
-
177
- // get a reference to the root bone
178
- this.root = this.skeleton.getRootBone();
179
- }
180
-
181
- /**
182
- * flip the Spine skeleton on the horizontal axis (around its center)
183
- * @param {boolean} [flip=true] - `true` to flip this Spine object.
184
- * @returns {Spine} Reference to this object for method chaining
185
- */
186
- flipX(flip = true) {
187
- if (this.isSpineFlipped.x !== flip) {
188
- this.isSpineFlipped.x = flip;
189
- this.root.scaleX *= -1;
190
- this.isDirty = true;
191
- }
192
- return this;
193
- }
194
-
195
- /**
196
- * flip the Spine skeleton on the vertical axis (around its center)
197
- * @param {boolean} [flip=true] - `true` to flip this Spine object.
198
- * @returns {Spine} Reference to this object for method chaining
199
- */
200
- flipY(flip = true) {
201
- if (this.isSpineFlipped.y !== flip) {
202
- this.isSpineFlipped.y = flip;
203
- this.root.scaleY *= -1;
204
- this.isDirty = true;
205
- }
206
- return this;
207
- }
208
-
209
- /**
210
- * Rotate this Spine object by the specified angle (in radians).
211
- * @param {number} angle - The angle to rotate (in radians)
212
- * @param {Vector2d|ObservableVector2d} [v] - an optional point to rotate around
213
- * @returns {Spine} Reference to this object for method chaining
214
- */
215
- rotate(angle, v) {
216
- // rotation for rootBone is in degrees (anti-clockwise)
217
- this.skeleton.getRootBone().rotation -= Math.radToDeg(angle) + 90;
218
- // melonJS rotate method takes radians
219
- return super.rotate(angle, v);
220
- }
221
-
222
- /**
223
- * scale the Spine object around his anchor point. Scaling actually applies changes
224
- * to the currentTransform member wich is used by the renderer to scale the object
225
- * when rendering. It does not scale the object itself. For example if the renderable
226
- * is an image, the image.width and image.height properties are unaltered but the currentTransform
227
- * member will be changed.
228
- * @param {number} x - a number representing the abscissa of the scaling vector.
229
- * @param {number} [y=x] - a number representing the ordinate of the scaling vector.
230
- * @returns {Spine} Reference to this object for method chaining
231
- */
232
- scale(x, y = x) {
233
- // untested
234
- return super.scale(x, y);
235
- }
236
-
237
- /**
238
- * update the bounding box for this spine object.
239
- * (this will automatically update the bounds of the entire skeleton animation)
240
- * @param {boolean} [absolute=true] - update the bounds size and position in (world) absolute coordinates
241
- * @returns {Bounds} this shape bounding box Rectangle object
242
- */
243
- updateBounds(absolute = true) {
244
- if (this.isRenderable) {
245
- let bounds = this.getBounds();
246
- let isIdentity = this.autoTransform === true && this.currentTransform.isIdentity();
247
-
248
- bounds.clear();
249
-
250
- if (typeof this.skeleton !== "undefined") {
251
- let rootBone = this.skeleton.getRootBone();
252
- let boneOffset = this.boneOffset;
253
- let boneSize = this.boneSize;
254
-
255
- this.skeleton.getBounds(boneOffset, boneSize, tempArray);
256
-
257
- let minX = boneOffset.x - rootBone.x,
258
- minY = boneOffset.y - rootBone.y;
259
-
260
- bounds.addFrame(
261
- minX,
262
- minY,
263
- minX + boneSize.x,
264
- minY + boneSize.y,
265
- !isIdentity ? this.currentTransform : undefined
266
- );
267
- } else {
268
- bounds.addFrame(
269
- 0,
270
- 0,
271
- this.width,
272
- this.height,
273
- !isIdentity ? this.currentTransform : undefined
274
- );
275
- }
276
-
277
- if (absolute === true) {
278
- var absPos = this.getAbsolutePosition();
279
- //bounds.translate(absPos.x, absPos.y);
280
- bounds.centerOn(absPos.x + bounds.centerX, absPos.y + bounds.centerY);
281
- }
282
- return bounds;
283
-
284
- } else {
285
- // manage the case where updateBounds is called
286
- // before the object being yet properly initialized
287
- return super.updateBounds(absolute);
288
- }
289
- }
290
-
291
- /**
292
- * update function (automatically called by melonJS).
293
- * @param {number} dt - time since the last update in milliseconds.
294
- * @returns {boolean} true if the renderable is dirty
295
- */
296
- update(dt) {
297
- if (typeof this.skeleton !== "undefined") {
298
- let rootBone = this.skeleton.getRootBone();
299
- //let height = this.renderer.getHeight();
300
-
301
- // Update and apply the animation state, update the skeleton's
302
- this.animationState.update(dt / 1000);
303
- this.animationState.apply(this.skeleton);
304
-
305
- // update the root bone position
306
- rootBone.x = this.pos.x;
307
- rootBone.y = this.pos.y;
308
-
309
- // world transforms
310
- this.skeleton.updateWorldTransform();
311
-
312
- // update Bounds
313
- this.updateBounds();
314
-
315
- // world transforms
316
- //this.skeleton.updateWorldTransform();
317
- }
318
- return true;
319
- }
320
-
321
-
322
- /**
323
- * draw this spine object
324
- * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance
325
- * @param {Camera2d} [viewport] - the viewport to (re)draw
326
- */
327
- draw(renderer) {
328
- this.skeletonRenderer.draw(renderer, this.skeleton);
329
- }
330
-
331
- /**
332
- * Sets the current animation for a track, discarding any queued animations.
333
- * @param {number} [track_index] - If the formerly current track entry was never applied to a skeleton, it is replaced (not mixed from). In either case trackEnd determines when the track is cleared.
334
- * @param {number} [index] - the animation index
335
- * @param {boolean} [loop= false] - If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its duration.
336
- * @returns {TrackEntry} A track entry to allow further customization of animation playback. References to the track entry must not be kept after the dispose event occurs.
337
- */
338
- setAnimationByIndex(track_index, index, loop = false) {
339
- if (index < 0 || index >= this.skeleton.data.animations.length) {
340
- return (console.log("Animation Index not found"));
341
- } else {
342
- return this.setAnimation(track_index, this.skeleton.data.animations[index].name, loop);
343
- }
344
- }
345
-
346
- /**
347
- * Sets the current animation for a track, discarding any queued animations.
348
- * @param {number} [track_index] - If the formerly current track entry was never applied to a skeleton, it is replaced (not mixed from). In either case trackEnd determines when the track is cleared.
349
- * @param {string} [name] - the animation name
350
- * @param {boolean} [loop= false] - If true, the animation will repeat. If false it will not, instead its last frame is applied if played beyond its duration.
351
- * @returns {TrackEntry} A track entry to allow further customization of animation playback. References to the track entry must not be kept after the dispose event occurs.
352
- * @example
353
- * // set the current animation
354
- * spineAlien.setAnimation(0, "death", true);
355
- */
356
- setAnimation(track_index, name, loop = false) {
357
- this.currentTrack = this.animationState.setAnimation(track_index, name, loop);
358
- return this.currentTrack;
359
- }
360
-
361
- /**
362
- * return true if the given animation name is the current running animation for the current track.
363
- * @name isCurrentAnimation
364
- * @param {string} name - animation name
365
- * @returns {boolean}
366
- * @example
367
- * if (!this.isCurrentAnimation("death")) {
368
- * // do something funny...
369
- * }
370
- */
371
- isCurrentAnimation(name) {
372
- return typeof this.currentTrack !== "undefined" && this.currentTrack.animation.name === name;
373
- }
374
-
375
- /**
376
- * Adds an animation to be played after the current or last queued animation for a track, and sets the track entry's mixDuration.
377
- * @param {number} [delay=0] - If > 0, sets delay. If <= 0, the delay set is the duration of the previous track entry minus any mix duration plus the specified `delay` (ie the mix ends at (`delay` = 0) or before (`delay` < 0) the previous track entry duration). If the previous entry is looping, its next loop completion is used instead of its duration.
378
- * @return {TrackEntry} A track entry to allow further customization of animation playback. References to the track entry must not be kept after the dispose} event occurs.
379
- */
380
- addAnimationByIndex(track_index, index, loop = false, delay = 0) {
381
- if (index < 0 || index >= this.skeleton.data.animations.length) {
382
- return (console.log("Animation Index not found"));
383
- } else {
384
- return this.addAnimation(track_index, this.skeleton.data.animations[index].name, loop, delay);
385
- }
386
- }
387
-
388
- addAnimationByName(track_index, animationName, loop = false, delay = 0) {
389
- this.animationState.addAnimation(track_index, animationName, loop, delay);
390
- }
391
-
392
- getSpinePosition() {
393
- return new Vector2d(this.pos.x, this.pos.y);
394
- }
395
-
396
- setSpineSize(width, height) {
397
- this.width = width;
398
- this.height = height;
399
- }
400
-
401
- getSpineSize() {
402
- return {
403
- width: this.width,
404
- height: this.height
405
- };
406
- }
407
-
408
- /**
409
- * Set the default mix duration to use when no mix duration has been defined between two animations.
410
- * @param {number} mixTime
411
- */
412
- setDefaultMixTime(mixTime) {
413
- this.animationState.data.defaultMix = this.mixTime = mixTime;
414
- }
415
-
416
- /**
417
- * Sets a mix duration by animation name.
418
- */
419
- setTransitionMixTime(firstAnimation, secondAnimation, mixTime) {
420
- this.animationState.setMix(firstAnimation, secondAnimation, mixTime);
421
- }
422
-
423
- /**
424
- * Sets a skin by name.
425
- * @param {string} skinName
426
- * @example
427
- * // create a new Spine Renderable
428
- * let spineAlien = new Spine(100, 100, {atlasFile: "mix-and-match-pma.atlas", jsonFile: "mix-and-match-pro.json"});
429
- *
430
- * // set default animation
431
- * spineAlien.setAnimation(0, "dance", true);
432
- *
433
- * // set default skin
434
- * spineAlien.setSkinByName("full-skins/girl");
435
- *
436
- * // add it to the game world
437
- * me.game.world.addChild(spineAlien);
438
- */
439
- setSkinByName(skinName) {
440
- this.skeleton.setSkinByName(skinName);
441
- }
442
-
443
- /**
444
- * Reset this slot to the setup pose.
445
- */
446
- setToSetupPose() {
447
- this.skeleton.setToSetupPose();
448
- // Spine uses Y-up, melonJS uses Y-down
449
- this.skeleton.getRootBone().scaleY *= -1;
450
- this.skeleton.updateWorldTransform();
451
- // reset flip flags
452
- this.isSpineFlipped.y = false;
453
- this.isSpineFlipped.x = false;
454
- // reset reference to current track entry
455
- this.currentTrack = undefined;
456
- // mark the object as dirty
457
- this.isDirty = true;
458
- }
459
- }