@melonjs/spine-plugin 1.3.0 → 1.5.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melonjs/spine-plugin",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "melonJS Spine plugin",
5
5
  "homepage": "https://github.com/melonjs/spine-plugin#readme",
6
6
  "type": "module",
@@ -46,23 +46,24 @@
46
46
  "CHANGELOG.md"
47
47
  ],
48
48
  "peerDependencies": {
49
- "melonjs": "^15.9.2"
49
+ "melonjs": "^15.12.0"
50
50
  },
51
51
  "dependencies": {
52
- "@esotericsoftware/spine-canvas": "^4.2.18",
53
- "@esotericsoftware/spine-core": "^4.2.18",
54
- "@esotericsoftware/spine-webgl": "^4.2.18"
52
+ "@esotericsoftware/spine-canvas": "^4.2.23",
53
+ "@esotericsoftware/spine-core": "^4.2.23",
54
+ "@esotericsoftware/spine-webgl": "^4.2.23"
55
55
  },
56
56
  "devDependencies": {
57
- "@babel/eslint-parser": "^7.22.11",
57
+ "@babel/eslint-parser": "^7.22.15",
58
58
  "@babel/plugin-syntax-import-assertions": "^7.22.5",
59
59
  "@rollup/plugin-commonjs": "^25.0.4",
60
+ "@rollup/plugin-json": "^6.0.0",
60
61
  "@rollup/plugin-node-resolve": "^15.2.1",
61
62
  "@rollup/plugin-replace": "^5.0.2",
62
- "del-cli": "^5.0.1",
63
- "eslint": "^8.48.0",
64
- "eslint-plugin-jsdoc": "^46.5.0",
65
- "rollup": "^3.28.1",
63
+ "del-cli": "^5.1.0",
64
+ "eslint": "^8.50.0",
65
+ "eslint-plugin-jsdoc": "^46.8.2",
66
+ "rollup": "^3.29.2",
66
67
  "rollup-plugin-bundle-size": "^1.0.3",
67
68
  "typescript": "^5.2.2"
68
69
  },
@@ -1,42 +1,129 @@
1
- import { event, video } from "melonjs";
1
+ import { utils, loader } from "melonjs";
2
2
  import * as spineWebGL from "@esotericsoftware/spine-webgl";
3
3
  import * as spineCanvas from "@esotericsoftware/spine-canvas";
4
4
 
