@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/CHANGELOG.md +19 -1
- package/README.md +52 -17
- package/dist/@melonjs/spine-plugin.d.ts +374 -101
- package/dist/@melonjs/spine-plugin.js +544 -129
- package/package.json +11 -10
- package/src/AssetManager.js +99 -12
- package/src/SpinePlugin.js +24 -0
- package/src/index.js +263 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@melonjs/spine-plugin",
|
|
3
|
-
"version": "1.
|
|
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.
|
|
49
|
+
"melonjs": "^15.12.0"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@esotericsoftware/spine-canvas": "^4.2.
|
|
53
|
-
"@esotericsoftware/spine-core": "^4.2.
|
|
54
|
-
"@esotericsoftware/spine-webgl": "^4.2.
|
|
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.
|
|
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
|
|
63
|
-
"eslint": "^8.
|
|
64
|
-
"eslint-plugin-jsdoc": "^46.
|
|
65
|
-
"rollup": "^3.
|
|
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
|
},
|
package/src/AssetManager.js
CHANGED
|
@@ -1,42 +1,129 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
10
|
-
|
|
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.
|
|
79
|
+
this.loadTextureAtlas(atlas);
|
|
30
80
|
}
|
|
31
81
|
|
|
32
82
|
if (skel.endsWith(".skel")) {
|
|
33
|
-
this.
|
|
83
|
+
this.loadBinary(skel);
|
|
34
84
|
} else {
|
|
35
|
-
this.
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
114
|
+
// use internally when calulcating bounds
|
|
43
115
|
this.boneOffset = new Vector2();
|
|
44
116
|
this.boneSize = new Vector2();
|
|
45
117
|
|
|
46
|
-
|
|
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
|
-
|
|
81
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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.
|
|
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.
|
|
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
|
}
|