5
+ /**
6
+ * @classdesc
7
+ * An Asset Manager class to load spine assets
8
+ */
5
9
  export default class AssetManager {
6
10
  asset_manager;
7
11
  pathPrefix;
8
12
 
9
- constructor(pathPrefix = "") {
10
- event.once(event.VIDEO_INIT, this.initAssetManager.bind(this));
13
+ /**
14
+ * @param {CanvasRenderer|WebGLRenderer} renderer - a melonJS renderer instance
15
+ * @param {string} [pathPrefix=""] - a default path prefix for assets location
16
+ */
17
+ constructor(renderer, pathPrefix = "") {
11
18
  this.pathPrefix = pathPrefix;
12
- }
13
-
14
- initAssetManager() {
15
- if (video.renderer.WebGLVersion >= 1) {
16
- this.asset_manager = new spineWebGL.AssetManager(video.renderer.getContext(), this.pathPrefix);
19
+ if (renderer.WebGLVersion >= 1) {
20
+ this.asset_manager = new spineWebGL.AssetManager(renderer.getContext(), this.pathPrefix);
17
21
  } else {
18
22
  // canvas renderer
19
23
  this.asset_manager = new spineCanvas.AssetManager(this.pathPrefix);
20
24
  }
25
+
26
+ // set the spine custom parser
27
+ loader.setParser("spine", (data, onload, onerror) => {
28
+ // decompose data.src for the spine loader
29
+ const ext = utils.file.getExtension(data.src);
30
+ const basename = utils.file.getBasename(data.src);
31
+ const path = utils.file.getPath(data.src);
32
+ const filename = basename + "." + ext;
33
+
34
+ this.setPrefix(path);
35
+
36
+ // load asset
37
+ switch (ext) {
38
+ case "atlas":
39
+ this.loadTextureAtlas(filename, onload, onerror);
40
+ break;
41
+ case "json":
42
+ this.loadText(filename, onload, onerror);
43
+ break;
44
+ case "skel":
45
+ this.loadBinary(filename, onload, onerror);
46
+ break;
47
+ default:
48
+ throw "Spine plugin: unknown extension when preloading spine assets";
49
+ }
50
+
51
+ return 1;
52
+ });
53
+
21
54
  }
22
55
 
56
+ /**
57
+ * set a default path prefix for assets location
58
+ * @see loadAsset
59
+ * @param {string} pathPrefix
60
+ */
23
61
  setPrefix(pathPrefix) {
24
- this.asset_manager.pathPrefix = pathPrefix;
62
+ this.asset_manager.pathPrefix = this.pathPrefix = pathPrefix;
25
63
  }
26
64
 
65
+ /**
66
+ * define all spine assets to be loaded
67
+ * @see setPrefix
68
+ * @see loadAll
69
+ * @param {string} atlas
70
+ * @param {string} skel
71
+ * @example
72
+ * // "manually" load spine assets
73
+ * Spine.assetManager.setPrefix("data/spine/");
74
+ * Spine.assetManager.loadAsset("alien.atlas", "alien-ess.json");
75
+ * await Spine.assetManager.loadAll();
76
+ */
27
77
  loadAsset(atlas, skel) {
28
78
  if (atlas) {
29
- this.asset_manager.loadTextureAtlas(atlas);
79
+ this.loadTextureAtlas(atlas);
30
80
  }
31
81
 
32
82
  if (skel.endsWith(".skel")) {
33
- this.asset_manager.loadBinary(skel);
83
+ this.loadBinary(skel);
34
84
  } else {
35
- this.asset_manager.loadText(skel);
85
+ this.loadText(skel);
36
86
  }
37
87
  }
38
88
 
89
+ /**
90
+ * load the given texture atlas
91
+ * @param {string} atlas
92
+ */
93
+ loadTextureAtlas(atlas, onload, onerror) {
94
+ return this.asset_manager.loadTextureAtlas(atlas, onload, onerror);
95
+ }
96
+
97
+
98
+ /**
99
+ * load the given skeleton .skel file
100
+ * @param {string} skel
101
+ */
102
+ loadBinary(skel, onload, onerror) {
103
+ return this.asset_manager.loadBinary(skel, onload, onerror);
104
+ }
105
+
106
+ /**
107
+ * load the given skeleton binary file
108
+ * @param {string} skel
109
+ */
110
+ loadText(skel, onload, onerror) {
111
+ return this.asset_manager.loadText(skel, onload, onerror);
112
+ }
113
+
114
+ /**
115
+ * load all defined spine assets
116
+ * @see loadAsset
117
+ */
39
118
  loadAll() {
40
119
  return this.asset_manager.loadAll();
41
120
  }
121
+
122
+ /**
123
+ * get the loaded skeleton data
124
+ * @param {string} path
125
+ */
126
+ require(path) {
127
+ return this.asset_manager.require(path);
128
+ }
42
129
  }
@@ -0,0 +1,24 @@
1
+ import { plugin } from "melonjs";
2
+ import { name, version, dependencies, homepage, peerDependencies } from "../package.json";
3
+ import AssetManager from "./AssetManager";
4
+
5
+ /**
6
+ * @classdesc
7
+ * a Spine 4.x plugin implementation for melonJS
8
+ * @augments plugin.BasePlugin
9
+ */
10
+ export class SpinePlugin extends plugin.BasePlugin {
11
+ constructor() {
12
+ // call the super constructor
13
+ super();
14
+
15
+ // minimum melonJS version expected to run this plugin
16
+ this.version = peerDependencies["melonjs"];
17
+
18
+ // hello world
19
+ console.log(`${name} ${version} - spine runtime ${dependencies["@esotericsoftware/spine-core"]} | ${homepage}`);
20
+
21
+ // instantiate the asset manager
22
+ this.assetManager = new AssetManager(this.app.renderer);
23
+ }
24
+ }
package/src/index.js CHANGED
@@ -1,34 +1,106 @@
1
- import { Math, Renderable, Vector2d, video } from "melonjs";
2
-
1
+ import { Math, Renderable, Vector2d, plugin } from "melonjs";
3
2
  import * as spineWebGL from "@esotericsoftware/spine-webgl";
4
3
  import * as spineCanvas from "@esotericsoftware/spine-canvas";
5
4
  import { Vector2 } from "@esotericsoftware/spine-core";
6
5
 
7
- import AssetManager from "./AssetManager.js";
8
6
  import SkeletonRenderer from "./SkeletonRenderer.js";
7
+ import { SpinePlugin } from "./SpinePlugin.js";
8
+
9
+ export { SpinePlugin } from "./SpinePlugin.js";
9
10
 
10
- export let assetManager = new AssetManager();
11
+ // a temporary array used for skeleton.getBounds();
12
+ let tempArray = [];
11
13
 
14
+ /**
15
+ * @classdesc
16
+ * An renderable object to render Spine animated skeleton.
17
+ * @augments Renderable
18
+ */
12
19
  export default class Spine extends Renderable {
13
20
  runtime;
14
21
  skeleton;
22
+ plugin;
23
+ renderer;
15
24
  animationState;
16
25
  skeletonRenderer;
17
- assetManager;
18
26
  root;
19
27
  boneOffset;
20
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;
21
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
+ */
22
88
  constructor(x, y, settings) {
23
89
  super(x, y, settings.width, settings.height);
24
90
 
25
- if (video.renderer.WebGLVersion >= 1) {
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) {
26
99
  this.runtime = spineWebGL;
27
100
  } else {
28
101
  this.runtime = spineCanvas;
29
102
  }
30
103
 
31
- this.assetManager = assetManager.asset_manager;
32
104
  this.skeletonRenderer = new SkeletonRenderer(this.runtime);
33
105
 
34
106
  // force anchorPoint to 0,0
@@ -39,20 +111,26 @@ export default class Spine extends Renderable {
39
111
  this.pos.z = settings.z;
40
112
  }
41
113
 
42
- this.scaleValue = {x: 1, y: 1};
114
+ // use internally when calulcating bounds
43
115
  this.boneOffset = new Vector2();
44
116
  this.boneSize = new Vector2();
45
117
 
46
- this.mixTime = settings.mixTime || 0.2;
118
+ // default mixTime
119
+ this.mixTime = typeof settings.mixTime !== "undefined" ? settings.mixTime : 0.2;
120
+
47
121
 
48
122
  if (settings.jsonFile) {
49
123
  this.jsonFile = settings.jsonFile;
50
124
  this.atlasFile = settings.atlasFile;
51
-
52
125
  this.setSkeleton(this.atlasFile, this.jsonFile);
53
126
  }
54
127
  }
55
128
 
129
+ /**
130
+ * Whether to enabler the debug mode when rendering the spine object
131
+ * @default false
132
+ * @type {boolean}
133
+ */
56
134
  get debugRendering() {
57
135
  return this.skeletonRenderer.debugRendering;
58
136
  }
@@ -61,43 +139,107 @@ export default class Spine extends Renderable {
61
139
  this.skeletonRenderer.debugRendering = value;
62
140
  }
63
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
+ */
64
160
  setSkeleton(atlasFile, jsonFile) {
65
- this.loadSpineAssets(atlasFile, jsonFile);
66
- this.root = this.skeleton.getRootBone();
67
- // Spine uses Y-up, melonJS uses Y-down
68
- this.root.scaleY *= -1;
69
- }
70
-
71
- loadSpineAssets(atlasFile, jsonFile) {
72
161
  // Create the texture atlas and skeleton data.
73
- let atlas = this.assetManager.require(atlasFile);
162
+ let atlas = this.plugin.assetManager.require(atlasFile);
74
163
  let atlasLoader = new this.runtime.AtlasAttachmentLoader(atlas);
75
164
  let skeletonJson = new this.runtime.SkeletonJson(atlasLoader);
76
- let skeletonData = skeletonJson.readSkeletonData(this.assetManager.require(jsonFile));
165
+ let skeletonData = skeletonJson.readSkeletonData(this.plugin.assetManager.require(jsonFile));
77
166
 
78
167
  // Instantiate a new skeleton based on the atlas and skeleton data.
79
168
  this.skeleton = new this.runtime.Skeleton(skeletonData);
80
- this.skeleton.setToSetupPose();
81
- this.skeleton.updateWorldTransform();
169
+
170
+ this.setToSetupPose();
82
171
 
83
172
  // Setup an animation state with a default mix of 0.2 seconds.
84
173
  var animationStateData = new this.runtime.AnimationStateData(this.skeleton.data);
85
174
  animationStateData.defaultMix = this.mixTime;
86
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;
87
207
  }
88
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
+ */
89
215
  rotate(angle, v) {
90
216
  // rotation for rootBone is in degrees (anti-clockwise)
91
217
  this.skeleton.getRootBone().rotation -= Math.radToDeg(angle) + 90;
92
218
  // melonJS rotate method takes radians
93
- super.rotate(angle, v);
219
+ return super.rotate(angle, v);
94
220
  }
95
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
+ */
96
232
  scale(x, y = x) {
97
- this.scaleValue = {x, y};
98
- super.scale(x, y);
233
+ // untested
234
+ return super.scale(x, y);
99
235
  }
100
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
+ */
101
243
  updateBounds(absolute = true) {
102
244
  if (this.isRenderable) {
103
245
  let bounds = this.getBounds();
@@ -110,13 +252,16 @@ export default class Spine extends Renderable {
110
252
  let boneOffset = this.boneOffset;
111
253
  let boneSize = this.boneSize;
112
254
 
113
- this.skeleton.getBounds(boneOffset, boneSize);
255
+ this.skeleton.getBounds(boneOffset, boneSize, tempArray);
256
+
257
+ let minX = boneOffset.x - rootBone.x,
258
+ minY = boneOffset.y - rootBone.y;
114
259
 
115
260
  bounds.addFrame(
116
- boneOffset.x - rootBone.x,
117
- boneOffset.y - rootBone.y,
118
- boneSize.x + boneOffset.x - rootBone.x,
119
- boneSize.y + boneOffset.y - rootBone.y,
261
+ minX,
262
+ minY,
263
+ minX + boneSize.x,
264
+ minY + boneSize.y,
120
265
  !isIdentity ? this.currentTransform : undefined
121
266
  );
122
267
  } else {
@@ -131,6 +276,7 @@ export default class Spine extends Renderable {
131
276
 
132
277
  if (absolute === true) {
133
278
  var absPos = this.getAbsolutePosition();
279
+ //bounds.translate(absPos.x, absPos.y);
134
280
  bounds.centerOn(absPos.x + bounds.centerX, absPos.y + bounds.centerY);
135
281
  }
136
282
  return bounds;
@@ -150,22 +296,24 @@ export default class Spine extends Renderable {
150
296
  update(dt) {
151
297
  if (typeof this.skeleton !== "undefined") {
152
298
  let rootBone = this.skeleton.getRootBone();
153
-
154
- // update the root bone position
155
- if (rootBone.x !== this.pos.x) {
156
- rootBone.x = this.pos.x;
157
- }
158
- if (rootBone.y !== this.pos.y) {
159
- rootBone.y = this.pos.y;
160
- }
299
+ //let height = this.renderer.getHeight();
161
300
 
162
301
  // Update and apply the animation state, update the skeleton's
163
- // world transforms and render the skeleton.
164
302
  this.animationState.update(dt / 1000);
165
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
166
310
  this.skeleton.updateWorldTransform();
167
311
 
312
+ // update Bounds
168
313
  this.updateBounds();
314
+
315
+ // world transforms
316
+ //this.skeleton.updateWorldTransform();
169
317
  }
170
318
  return true;
171
319
  }
@@ -173,9 +321,6 @@ export default class Spine extends Renderable {
173
321
 
174
322
  /**
175
323
  * draw this spine object
176
- * @name draw
177
- * @memberof Spine
178
- * @protected
179
324
  * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance
180
325
  * @param {Camera2d} [viewport] - the viewport to (re)draw
181
326
  */
@@ -183,23 +328,60 @@ export default class Spine extends Renderable {
183
328
  this.skeletonRenderer.draw(renderer, this.skeleton);
184
329
  }
185
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
+ */
186
338
  setAnimationByIndex(track_index, index, loop = false) {
187
339
  if (index < 0 || index >= this.skeleton.data.animations.length) {
188
340
  return (console.log("Animation Index not found"));
189
341
  } else {
190
- this.animationState.setAnimation(track_index, this.skeleton.data.animations[index].name, loop);
342
+ return this.setAnimation(track_index, this.skeleton.data.animations[index].name, loop);
191
343
  }
192
344
  }
193
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
+ */
194
356
  setAnimation(track_index, name, loop = false) {
195
- this.animationState.setAnimation(track_index, name, loop);
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;
196
373
  }
197
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
+ */
198
380
  addAnimationByIndex(track_index, index, loop = false, delay = 0) {
199
381
  if (index < 0 || index >= this.skeleton.data.animations.length) {
200
382
  return (console.log("Animation Index not found"));
201
383
  } else {
202
- this.animationState.addAnimation(track_index, this.skeleton.data.animations[index].name, loop, delay);
384
+ return this.addAnimation(track_index, this.skeleton.data.animations[index].name, loop, delay);
203
385
  }
204
386
  }
205
387
 
@@ -223,19 +405,55 @@ export default class Spine extends Renderable {
223
405
  };
224
406
  }
225
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
+ */
226
412
  setDefaultMixTime(mixTime) {
227
- this.animationState.data.defaultMix = mixTime;
413
+ this.animationState.data.defaultMix = this.mixTime = mixTime;
228
414
  }
229
415
 
416
+ /**
417
+ * Sets a mix duration by animation name.
418
+ */
230
419
  setTransitionMixTime(firstAnimation, secondAnimation, mixTime) {
231
420
  this.animationState.setMix(firstAnimation, secondAnimation, mixTime);
232
421
  }
233
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
+ */
234
439
  setSkinByName(skinName) {
235
440
  this.skeleton.setSkinByName(skinName);
236
441
  }
237
442
 
443
+ /**
444
+ * Reset this slot to the setup pose.
445
+ */
238
446
  setToSetupPose() {
239
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;
240
458
  }
241
459
  }