@eva/spine-base 2.0.1-beta.3 → 2.0.1-beta.31
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/dist/EVA.plugin.spineBase.js +9 -13
- package/dist/EVA.plugin.spineBase.min.js +1 -1
- package/dist/spine-base.cjs.js +195 -14
- package/dist/spine-base.cjs.prod.js +2 -2
- package/dist/spine-base.d.ts +173 -0
- package/dist/spine-base.esm.js +195 -14
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
2
2
|
globalThis.EVA = globalThis.EVA || {};
|
|
3
3
|
globalThis.EVA.plugin = globalThis.EVA.plugin || {};
|
|
4
|
-
var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer
|
|
4
|
+
var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer) {
|
|
5
5
|
'use strict';
|
|
6
6
|
function __decorate(decorators, target, key, desc) {
|
|
7
7
|
var c = arguments.length,
|
|
@@ -61,6 +61,7 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
61
61
|
this.scale = 1;
|
|
62
62
|
this.animationName = '';
|
|
63
63
|
this.autoPlay = true;
|
|
64
|
+
this.keepResource = false;
|
|
64
65
|
this.waitExecuteInfos = [];
|
|
65
66
|
}
|
|
66
67
|
set armature(val) {
|
|
@@ -176,6 +177,7 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
176
177
|
__decorate([type('number')], Spine.prototype, "scale", void 0);
|
|
177
178
|
__decorate([type('string')], Spine.prototype, "animationName", void 0);
|
|
178
179
|
__decorate([type('boolean')], Spine.prototype, "autoPlay", void 0);
|
|
180
|
+
__decorate([type('boolean')], Spine.prototype, "keepResource", void 0);
|
|
179
181
|
let dataMap = {};
|
|
180
182
|
function createSpineData(name, data, scale, pixiSpine) {
|
|
181
183
|
const skeletonAsset = data.ske;
|
|
@@ -215,14 +217,6 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
215
217
|
data.ref--;
|
|
216
218
|
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
217
219
|
if (data.ref <= 0) {
|
|
218
|
-
yield pixi_js.Assets.unload([res.src.image.url, res.src.atlas.url, res.src.ske.url]);
|
|
219
|
-
const resolver = pixi_js.Assets.resolver;
|
|
220
|
-
delete resolver._assetMap[res.src.image.url];
|
|
221
|
-
delete resolver._assetMap[res.src.atlas.url];
|
|
222
|
-
delete resolver._assetMap[res.src.ske.url];
|
|
223
|
-
delete resolver._resolverHash[res.src.image.url];
|
|
224
|
-
delete resolver._resolverHash[res.src.atlas.url];
|
|
225
|
-
delete resolver._resolverHash[res.src.ske.url];
|
|
226
220
|
eva_js.resource.destroy(resourceName);
|
|
227
221
|
delete dataMap[resourceName];
|
|
228
222
|
}
|
|
@@ -396,9 +390,11 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
396
390
|
component.armature.destroy({
|
|
397
391
|
children: true
|
|
398
392
|
});
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
393
|
+
if (!component.keepResource) {
|
|
394
|
+
const res = yield eva_js.resource.getResource(component.lastResource);
|
|
395
|
+
((_d = (_c = res.data) === null || _c === void 0 ? void 0 : _c.image) === null || _d === void 0 ? void 0 : _d.src) || ((_f = (_e = res.data) === null || _e === void 0 ? void 0 : _e.image) === null || _f === void 0 ? void 0 : _f.label);
|
|
396
|
+
releaseSpineData(res);
|
|
397
|
+
}
|
|
402
398
|
}
|
|
403
399
|
component.armature = null;
|
|
404
400
|
delete this.armatures[changed.gameObject.id];
|
|
@@ -418,5 +414,5 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
418
414
|
value: true
|
|
419
415
|
});
|
|
420
416
|
return exports;
|
|
421
|
-
}({}, EVA, EVA.plugin.renderer
|
|
417
|
+
}({}, EVA, EVA.plugin.renderer);
|
|
422
418
|
globalThis.EVA.plugin.spineBase = globalThis.EVA.plugin.spineBase || _EVA_IIFE_spineBase;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function _extends(){return _extends=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var
|
|
1
|
+
function _extends(){return _extends=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var a in n)({}).hasOwnProperty.call(n,a)&&(e[a]=n[a])}return e},_extends.apply(null,arguments)}globalThis.EVA=globalThis.EVA||{},globalThis.EVA.plugin=globalThis.EVA.plugin||{};var _EVA_IIFE_spineBase=function(e,t,n){"use strict";function a(e,t,n,a){var i,r=arguments.length,o=r<3?t:null===a?a=Object.getOwnPropertyDescriptor(t,n):a;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,n,a);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(r<3?i(o):r>3?i(t,n,o):i(t,n))||o);return r>3&&o&&Object.defineProperty(t,n,o),o}function i(e,t,n,a){return new(n||(n=Promise))((function(i,r){function o(e){try{c(a.next(e))}catch(e){r(e)}}function s(e){try{c(a.throw(e))}catch(e){r(e)}}function c(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(o,s)}c((a=a.apply(e,t||[])).next())}))}function r(e){return function(t,n){var a=function(e,t){return e.constructor.IDEProps||(e.constructor.IDEProps={}),e.constructor.IDEProps[t]||(e.constructor.IDEProps[t]={}),e.constructor.IDEProps[t]}(t,n);a.key=n,a.type=e}}class o extends t.Component{constructor(){super(...arguments),this.resource="",this.scale=1,this.animationName="",this.autoPlay=!0,this.keepResource=!1,this.waitExecuteInfos=[]}set armature(e){if(this._armature=e,e){this.autoPlay&&this.play(this.animationName);for(const e of this.waitExecuteInfos)if(e.playType){const{name:t,loop:n,track:a}=e;this.play(t,n,a)}else this.stop(e.track);this.waitExecuteInfos=[]}}get armature(){return this._armature}init(e){e&&_extends(this,e)}onDestroy(){this.destroied=!0}play(e,t,n){try{const a=null!=t?t:this.autoPlay;e&&(this.animationName=e),this.armature?(void 0===n&&(n=0),this.armature.state.setAnimation(n,this.animationName,a)):this.waitExecuteInfos.push({playType:!0,name:e,loop:a,track:n})}catch(e){console.log(e)}}stop(e){this.armature?(void 0===e&&(e=0),this.armature.state.setEmptyAnimation(e,0)):this.waitExecuteInfos.push({playType:!1,track:e})}addAnimation(e,t,n,a){try{this.armature&&(void 0===a&&(a=0),this.armature.state.addAnimation(a,e,n,t))}catch(e){console.log(e)}}setMix(e,t,n){this.armature&&this.armature.state.data.setMix(e,t,n)}getAnim(e=0){try{if(this.armature)return this.armature.state.tracks[e].animation.name}catch(e){console.log(e)}}setDefaultMix(e){this.armature&&(this.armature.state.data.defaultMix=e)}setAttachment(e,t){this.armature&&this.armature.skeleton.setAttachment(e,t)}getBone(e){if(this.armature)return this.armature.skeleton.findBone(e)}}o.componentName="Spine",a([r("string")],o.prototype,"resource",void 0),a([r("number")],o.prototype,"scale",void 0),a([r("string")],o.prototype,"animationName",void 0),a([r("boolean")],o.prototype,"autoPlay",void 0),a([r("boolean")],o.prototype,"keepResource",void 0);let s={};function c(e,t,n){return i(this,void 0,void 0,(function*(){let a=s[e.name];if(!a)if(e.complete)a=function(e,t,n,a){const i=t.ske,r=t.atlas,o=new a.AtlasAttachmentLoader(r),c=i instanceof Uint8Array?new a.SkeletonBinary(o):new a.SkeletonJson(o);c.scale=n||1;const u={spineData:c.readSkeletonData(i),ref:0,imageSrc:t.image.label};return s[e]=u,u}(e.name,e.data,t,n);else if(!a)return;return a.ref++,a.spineData}))}let u=class extends n.Renderer{constructor(){super(...arguments),this.armatures={}}init({pixiSpine:e}){this.renderSystem=this.game.getSystem(n.RendererSystem),this.renderSystem.rendererManager.register(this),this.pixiSpine=e,this.game.canvas.addEventListener("webglcontextrestored",(()=>{const e=this.game.gameObjects;let n=[];for(let a in this.armatures){const i=+a;for(let a=0;a<e.length;++a){let r=e[a];if(r.id===i){let e=r.getComponent(o);e&&(this.remove({type:t.OBSERVER_TYPE.REMOVE,gameObject:r,component:e,componentName:o.componentName}),n.push({type:t.OBSERVER_TYPE.ADD,gameObject:r,component:e,componentName:o.componentName}));break}}}setTimeout((()=>{n.forEach((e=>{this.add(e)}))}),1e3)}),!1)}update(e){for(let t in this.armatures)this.armatures[t].update(.001*e.deltaTime);super.update()}componentChanged(e){return i(this,void 0,void 0,(function*(){if("Spine"===e.componentName)if(e.type===t.OBSERVER_TYPE.ADD)this.add(e);else if(e.type===t.OBSERVER_TYPE.CHANGE){if("resource"===e.prop.prop[0])this.change(e)}else e.type===t.OBSERVER_TYPE.REMOVE&&this.remove(e)}))}add(e,n){var a,r;return i(this,void 0,void 0,(function*(){const i=e.component;clearTimeout(i.addHandler);const o=e.gameObject.id,s=this.increaseAsyncId(o),u=yield t.resource.getResource(i.resource);if(!this.validateAsyncId(o,s))return;const m=yield c(u,i.scale,this.pixiSpine);if(!this.validateAsyncId(o,s))return;if(!m)return void(i.addHandler=setTimeout((()=>{i.destroied||(void 0===n&&(n=20),--n>0?this.add(e,n):console.log("retry exceed max times",i.resource))}),1e3));this.remove(e);const d=null===(r=null===(a=this.renderSystem)||void 0===a?void 0:a.containerManager)||void 0===r?void 0:r.getContainer(e.gameObject.id);if(!d)return;i.lastResource=i.resource;const l=new this.pixiSpine.Spine({skeletonData:m,autoUpdate:!1});if(this.armatures[e.gameObject.id]=l,e.gameObject&&e.gameObject.transform){const t=e.gameObject.transform;l.x=t.size.width*t.origin.x,l.y=t.size.height*t.origin.y}d.addChildAt(l,0),l.update(),i.armature=l,i.emit("loaded",{resource:i.resource}),l.state.addListener({start:(e,t)=>{i.emit("start",{track:e,name:e.animation.name})},complete:(e,t)=>{i.emit("complete",{track:e,name:e.animation.name})},interrupt:(e,t)=>{i.emit("interrupt",{track:e,name:e.animation.name})},end:(e,t)=>{i.emit("end",{track:e,name:e.animation.name})},event:(e,t)=>{i.emit("event",e,t)}})}))}change(e){this.remove(e),this.add(e)}remove(e){var n,a,r,o,c,u;return i(this,void 0,void 0,(function*(){this.increaseAsyncId(e.gameObject.id);const m=e.component;clearTimeout(m.addHandler);const d=this.armatures[e.gameObject.id],l=null===(a=null===(n=this.renderSystem)||void 0===n?void 0:n.containerManager)||void 0===a?void 0:a.getContainer(e.gameObject.id);if(l&&d&&l.removeChild(d),m.armature&&(m.armature.destroy({children:!0}),!m.keepResource)){const e=yield t.resource.getResource(m.lastResource);(null===(o=null===(r=e.data)||void 0===r?void 0:r.image)||void 0===o?void 0:o.src)||null===(u=null===(c=e.data)||void 0===c?void 0:c.image)||void 0===u||u.label,function(e){const n=e.name,a=s[n];a&&(a.ref--,setTimeout((()=>i(this,void 0,void 0,(function*(){a.ref<=0&&(t.resource.destroy(n),delete s[n])}))),100))}(e)}m.armature=null,delete this.armatures[e.gameObject.id],e.type,t.OBSERVER_TYPE.CHANGE}))}};u.systemName="SpineSystem",u=a([t.decorators.componentObserver({Spine:["resource"]})],u);var m=u;return t.resource.registerResourceType("SPINE"),e.Spine=o,e.SpineSystem=m,Object.defineProperty(e,"__esModule",{value:!0}),e}({},EVA,EVA.plugin.renderer);globalThis.EVA.plugin.spineBase=globalThis.EVA.plugin.spineBase||_EVA_IIFE_spineBase;
|
package/dist/spine-base.cjs.js
CHANGED
|
@@ -5,7 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var eva_js = require('@eva/eva.js');
|
|
6
6
|
var pluginRenderer = require('@eva/plugin-renderer');
|
|
7
7
|
var inspectorDecorator = require('@eva/inspector-decorator');
|
|
8
|
-
var pixi_js = require('pixi.js');
|
|
9
8
|
|
|
10
9
|
/*! *****************************************************************************
|
|
11
10
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
@@ -38,15 +37,80 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
38
37
|
});
|
|
39
38
|
}
|
|
40
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Spine 骨骼动画组件
|
|
42
|
+
*
|
|
43
|
+
* Spine 组件用于播放 Esoteric Software 的 Spine 骨骼动画。
|
|
44
|
+
* 支持骨骼动画播放控制、动画混合、附件替换等高级功能,
|
|
45
|
+
* 适用于角色动画、复杂特效等需要骨骼动画的场景。
|
|
46
|
+
*
|
|
47
|
+
* 主要功能:
|
|
48
|
+
* - 骨骼动画播放和控制
|
|
49
|
+
* - 动画轨道管理(多动画并行)
|
|
50
|
+
* - 动画混合过渡
|
|
51
|
+
* - 骨骼和附件访问
|
|
52
|
+
* - 支持 Spine 3.6 和 3.8 版本
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* // 创建 Spine 动画
|
|
57
|
+
* const character = new GameObject('character');
|
|
58
|
+
* const spine = new Spine({
|
|
59
|
+
* resource: 'heroSpine', // Spine 资源
|
|
60
|
+
* animationName: 'idle', // 默认动画
|
|
61
|
+
* autoPlay: true, // 自动播放
|
|
62
|
+
* scale: 0.5 // 缩放比例
|
|
63
|
+
* });
|
|
64
|
+
* character.addComponent(spine);
|
|
65
|
+
*
|
|
66
|
+
* // 播放动画
|
|
67
|
+
* spine.play('walk', true); // 循环播放 walk 动画
|
|
68
|
+
*
|
|
69
|
+
* // 停止动画
|
|
70
|
+
* spine.stop();
|
|
71
|
+
*
|
|
72
|
+
* // 动画混合
|
|
73
|
+
* spine.setMix('idle', 'walk', 0.3); // 设置过渡时间
|
|
74
|
+
* spine.play('walk');
|
|
75
|
+
*
|
|
76
|
+
* // 添加动画队列
|
|
77
|
+
* spine.play('attack', false); // 播放攻击动画
|
|
78
|
+
* spine.addAnimation('idle', 0, true); // 攻击完成后回到 idle
|
|
79
|
+
*
|
|
80
|
+
* // 替换附件(换装)
|
|
81
|
+
* spine.setAttachment('weapon', 'sword'); // 将武器槽替换为剑
|
|
82
|
+
*
|
|
83
|
+
* // 访问骨骼
|
|
84
|
+
* const headBone = spine.getBone('head');
|
|
85
|
+
* if (headBone) {
|
|
86
|
+
* headBone.rotation = 15; // 旋转头部
|
|
87
|
+
* }
|
|
88
|
+
*
|
|
89
|
+
* // 多轨道动画
|
|
90
|
+
* spine.play('walk', true, 0); // 轨道0:身体动画
|
|
91
|
+
* spine.play('shoot', false, 1); // 轨道1:上半身动画
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
41
94
|
class Spine extends eva_js.Component {
|
|
42
95
|
constructor() {
|
|
43
96
|
super(...arguments);
|
|
97
|
+
/** Spine 资源名称 */
|
|
44
98
|
this.resource = '';
|
|
99
|
+
/** 动画缩放比例 */
|
|
45
100
|
this.scale = 1;
|
|
101
|
+
/** 当前播放的动画名称 */
|
|
46
102
|
this.animationName = '';
|
|
103
|
+
/** 是否自动播放动画 */
|
|
47
104
|
this.autoPlay = true;
|
|
105
|
+
/** 是否保留资源(销毁时不释放) */
|
|
106
|
+
this.keepResource = false;
|
|
107
|
+
/** 等待执行的动画操作队列 */
|
|
48
108
|
this.waitExecuteInfos = [];
|
|
49
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* 设置骨架实例
|
|
112
|
+
* 当骨架加载完成后自动执行等待队列中的动画操作
|
|
113
|
+
*/
|
|
50
114
|
set armature(val) {
|
|
51
115
|
this._armature = val;
|
|
52
116
|
if (!val)
|
|
@@ -65,17 +129,36 @@ class Spine extends eva_js.Component {
|
|
|
65
129
|
}
|
|
66
130
|
this.waitExecuteInfos = [];
|
|
67
131
|
}
|
|
132
|
+
/** 获取骨架实例 */
|
|
68
133
|
get armature() {
|
|
69
134
|
return this._armature;
|
|
70
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* 初始化组件
|
|
138
|
+
* @param obj - 初始化参数
|
|
139
|
+
* @param obj.resource - Spine 资源名称
|
|
140
|
+
* @param obj.animationName - 默认动画名称
|
|
141
|
+
* @param obj.scale - 缩放比例
|
|
142
|
+
* @param obj.autoPlay - 是否自动播放
|
|
143
|
+
*/
|
|
71
144
|
init(obj) {
|
|
72
145
|
if (!obj)
|
|
73
146
|
return;
|
|
74
147
|
Object.assign(this, obj);
|
|
75
148
|
}
|
|
149
|
+
/** 组件销毁时调用 */
|
|
76
150
|
onDestroy() {
|
|
77
151
|
this.destroied = true;
|
|
78
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* 播放指定动画
|
|
155
|
+
*
|
|
156
|
+
* 如果骨架尚未加载完成,动画操作将被加入等待队列。
|
|
157
|
+
*
|
|
158
|
+
* @param name - 动画名称,不指定则使用 animationName 属性
|
|
159
|
+
* @param loopAnimation - 是否循环播放,默认跟随 autoPlay 属性
|
|
160
|
+
* @param track - 动画轨道编号,默认为 0
|
|
161
|
+
*/
|
|
79
162
|
play(name, loopAnimation, track) {
|
|
80
163
|
try {
|
|
81
164
|
const loop = loopAnimation !== null && loopAnimation !== void 0 ? loopAnimation : this.autoPlay;
|
|
@@ -85,6 +168,12 @@ class Spine extends eva_js.Component {
|
|
|
85
168
|
this.waitExecuteInfos.push({
|
|
86
169
|
playType: true,
|
|
87
170
|
name,
|
|
171
|
+
/**
|
|
172
|
+
* 在 v1.2.2 之前,Spine 动画的 autoPlay 为 true,动画会循环播放 https://github.com/eva-engine/eva.js/pull/164/files#diff-46e9ae36c04e7a0abedc1e14fd9d1c4e81d8386e9bb851f85971ccdba8957804L131
|
|
173
|
+
* 在 v1.2.2 之前,Spine 动画在每加载完( armature 设置之前)调用 play 是不生效的, 在 v1.2.2 [#164](https://github.com/eva-engine/eva.js/pull/164) 解决了这个问题
|
|
174
|
+
* 解决了不生效的问题以后,加载完成之前调用 play 默认循环是false,导致 autoPlay 下本来循环动画不循环了,和之前表现不一致
|
|
175
|
+
* 为了解决这个问题,在 autoPlay 的情况下,未加载完之前调用 play ,默认循环播放,除非设置不循环参数
|
|
176
|
+
*/
|
|
88
177
|
loop,
|
|
89
178
|
track,
|
|
90
179
|
});
|
|
@@ -100,6 +189,13 @@ class Spine extends eva_js.Component {
|
|
|
100
189
|
console.log(e);
|
|
101
190
|
}
|
|
102
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* 停止指定轨道的动画
|
|
194
|
+
*
|
|
195
|
+
* 如果骨架尚未加载完成,停止操作将被加入等待队列。
|
|
196
|
+
*
|
|
197
|
+
* @param track - 动画轨道编号,默认为 0
|
|
198
|
+
*/
|
|
103
199
|
stop(track) {
|
|
104
200
|
if (!this.armature) {
|
|
105
201
|
this.waitExecuteInfos.push({
|
|
@@ -113,6 +209,16 @@ class Spine extends eva_js.Component {
|
|
|
113
209
|
}
|
|
114
210
|
this.armature.state.setEmptyAnimation(track, 0);
|
|
115
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* 在当前动画之后添加新动画到队列
|
|
214
|
+
*
|
|
215
|
+
* 用于创建动画序列,当前动画播放完毕后自动播放下一个动画。
|
|
216
|
+
*
|
|
217
|
+
* @param name - 动画名称
|
|
218
|
+
* @param delay - 延迟时间(秒)
|
|
219
|
+
* @param loop - 是否循环播放
|
|
220
|
+
* @param track - 动画轨道编号,默认为 0
|
|
221
|
+
*/
|
|
116
222
|
addAnimation(name, delay, loop, track) {
|
|
117
223
|
try {
|
|
118
224
|
if (!this.armature) {
|
|
@@ -128,12 +234,27 @@ class Spine extends eva_js.Component {
|
|
|
128
234
|
console.log(e);
|
|
129
235
|
}
|
|
130
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* 设置两个动画之间的混合过渡时间
|
|
239
|
+
*
|
|
240
|
+
* 当从一个动画切换到另一个动画时,会在指定时间内进行平滑过渡。
|
|
241
|
+
*
|
|
242
|
+
* @param from - 起始动画名称
|
|
243
|
+
* @param to - 目标动画名称
|
|
244
|
+
* @param duration - 过渡时长(秒)
|
|
245
|
+
*/
|
|
131
246
|
setMix(from, to, duration) {
|
|
132
247
|
if (!this.armature) ;
|
|
133
248
|
else {
|
|
134
249
|
this.armature.state.data.setMix(from, to, duration);
|
|
135
250
|
}
|
|
136
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* 获取指定轨道当前播放的动画名称
|
|
254
|
+
*
|
|
255
|
+
* @param track - 动画轨道编号,默认为 0
|
|
256
|
+
* @returns 动画名称,如果未找到则返回 undefined
|
|
257
|
+
*/
|
|
137
258
|
getAnim(track = 0) {
|
|
138
259
|
try {
|
|
139
260
|
if (!this.armature) {
|
|
@@ -146,18 +267,41 @@ class Spine extends eva_js.Component {
|
|
|
146
267
|
console.log(e);
|
|
147
268
|
}
|
|
148
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* 设置默认的动画混合时间
|
|
272
|
+
*
|
|
273
|
+
* 当没有为特定动画对指定混合时间时,将使用此默认值。
|
|
274
|
+
*
|
|
275
|
+
* @param duration - 默认混合时长(秒)
|
|
276
|
+
*/
|
|
149
277
|
setDefaultMix(duration) {
|
|
150
278
|
if (!this.armature) ;
|
|
151
279
|
else {
|
|
152
280
|
this.armature.state.data.defaultMix = duration;
|
|
153
281
|
}
|
|
154
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* 替换指定插槽的附件
|
|
285
|
+
*
|
|
286
|
+
* 用于换装、武器切换等场景。
|
|
287
|
+
*
|
|
288
|
+
* @param slotName - 插槽名称
|
|
289
|
+
* @param attachmentName - 附件名称
|
|
290
|
+
*/
|
|
155
291
|
setAttachment(slotName, attachmentName) {
|
|
156
292
|
if (!this.armature) {
|
|
157
293
|
return;
|
|
158
294
|
}
|
|
159
295
|
this.armature.skeleton.setAttachment(slotName, attachmentName);
|
|
160
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* 获取指定名称的骨骼
|
|
299
|
+
*
|
|
300
|
+
* 可用于直接操作骨骼的位置、旋转、缩放等属性。
|
|
301
|
+
*
|
|
302
|
+
* @param boneName - 骨骼名称
|
|
303
|
+
* @returns 骨骼对象,如果未找到则返回 undefined
|
|
304
|
+
*/
|
|
161
305
|
getBone(boneName) {
|
|
162
306
|
if (!this.armature) {
|
|
163
307
|
return;
|
|
@@ -165,6 +309,7 @@ class Spine extends eva_js.Component {
|
|
|
165
309
|
return this.armature.skeleton.findBone(boneName);
|
|
166
310
|
}
|
|
167
311
|
}
|
|
312
|
+
/** 组件名称 */
|
|
168
313
|
Spine.componentName = 'Spine';
|
|
169
314
|
__decorate([
|
|
170
315
|
inspectorDecorator.type('string')
|
|
@@ -177,7 +322,10 @@ __decorate([
|
|
|
177
322
|
], Spine.prototype, "animationName", void 0);
|
|
178
323
|
__decorate([
|
|
179
324
|
inspectorDecorator.type('boolean')
|
|
180
|
-
], Spine.prototype, "autoPlay", void 0);
|
|
325
|
+
], Spine.prototype, "autoPlay", void 0);
|
|
326
|
+
__decorate([
|
|
327
|
+
inspectorDecorator.type('boolean')
|
|
328
|
+
], Spine.prototype, "keepResource", void 0);
|
|
181
329
|
|
|
182
330
|
let dataMap = {};
|
|
183
331
|
function createSpineData(name, data, scale, pixiSpine) {
|
|
@@ -217,14 +365,6 @@ function releaseSpineData(res, _imageSrc) {
|
|
|
217
365
|
data.ref--;
|
|
218
366
|
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
219
367
|
if (data.ref <= 0) {
|
|
220
|
-
yield pixi_js.Assets.unload([res.src.image.url, res.src.atlas.url, res.src.ske.url]);
|
|
221
|
-
const resolver = pixi_js.Assets.resolver;
|
|
222
|
-
delete resolver._assetMap[res.src.image.url];
|
|
223
|
-
delete resolver._assetMap[res.src.atlas.url];
|
|
224
|
-
delete resolver._assetMap[res.src.ske.url];
|
|
225
|
-
delete resolver._resolverHash[res.src.image.url];
|
|
226
|
-
delete resolver._resolverHash[res.src.atlas.url];
|
|
227
|
-
delete resolver._resolverHash[res.src.ske.url];
|
|
228
368
|
eva_js.resource.destroy(resourceName);
|
|
229
369
|
delete dataMap[resourceName];
|
|
230
370
|
}
|
|
@@ -232,17 +372,39 @@ function releaseSpineData(res, _imageSrc) {
|
|
|
232
372
|
}
|
|
233
373
|
|
|
234
374
|
const MaxRetryCount = 20;
|
|
375
|
+
/**
|
|
376
|
+
* Spine 骨骼动画系统
|
|
377
|
+
*
|
|
378
|
+
* SpineSystem 负责管理所有 Spine 组件的骨架创建、动画更新和资源管理。
|
|
379
|
+
* 系统会监听 Spine 组件的变化,自动加载骨骼数据并创建动画实例,
|
|
380
|
+
* 并在每帧更新所有活跃的 Spine 动画。
|
|
381
|
+
*
|
|
382
|
+
* 主要功能:
|
|
383
|
+
* - 骨骼数据加载和缓存
|
|
384
|
+
* - 动画实例创建和销毁
|
|
385
|
+
* - 每帧动画状态更新
|
|
386
|
+
* - WebGL 上下文恢复处理
|
|
387
|
+
* - 资源重试机制
|
|
388
|
+
*/
|
|
235
389
|
let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
|
|
236
390
|
constructor() {
|
|
237
391
|
super(...arguments);
|
|
392
|
+
/** 骨架实例映射表(游戏对象 ID -> 骨架容器) */
|
|
238
393
|
this.armatures = {};
|
|
239
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* 初始化系统
|
|
397
|
+
* @param obj - 初始化参数
|
|
398
|
+
* @param obj.pixiSpine - PixiJS Spine 插件实例
|
|
399
|
+
*/
|
|
240
400
|
init({ pixiSpine }) {
|
|
241
401
|
this.renderSystem = this.game.getSystem(pluginRenderer.RendererSystem);
|
|
242
402
|
this.renderSystem.rendererManager.register(this);
|
|
243
403
|
this.pixiSpine = pixiSpine;
|
|
244
404
|
this.game.canvas.addEventListener('webglcontextrestored', () => {
|
|
405
|
+
// 重建所有spine
|
|
245
406
|
const objs = this.game.gameObjects;
|
|
407
|
+
// clearCache();
|
|
246
408
|
let toAdd = [];
|
|
247
409
|
for (let k in this.armatures) {
|
|
248
410
|
const id = +k;
|
|
@@ -275,8 +437,14 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
|
|
|
275
437
|
}, 1000);
|
|
276
438
|
}, false);
|
|
277
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* 每帧更新所有 Spine 动画
|
|
442
|
+
* @param e - 更新参数,包含帧间隔时间
|
|
443
|
+
*/
|
|
278
444
|
update(e) {
|
|
279
445
|
for (let key in this.armatures) {
|
|
446
|
+
// TODO: 类型
|
|
447
|
+
// @ts-ignore
|
|
280
448
|
this.armatures[key].update(e.deltaTime * 0.001);
|
|
281
449
|
}
|
|
282
450
|
super.update();
|
|
@@ -317,6 +485,7 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
|
|
|
317
485
|
component.addHandler = setTimeout(() => {
|
|
318
486
|
if (!component.destroied) {
|
|
319
487
|
if (count === undefined) {
|
|
488
|
+
// 最大重试次数
|
|
320
489
|
count = MaxRetryCount;
|
|
321
490
|
}
|
|
322
491
|
count--;
|
|
@@ -333,9 +502,11 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
|
|
|
333
502
|
this.remove(changed);
|
|
334
503
|
const container = (_b = (_a = this.renderSystem) === null || _a === void 0 ? void 0 : _a.containerManager) === null || _b === void 0 ? void 0 : _b.getContainer(changed.gameObject.id);
|
|
335
504
|
if (!container) {
|
|
505
|
+
// console.warn('添加spine的container不存在');
|
|
336
506
|
return;
|
|
337
507
|
}
|
|
338
508
|
component.lastResource = component.resource;
|
|
509
|
+
// @ts-ignore
|
|
339
510
|
const armature = new this.pixiSpine.Spine({
|
|
340
511
|
skeletonData: spineData,
|
|
341
512
|
autoUpdate: false,
|
|
@@ -347,23 +518,30 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
|
|
|
347
518
|
armature.y = tran.size.height * tran.origin.y;
|
|
348
519
|
}
|
|
349
520
|
container.addChildAt(armature, 0);
|
|
521
|
+
/** 保证第一帧显示正常 */
|
|
350
522
|
armature.update();
|
|
351
523
|
component.armature = armature;
|
|
524
|
+
// @ts-ignore
|
|
352
525
|
component.emit('loaded', { resource: component.resource });
|
|
353
526
|
armature.state.addListener({
|
|
527
|
+
// @ts-ignore
|
|
354
528
|
start: (track, event) => {
|
|
355
529
|
component.emit('start', { track, name: track.animation.name });
|
|
356
530
|
},
|
|
531
|
+
// @ts-ignore
|
|
357
532
|
complete: (track, event) => {
|
|
358
533
|
component.emit('complete', { track, name: track.animation.name });
|
|
359
534
|
},
|
|
535
|
+
// @ts-ignore
|
|
360
536
|
interrupt: (track, event) => {
|
|
361
537
|
component.emit('interrupt', { track, name: track.animation.name });
|
|
362
538
|
},
|
|
363
|
-
end: (track,
|
|
539
|
+
end: (track, // @ts-ignore
|
|
540
|
+
event) => {
|
|
364
541
|
component.emit('end', { track, name: track.animation.name });
|
|
365
542
|
},
|
|
366
543
|
event: (track, event) => {
|
|
544
|
+
// @ts-ignore
|
|
367
545
|
component.emit('event', track, event);
|
|
368
546
|
},
|
|
369
547
|
});
|
|
@@ -386,9 +564,11 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
|
|
|
386
564
|
}
|
|
387
565
|
if (component.armature) {
|
|
388
566
|
component.armature.destroy({ children: true });
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
567
|
+
if (!component.keepResource) {
|
|
568
|
+
const res = yield eva_js.resource.getResource(component.lastResource);
|
|
569
|
+
((_d = (_c = res.data) === null || _c === void 0 ? void 0 : _c.image) === null || _d === void 0 ? void 0 : _d.src) || ((_f = (_e = res.data) === null || _e === void 0 ? void 0 : _e.image) === null || _f === void 0 ? void 0 : _f.label);
|
|
570
|
+
releaseSpineData(res);
|
|
571
|
+
}
|
|
392
572
|
}
|
|
393
573
|
component.armature = null;
|
|
394
574
|
delete this.armatures[changed.gameObject.id];
|
|
@@ -396,6 +576,7 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
|
|
|
396
576
|
});
|
|
397
577
|
}
|
|
398
578
|
};
|
|
579
|
+
/** 系统名称 */
|
|
399
580
|
SpineSystem.systemName = 'SpineSystem';
|
|
400
581
|
SpineSystem = __decorate([
|
|
401
582
|
eva_js.decorators.componentObserver({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@eva/eva.js"),t=require("@eva/plugin-renderer"),a=require("@eva/inspector-decorator")
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@eva/eva.js"),t=require("@eva/plugin-renderer"),a=require("@eva/inspector-decorator");
|
|
2
2
|
/*! *****************************************************************************
|
|
3
3
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
@@ -13,4 +13,4 @@ MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
|
13
13
|
See the Apache Version 2.0 License for specific language governing permissions
|
|
14
14
|
and limitations under the License.
|
|
15
15
|
***************************************************************************** */
|
|
16
|
-
function i(e,t,a,
|
|
16
|
+
function i(e,t,a,i){var r,n=arguments.length,o=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,a):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,a,i);else for(var s=e.length-1;s>=0;s--)(r=e[s])&&(o=(n<3?r(o):n>3?r(t,a,o):r(t,a))||o);return n>3&&o&&Object.defineProperty(t,a,o),o}function r(e,t,a,i){return new(a||(a=Promise))((function(r,n){function o(e){try{c(i.next(e))}catch(e){n(e)}}function s(e){try{c(i.throw(e))}catch(e){n(e)}}function c(e){e.done?r(e.value):new a((function(t){t(e.value)})).then(o,s)}c((i=i.apply(e,t||[])).next())}))}class n extends e.Component{constructor(){super(...arguments),this.resource="",this.scale=1,this.animationName="",this.autoPlay=!0,this.keepResource=!1,this.waitExecuteInfos=[]}set armature(e){if(this._armature=e,e){this.autoPlay&&this.play(this.animationName);for(const e of this.waitExecuteInfos)if(e.playType){const{name:t,loop:a,track:i}=e;this.play(t,a,i)}else this.stop(e.track);this.waitExecuteInfos=[]}}get armature(){return this._armature}init(e){e&&Object.assign(this,e)}onDestroy(){this.destroied=!0}play(e,t,a){try{const i=null!=t?t:this.autoPlay;e&&(this.animationName=e),this.armature?(void 0===a&&(a=0),this.armature.state.setAnimation(a,this.animationName,i)):this.waitExecuteInfos.push({playType:!0,name:e,loop:i,track:a})}catch(e){console.log(e)}}stop(e){this.armature?(void 0===e&&(e=0),this.armature.state.setEmptyAnimation(e,0)):this.waitExecuteInfos.push({playType:!1,track:e})}addAnimation(e,t,a,i){try{this.armature&&(void 0===i&&(i=0),this.armature.state.addAnimation(i,e,a,t))}catch(e){console.log(e)}}setMix(e,t,a){this.armature&&this.armature.state.data.setMix(e,t,a)}getAnim(e=0){try{if(this.armature)return this.armature.state.tracks[e].animation.name}catch(e){console.log(e)}}setDefaultMix(e){this.armature&&(this.armature.state.data.defaultMix=e)}setAttachment(e,t){this.armature&&this.armature.skeleton.setAttachment(e,t)}getBone(e){if(this.armature)return this.armature.skeleton.findBone(e)}}n.componentName="Spine",i([a.type("string")],n.prototype,"resource",void 0),i([a.type("number")],n.prototype,"scale",void 0),i([a.type("string")],n.prototype,"animationName",void 0),i([a.type("boolean")],n.prototype,"autoPlay",void 0),i([a.type("boolean")],n.prototype,"keepResource",void 0);let o={};function s(e,t,a){return r(this,void 0,void 0,(function*(){let i=o[e.name];if(!i)if(e.complete)i=function(e,t,a,i){const r=t.ske,n=t.atlas,s=new i.AtlasAttachmentLoader(n),c=r instanceof Uint8Array?new i.SkeletonBinary(s):new i.SkeletonJson(s);c.scale=a||1;const m={spineData:c.readSkeletonData(r),ref:0,imageSrc:t.image.label};return o[e]=m,m}(e.name,e.data,t,a);else if(!i)return;return i.ref++,i.spineData}))}let c=class extends t.Renderer{constructor(){super(...arguments),this.armatures={}}init({pixiSpine:a}){this.renderSystem=this.game.getSystem(t.RendererSystem),this.renderSystem.rendererManager.register(this),this.pixiSpine=a,this.game.canvas.addEventListener("webglcontextrestored",(()=>{const t=this.game.gameObjects;let a=[];for(let i in this.armatures){const r=+i;for(let i=0;i<t.length;++i){let o=t[i];if(o.id===r){let t=o.getComponent(n);t&&(this.remove({type:e.OBSERVER_TYPE.REMOVE,gameObject:o,component:t,componentName:n.componentName}),a.push({type:e.OBSERVER_TYPE.ADD,gameObject:o,component:t,componentName:n.componentName}));break}}}setTimeout((()=>{a.forEach((e=>{this.add(e)}))}),1e3)}),!1)}update(e){for(let t in this.armatures)this.armatures[t].update(.001*e.deltaTime);super.update()}componentChanged(t){return r(this,void 0,void 0,(function*(){if("Spine"===t.componentName)if(t.type===e.OBSERVER_TYPE.ADD)this.add(t);else if(t.type===e.OBSERVER_TYPE.CHANGE){if("resource"===t.prop.prop[0])this.change(t)}else t.type===e.OBSERVER_TYPE.REMOVE&&this.remove(t)}))}add(t,a){var i,n;return r(this,void 0,void 0,(function*(){const r=t.component;clearTimeout(r.addHandler);const o=t.gameObject.id,c=this.increaseAsyncId(o),m=yield e.resource.getResource(r.resource);if(!this.validateAsyncId(o,c))return;const u=yield s(m,r.scale,this.pixiSpine);if(!this.validateAsyncId(o,c))return;if(!u)return void(r.addHandler=setTimeout((()=>{r.destroied||(void 0===a&&(a=20),--a>0?this.add(t,a):console.log("retry exceed max times",r.resource))}),1e3));this.remove(t);const d=null===(n=null===(i=this.renderSystem)||void 0===i?void 0:i.containerManager)||void 0===n?void 0:n.getContainer(t.gameObject.id);if(!d)return;r.lastResource=r.resource;const l=new this.pixiSpine.Spine({skeletonData:u,autoUpdate:!1});if(this.armatures[t.gameObject.id]=l,t.gameObject&&t.gameObject.transform){const e=t.gameObject.transform;l.x=e.size.width*e.origin.x,l.y=e.size.height*e.origin.y}d.addChildAt(l,0),l.update(),r.armature=l,r.emit("loaded",{resource:r.resource}),l.state.addListener({start:(e,t)=>{r.emit("start",{track:e,name:e.animation.name})},complete:(e,t)=>{r.emit("complete",{track:e,name:e.animation.name})},interrupt:(e,t)=>{r.emit("interrupt",{track:e,name:e.animation.name})},end:(e,t)=>{r.emit("end",{track:e,name:e.animation.name})},event:(e,t)=>{r.emit("event",e,t)}})}))}change(e){this.remove(e),this.add(e)}remove(t){var a,i,n,s,c,m;return r(this,void 0,void 0,(function*(){this.increaseAsyncId(t.gameObject.id);const u=t.component;clearTimeout(u.addHandler);const d=this.armatures[t.gameObject.id],l=null===(i=null===(a=this.renderSystem)||void 0===a?void 0:a.containerManager)||void 0===i?void 0:i.getContainer(t.gameObject.id);if(l&&d&&l.removeChild(d),u.armature&&(u.armature.destroy({children:!0}),!u.keepResource)){const t=yield e.resource.getResource(u.lastResource);(null===(s=null===(n=t.data)||void 0===n?void 0:n.image)||void 0===s?void 0:s.src)||null===(m=null===(c=t.data)||void 0===c?void 0:c.image)||void 0===m||m.label,function(t){const a=t.name,i=o[a];i&&(i.ref--,setTimeout((()=>r(this,void 0,void 0,(function*(){i.ref<=0&&(e.resource.destroy(a),delete o[a])}))),100))}(t)}u.armature=null,delete this.armatures[t.gameObject.id],t.type,e.OBSERVER_TYPE.CHANGE}))}};c.systemName="SpineSystem",c=i([e.decorators.componentObserver({Spine:["resource"]})],c);var m=c;e.resource.registerResourceType("SPINE"),exports.Spine=n,exports.SpineSystem=m;
|
package/dist/spine-base.d.ts
CHANGED
|
@@ -8,28 +8,172 @@ import { RendererManager } from '@eva/plugin-renderer';
|
|
|
8
8
|
import { RendererSystem } from '@eva/plugin-renderer';
|
|
9
9
|
import { UpdateParams } from '@eva/eva.js';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Spine 骨骼动画组件
|
|
13
|
+
*
|
|
14
|
+
* Spine 组件用于播放 Esoteric Software 的 Spine 骨骼动画。
|
|
15
|
+
* 支持骨骼动画播放控制、动画混合、附件替换等高级功能,
|
|
16
|
+
* 适用于角色动画、复杂特效等需要骨骼动画的场景。
|
|
17
|
+
*
|
|
18
|
+
* 主要功能:
|
|
19
|
+
* - 骨骼动画播放和控制
|
|
20
|
+
* - 动画轨道管理(多动画并行)
|
|
21
|
+
* - 动画混合过渡
|
|
22
|
+
* - 骨骼和附件访问
|
|
23
|
+
* - 支持 Spine 3.6 和 3.8 版本
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* // 创建 Spine 动画
|
|
28
|
+
* const character = new GameObject('character');
|
|
29
|
+
* const spine = new Spine({
|
|
30
|
+
* resource: 'heroSpine', // Spine 资源
|
|
31
|
+
* animationName: 'idle', // 默认动画
|
|
32
|
+
* autoPlay: true, // 自动播放
|
|
33
|
+
* scale: 0.5 // 缩放比例
|
|
34
|
+
* });
|
|
35
|
+
* character.addComponent(spine);
|
|
36
|
+
*
|
|
37
|
+
* // 播放动画
|
|
38
|
+
* spine.play('walk', true); // 循环播放 walk 动画
|
|
39
|
+
*
|
|
40
|
+
* // 停止动画
|
|
41
|
+
* spine.stop();
|
|
42
|
+
*
|
|
43
|
+
* // 动画混合
|
|
44
|
+
* spine.setMix('idle', 'walk', 0.3); // 设置过渡时间
|
|
45
|
+
* spine.play('walk');
|
|
46
|
+
*
|
|
47
|
+
* // 添加动画队列
|
|
48
|
+
* spine.play('attack', false); // 播放攻击动画
|
|
49
|
+
* spine.addAnimation('idle', 0, true); // 攻击完成后回到 idle
|
|
50
|
+
*
|
|
51
|
+
* // 替换附件(换装)
|
|
52
|
+
* spine.setAttachment('weapon', 'sword'); // 将武器槽替换为剑
|
|
53
|
+
*
|
|
54
|
+
* // 访问骨骼
|
|
55
|
+
* const headBone = spine.getBone('head');
|
|
56
|
+
* if (headBone) {
|
|
57
|
+
* headBone.rotation = 15; // 旋转头部
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* // 多轨道动画
|
|
61
|
+
* spine.play('walk', true, 0); // 轨道0:身体动画
|
|
62
|
+
* spine.play('shoot', false, 1); // 轨道1:上半身动画
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
11
65
|
export declare class Spine extends Component<SpineParams> {
|
|
66
|
+
/** 组件名称 */
|
|
12
67
|
static componentName: string;
|
|
68
|
+
/** Spine 资源名称 */
|
|
13
69
|
resource: string;
|
|
70
|
+
/** 动画缩放比例 */
|
|
14
71
|
scale: number;
|
|
72
|
+
/** 当前播放的动画名称 */
|
|
15
73
|
animationName: string;
|
|
74
|
+
/** 是否自动播放动画 */
|
|
16
75
|
autoPlay: boolean;
|
|
76
|
+
/** 是否保留资源(销毁时不释放) */
|
|
77
|
+
keepResource: boolean;
|
|
78
|
+
/** Spine 骨架实例(内部使用) */
|
|
17
79
|
private _armature;
|
|
80
|
+
/** 等待执行的动画操作队列 */
|
|
18
81
|
private waitExecuteInfos;
|
|
82
|
+
/**
|
|
83
|
+
* 设置骨架实例
|
|
84
|
+
* 当骨架加载完成后自动执行等待队列中的动画操作
|
|
85
|
+
*/
|
|
19
86
|
set armature(val: any);
|
|
87
|
+
/** 获取骨架实例 */
|
|
20
88
|
get armature(): any;
|
|
89
|
+
/** 组件是否已销毁 */
|
|
21
90
|
destroied: boolean;
|
|
91
|
+
/** 动画事件处理器 */
|
|
22
92
|
addHandler: any;
|
|
93
|
+
/** 上一次使用的资源名称 */
|
|
23
94
|
lastResource: string;
|
|
95
|
+
/**
|
|
96
|
+
* 初始化组件
|
|
97
|
+
* @param obj - 初始化参数
|
|
98
|
+
* @param obj.resource - Spine 资源名称
|
|
99
|
+
* @param obj.animationName - 默认动画名称
|
|
100
|
+
* @param obj.scale - 缩放比例
|
|
101
|
+
* @param obj.autoPlay - 是否自动播放
|
|
102
|
+
*/
|
|
24
103
|
init(obj?: SpineParams): void;
|
|
104
|
+
/** 组件销毁时调用 */
|
|
25
105
|
onDestroy(): void;
|
|
106
|
+
/**
|
|
107
|
+
* 播放指定动画
|
|
108
|
+
*
|
|
109
|
+
* 如果骨架尚未加载完成,动画操作将被加入等待队列。
|
|
110
|
+
*
|
|
111
|
+
* @param name - 动画名称,不指定则使用 animationName 属性
|
|
112
|
+
* @param loopAnimation - 是否循环播放,默认跟随 autoPlay 属性
|
|
113
|
+
* @param track - 动画轨道编号,默认为 0
|
|
114
|
+
*/
|
|
26
115
|
play(name?: string, loopAnimation?: boolean, track?: number): void;
|
|
116
|
+
/**
|
|
117
|
+
* 停止指定轨道的动画
|
|
118
|
+
*
|
|
119
|
+
* 如果骨架尚未加载完成,停止操作将被加入等待队列。
|
|
120
|
+
*
|
|
121
|
+
* @param track - 动画轨道编号,默认为 0
|
|
122
|
+
*/
|
|
27
123
|
stop(track?: number): void;
|
|
124
|
+
/**
|
|
125
|
+
* 在当前动画之后添加新动画到队列
|
|
126
|
+
*
|
|
127
|
+
* 用于创建动画序列,当前动画播放完毕后自动播放下一个动画。
|
|
128
|
+
*
|
|
129
|
+
* @param name - 动画名称
|
|
130
|
+
* @param delay - 延迟时间(秒)
|
|
131
|
+
* @param loop - 是否循环播放
|
|
132
|
+
* @param track - 动画轨道编号,默认为 0
|
|
133
|
+
*/
|
|
28
134
|
addAnimation(name?: string, delay?: number, loop?: boolean, track?: number): void;
|
|
135
|
+
/**
|
|
136
|
+
* 设置两个动画之间的混合过渡时间
|
|
137
|
+
*
|
|
138
|
+
* 当从一个动画切换到另一个动画时,会在指定时间内进行平滑过渡。
|
|
139
|
+
*
|
|
140
|
+
* @param from - 起始动画名称
|
|
141
|
+
* @param to - 目标动画名称
|
|
142
|
+
* @param duration - 过渡时长(秒)
|
|
143
|
+
*/
|
|
29
144
|
setMix(from: string, to: string, duration: number): void;
|
|
145
|
+
/**
|
|
146
|
+
* 获取指定轨道当前播放的动画名称
|
|
147
|
+
*
|
|
148
|
+
* @param track - 动画轨道编号,默认为 0
|
|
149
|
+
* @returns 动画名称,如果未找到则返回 undefined
|
|
150
|
+
*/
|
|
30
151
|
getAnim(track?: number): any;
|
|
152
|
+
/**
|
|
153
|
+
* 设置默认的动画混合时间
|
|
154
|
+
*
|
|
155
|
+
* 当没有为特定动画对指定混合时间时,将使用此默认值。
|
|
156
|
+
*
|
|
157
|
+
* @param duration - 默认混合时长(秒)
|
|
158
|
+
*/
|
|
31
159
|
setDefaultMix(duration: number): void;
|
|
160
|
+
/**
|
|
161
|
+
* 替换指定插槽的附件
|
|
162
|
+
*
|
|
163
|
+
* 用于换装、武器切换等场景。
|
|
164
|
+
*
|
|
165
|
+
* @param slotName - 插槽名称
|
|
166
|
+
* @param attachmentName - 附件名称
|
|
167
|
+
*/
|
|
32
168
|
setAttachment(slotName: string, attachmentName: string): void;
|
|
169
|
+
/**
|
|
170
|
+
* 获取指定名称的骨骼
|
|
171
|
+
*
|
|
172
|
+
* 可用于直接操作骨骼的位置、旋转、缩放等属性。
|
|
173
|
+
*
|
|
174
|
+
* @param boneName - 骨骼名称
|
|
175
|
+
* @returns 骨骼对象,如果未找到则返回 undefined
|
|
176
|
+
*/
|
|
33
177
|
getBone(boneName: string): any;
|
|
34
178
|
}
|
|
35
179
|
|
|
@@ -40,16 +184,45 @@ export declare interface SpineParams {
|
|
|
40
184
|
autoPlay?: boolean;
|
|
41
185
|
}
|
|
42
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Spine 骨骼动画系统
|
|
189
|
+
*
|
|
190
|
+
* SpineSystem 负责管理所有 Spine 组件的骨架创建、动画更新和资源管理。
|
|
191
|
+
* 系统会监听 Spine 组件的变化,自动加载骨骼数据并创建动画实例,
|
|
192
|
+
* 并在每帧更新所有活跃的 Spine 动画。
|
|
193
|
+
*
|
|
194
|
+
* 主要功能:
|
|
195
|
+
* - 骨骼数据加载和缓存
|
|
196
|
+
* - 动画实例创建和销毁
|
|
197
|
+
* - 每帧动画状态更新
|
|
198
|
+
* - WebGL 上下文恢复处理
|
|
199
|
+
* - 资源重试机制
|
|
200
|
+
*/
|
|
43
201
|
export declare class SpineSystem extends Renderer {
|
|
202
|
+
/** 系统名称 */
|
|
44
203
|
static systemName: string;
|
|
204
|
+
/** 骨架实例映射表(游戏对象 ID -> 骨架容器) */
|
|
45
205
|
armatures: Record<number, Container>;
|
|
206
|
+
/** 渲染系统引用 */
|
|
46
207
|
renderSystem: RendererSystem;
|
|
208
|
+
/** 渲染器管理器 */
|
|
47
209
|
rendererManager: RendererManager;
|
|
210
|
+
/** 容器管理器 */
|
|
48
211
|
containerManager: ContainerManager;
|
|
212
|
+
/** PixiJS Spine 插件实例 */
|
|
49
213
|
pixiSpine: any;
|
|
214
|
+
/**
|
|
215
|
+
* 初始化系统
|
|
216
|
+
* @param obj - 初始化参数
|
|
217
|
+
* @param obj.pixiSpine - PixiJS Spine 插件实例
|
|
218
|
+
*/
|
|
50
219
|
init({ pixiSpine }: {
|
|
51
220
|
pixiSpine: any;
|
|
52
221
|
}): void;
|
|
222
|
+
/**
|
|
223
|
+
* 每帧更新所有 Spine 动画
|
|
224
|
+
* @param e - 更新参数,包含帧间隔时间
|
|
225
|
+
*/
|
|
53
226
|
update(e: UpdateParams): void;
|
|
54
227
|
componentChanged(changed: ComponentChanged): Promise<void>;
|
|
55
228
|
add(changed: ComponentChanged, count?: number): Promise<void>;
|
package/dist/spine-base.esm.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Component, resource, OBSERVER_TYPE, decorators } from '@eva/eva.js';
|
|
2
2
|
import { Renderer, RendererSystem } from '@eva/plugin-renderer';
|
|
3
3
|
import { type } from '@eva/inspector-decorator';
|
|
4
|
-
import { Assets } from 'pixi.js';
|
|
5
4
|
|
|
6
5
|
/*! *****************************************************************************
|
|
7
6
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
@@ -34,15 +33,80 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
34
33
|
});
|
|
35
34
|
}
|
|
36
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Spine 骨骼动画组件
|
|
38
|
+
*
|
|
39
|
+
* Spine 组件用于播放 Esoteric Software 的 Spine 骨骼动画。
|
|
40
|
+
* 支持骨骼动画播放控制、动画混合、附件替换等高级功能,
|
|
41
|
+
* 适用于角色动画、复杂特效等需要骨骼动画的场景。
|
|
42
|
+
*
|
|
43
|
+
* 主要功能:
|
|
44
|
+
* - 骨骼动画播放和控制
|
|
45
|
+
* - 动画轨道管理(多动画并行)
|
|
46
|
+
* - 动画混合过渡
|
|
47
|
+
* - 骨骼和附件访问
|
|
48
|
+
* - 支持 Spine 3.6 和 3.8 版本
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // 创建 Spine 动画
|
|
53
|
+
* const character = new GameObject('character');
|
|
54
|
+
* const spine = new Spine({
|
|
55
|
+
* resource: 'heroSpine', // Spine 资源
|
|
56
|
+
* animationName: 'idle', // 默认动画
|
|
57
|
+
* autoPlay: true, // 自动播放
|
|
58
|
+
* scale: 0.5 // 缩放比例
|
|
59
|
+
* });
|
|
60
|
+
* character.addComponent(spine);
|
|
61
|
+
*
|
|
62
|
+
* // 播放动画
|
|
63
|
+
* spine.play('walk', true); // 循环播放 walk 动画
|
|
64
|
+
*
|
|
65
|
+
* // 停止动画
|
|
66
|
+
* spine.stop();
|
|
67
|
+
*
|
|
68
|
+
* // 动画混合
|
|
69
|
+
* spine.setMix('idle', 'walk', 0.3); // 设置过渡时间
|
|
70
|
+
* spine.play('walk');
|
|
71
|
+
*
|
|
72
|
+
* // 添加动画队列
|
|
73
|
+
* spine.play('attack', false); // 播放攻击动画
|
|
74
|
+
* spine.addAnimation('idle', 0, true); // 攻击完成后回到 idle
|
|
75
|
+
*
|
|
76
|
+
* // 替换附件(换装)
|
|
77
|
+
* spine.setAttachment('weapon', 'sword'); // 将武器槽替换为剑
|
|
78
|
+
*
|
|
79
|
+
* // 访问骨骼
|
|
80
|
+
* const headBone = spine.getBone('head');
|
|
81
|
+
* if (headBone) {
|
|
82
|
+
* headBone.rotation = 15; // 旋转头部
|
|
83
|
+
* }
|
|
84
|
+
*
|
|
85
|
+
* // 多轨道动画
|
|
86
|
+
* spine.play('walk', true, 0); // 轨道0:身体动画
|
|
87
|
+
* spine.play('shoot', false, 1); // 轨道1:上半身动画
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
37
90
|
class Spine extends Component {
|
|
38
91
|
constructor() {
|
|
39
92
|
super(...arguments);
|
|
93
|
+
/** Spine 资源名称 */
|
|
40
94
|
this.resource = '';
|
|
95
|
+
/** 动画缩放比例 */
|
|
41
96
|
this.scale = 1;
|
|
97
|
+
/** 当前播放的动画名称 */
|
|
42
98
|
this.animationName = '';
|
|
99
|
+
/** 是否自动播放动画 */
|
|
43
100
|
this.autoPlay = true;
|
|
101
|
+
/** 是否保留资源(销毁时不释放) */
|
|
102
|
+
this.keepResource = false;
|
|
103
|
+
/** 等待执行的动画操作队列 */
|
|
44
104
|
this.waitExecuteInfos = [];
|
|
45
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* 设置骨架实例
|
|
108
|
+
* 当骨架加载完成后自动执行等待队列中的动画操作
|
|
109
|
+
*/
|
|
46
110
|
set armature(val) {
|
|
47
111
|
this._armature = val;
|
|
48
112
|
if (!val)
|
|
@@ -61,17 +125,36 @@ class Spine extends Component {
|
|
|
61
125
|
}
|
|
62
126
|
this.waitExecuteInfos = [];
|
|
63
127
|
}
|
|
128
|
+
/** 获取骨架实例 */
|
|
64
129
|
get armature() {
|
|
65
130
|
return this._armature;
|
|
66
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* 初始化组件
|
|
134
|
+
* @param obj - 初始化参数
|
|
135
|
+
* @param obj.resource - Spine 资源名称
|
|
136
|
+
* @param obj.animationName - 默认动画名称
|
|
137
|
+
* @param obj.scale - 缩放比例
|
|
138
|
+
* @param obj.autoPlay - 是否自动播放
|
|
139
|
+
*/
|
|
67
140
|
init(obj) {
|
|
68
141
|
if (!obj)
|
|
69
142
|
return;
|
|
70
143
|
Object.assign(this, obj);
|
|
71
144
|
}
|
|
145
|
+
/** 组件销毁时调用 */
|
|
72
146
|
onDestroy() {
|
|
73
147
|
this.destroied = true;
|
|
74
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* 播放指定动画
|
|
151
|
+
*
|
|
152
|
+
* 如果骨架尚未加载完成,动画操作将被加入等待队列。
|
|
153
|
+
*
|
|
154
|
+
* @param name - 动画名称,不指定则使用 animationName 属性
|
|
155
|
+
* @param loopAnimation - 是否循环播放,默认跟随 autoPlay 属性
|
|
156
|
+
* @param track - 动画轨道编号,默认为 0
|
|
157
|
+
*/
|
|
75
158
|
play(name, loopAnimation, track) {
|
|
76
159
|
try {
|
|
77
160
|
const loop = loopAnimation !== null && loopAnimation !== void 0 ? loopAnimation : this.autoPlay;
|
|
@@ -81,6 +164,12 @@ class Spine extends Component {
|
|
|
81
164
|
this.waitExecuteInfos.push({
|
|
82
165
|
playType: true,
|
|
83
166
|
name,
|
|
167
|
+
/**
|
|
168
|
+
* 在 v1.2.2 之前,Spine 动画的 autoPlay 为 true,动画会循环播放 https://github.com/eva-engine/eva.js/pull/164/files#diff-46e9ae36c04e7a0abedc1e14fd9d1c4e81d8386e9bb851f85971ccdba8957804L131
|
|
169
|
+
* 在 v1.2.2 之前,Spine 动画在每加载完( armature 设置之前)调用 play 是不生效的, 在 v1.2.2 [#164](https://github.com/eva-engine/eva.js/pull/164) 解决了这个问题
|
|
170
|
+
* 解决了不生效的问题以后,加载完成之前调用 play 默认循环是false,导致 autoPlay 下本来循环动画不循环了,和之前表现不一致
|
|
171
|
+
* 为了解决这个问题,在 autoPlay 的情况下,未加载完之前调用 play ,默认循环播放,除非设置不循环参数
|
|
172
|
+
*/
|
|
84
173
|
loop,
|
|
85
174
|
track,
|
|
86
175
|
});
|
|
@@ -96,6 +185,13 @@ class Spine extends Component {
|
|
|
96
185
|
console.log(e);
|
|
97
186
|
}
|
|
98
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* 停止指定轨道的动画
|
|
190
|
+
*
|
|
191
|
+
* 如果骨架尚未加载完成,停止操作将被加入等待队列。
|
|
192
|
+
*
|
|
193
|
+
* @param track - 动画轨道编号,默认为 0
|
|
194
|
+
*/
|
|
99
195
|
stop(track) {
|
|
100
196
|
if (!this.armature) {
|
|
101
197
|
this.waitExecuteInfos.push({
|
|
@@ -109,6 +205,16 @@ class Spine extends Component {
|
|
|
109
205
|
}
|
|
110
206
|
this.armature.state.setEmptyAnimation(track, 0);
|
|
111
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* 在当前动画之后添加新动画到队列
|
|
210
|
+
*
|
|
211
|
+
* 用于创建动画序列,当前动画播放完毕后自动播放下一个动画。
|
|
212
|
+
*
|
|
213
|
+
* @param name - 动画名称
|
|
214
|
+
* @param delay - 延迟时间(秒)
|
|
215
|
+
* @param loop - 是否循环播放
|
|
216
|
+
* @param track - 动画轨道编号,默认为 0
|
|
217
|
+
*/
|
|
112
218
|
addAnimation(name, delay, loop, track) {
|
|
113
219
|
try {
|
|
114
220
|
if (!this.armature) {
|
|
@@ -124,12 +230,27 @@ class Spine extends Component {
|
|
|
124
230
|
console.log(e);
|
|
125
231
|
}
|
|
126
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* 设置两个动画之间的混合过渡时间
|
|
235
|
+
*
|
|
236
|
+
* 当从一个动画切换到另一个动画时,会在指定时间内进行平滑过渡。
|
|
237
|
+
*
|
|
238
|
+
* @param from - 起始动画名称
|
|
239
|
+
* @param to - 目标动画名称
|
|
240
|
+
* @param duration - 过渡时长(秒)
|
|
241
|
+
*/
|
|
127
242
|
setMix(from, to, duration) {
|
|
128
243
|
if (!this.armature) ;
|
|
129
244
|
else {
|
|
130
245
|
this.armature.state.data.setMix(from, to, duration);
|
|
131
246
|
}
|
|
132
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* 获取指定轨道当前播放的动画名称
|
|
250
|
+
*
|
|
251
|
+
* @param track - 动画轨道编号,默认为 0
|
|
252
|
+
* @returns 动画名称,如果未找到则返回 undefined
|
|
253
|
+
*/
|
|
133
254
|
getAnim(track = 0) {
|
|
134
255
|
try {
|
|
135
256
|
if (!this.armature) {
|
|
@@ -142,18 +263,41 @@ class Spine extends Component {
|
|
|
142
263
|
console.log(e);
|
|
143
264
|
}
|
|
144
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* 设置默认的动画混合时间
|
|
268
|
+
*
|
|
269
|
+
* 当没有为特定动画对指定混合时间时,将使用此默认值。
|
|
270
|
+
*
|
|
271
|
+
* @param duration - 默认混合时长(秒)
|
|
272
|
+
*/
|
|
145
273
|
setDefaultMix(duration) {
|
|
146
274
|
if (!this.armature) ;
|
|
147
275
|
else {
|
|
148
276
|
this.armature.state.data.defaultMix = duration;
|
|
149
277
|
}
|
|
150
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* 替换指定插槽的附件
|
|
281
|
+
*
|
|
282
|
+
* 用于换装、武器切换等场景。
|
|
283
|
+
*
|
|
284
|
+
* @param slotName - 插槽名称
|
|
285
|
+
* @param attachmentName - 附件名称
|
|
286
|
+
*/
|
|
151
287
|
setAttachment(slotName, attachmentName) {
|
|
152
288
|
if (!this.armature) {
|
|
153
289
|
return;
|
|
154
290
|
}
|
|
155
291
|
this.armature.skeleton.setAttachment(slotName, attachmentName);
|
|
156
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* 获取指定名称的骨骼
|
|
295
|
+
*
|
|
296
|
+
* 可用于直接操作骨骼的位置、旋转、缩放等属性。
|
|
297
|
+
*
|
|
298
|
+
* @param boneName - 骨骼名称
|
|
299
|
+
* @returns 骨骼对象,如果未找到则返回 undefined
|
|
300
|
+
*/
|
|
157
301
|
getBone(boneName) {
|
|
158
302
|
if (!this.armature) {
|
|
159
303
|
return;
|
|
@@ -161,6 +305,7 @@ class Spine extends Component {
|
|
|
161
305
|
return this.armature.skeleton.findBone(boneName);
|
|
162
306
|
}
|
|
163
307
|
}
|
|
308
|
+
/** 组件名称 */
|
|
164
309
|
Spine.componentName = 'Spine';
|
|
165
310
|
__decorate([
|
|
166
311
|
type('string')
|
|
@@ -173,7 +318,10 @@ __decorate([
|
|
|
173
318
|
], Spine.prototype, "animationName", void 0);
|
|
174
319
|
__decorate([
|
|
175
320
|
type('boolean')
|
|
176
|
-
], Spine.prototype, "autoPlay", void 0);
|
|
321
|
+
], Spine.prototype, "autoPlay", void 0);
|
|
322
|
+
__decorate([
|
|
323
|
+
type('boolean')
|
|
324
|
+
], Spine.prototype, "keepResource", void 0);
|
|
177
325
|
|
|
178
326
|
let dataMap = {};
|
|
179
327
|
function createSpineData(name, data, scale, pixiSpine) {
|
|
@@ -213,14 +361,6 @@ function releaseSpineData(res, _imageSrc) {
|
|
|
213
361
|
data.ref--;
|
|
214
362
|
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
215
363
|
if (data.ref <= 0) {
|
|
216
|
-
yield Assets.unload([res.src.image.url, res.src.atlas.url, res.src.ske.url]);
|
|
217
|
-
const resolver = Assets.resolver;
|
|
218
|
-
delete resolver._assetMap[res.src.image.url];
|
|
219
|
-
delete resolver._assetMap[res.src.atlas.url];
|
|
220
|
-
delete resolver._assetMap[res.src.ske.url];
|
|
221
|
-
delete resolver._resolverHash[res.src.image.url];
|
|
222
|
-
delete resolver._resolverHash[res.src.atlas.url];
|
|
223
|
-
delete resolver._resolverHash[res.src.ske.url];
|
|
224
364
|
resource.destroy(resourceName);
|
|
225
365
|
delete dataMap[resourceName];
|
|
226
366
|
}
|
|
@@ -228,17 +368,39 @@ function releaseSpineData(res, _imageSrc) {
|
|
|
228
368
|
}
|
|
229
369
|
|
|
230
370
|
const MaxRetryCount = 20;
|
|
371
|
+
/**
|
|
372
|
+
* Spine 骨骼动画系统
|
|
373
|
+
*
|
|
374
|
+
* SpineSystem 负责管理所有 Spine 组件的骨架创建、动画更新和资源管理。
|
|
375
|
+
* 系统会监听 Spine 组件的变化,自动加载骨骼数据并创建动画实例,
|
|
376
|
+
* 并在每帧更新所有活跃的 Spine 动画。
|
|
377
|
+
*
|
|
378
|
+
* 主要功能:
|
|
379
|
+
* - 骨骼数据加载和缓存
|
|
380
|
+
* - 动画实例创建和销毁
|
|
381
|
+
* - 每帧动画状态更新
|
|
382
|
+
* - WebGL 上下文恢复处理
|
|
383
|
+
* - 资源重试机制
|
|
384
|
+
*/
|
|
231
385
|
let SpineSystem = class SpineSystem extends Renderer {
|
|
232
386
|
constructor() {
|
|
233
387
|
super(...arguments);
|
|
388
|
+
/** 骨架实例映射表(游戏对象 ID -> 骨架容器) */
|
|
234
389
|
this.armatures = {};
|
|
235
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* 初始化系统
|
|
393
|
+
* @param obj - 初始化参数
|
|
394
|
+
* @param obj.pixiSpine - PixiJS Spine 插件实例
|
|
395
|
+
*/
|
|
236
396
|
init({ pixiSpine }) {
|
|
237
397
|
this.renderSystem = this.game.getSystem(RendererSystem);
|
|
238
398
|
this.renderSystem.rendererManager.register(this);
|
|
239
399
|
this.pixiSpine = pixiSpine;
|
|
240
400
|
this.game.canvas.addEventListener('webglcontextrestored', () => {
|
|
401
|
+
// 重建所有spine
|
|
241
402
|
const objs = this.game.gameObjects;
|
|
403
|
+
// clearCache();
|
|
242
404
|
let toAdd = [];
|
|
243
405
|
for (let k in this.armatures) {
|
|
244
406
|
const id = +k;
|
|
@@ -271,8 +433,14 @@ let SpineSystem = class SpineSystem extends Renderer {
|
|
|
271
433
|
}, 1000);
|
|
272
434
|
}, false);
|
|
273
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* 每帧更新所有 Spine 动画
|
|
438
|
+
* @param e - 更新参数,包含帧间隔时间
|
|
439
|
+
*/
|
|
274
440
|
update(e) {
|
|
275
441
|
for (let key in this.armatures) {
|
|
442
|
+
// TODO: 类型
|
|
443
|
+
// @ts-ignore
|
|
276
444
|
this.armatures[key].update(e.deltaTime * 0.001);
|
|
277
445
|
}
|
|
278
446
|
super.update();
|
|
@@ -313,6 +481,7 @@ let SpineSystem = class SpineSystem extends Renderer {
|
|
|
313
481
|
component.addHandler = setTimeout(() => {
|
|
314
482
|
if (!component.destroied) {
|
|
315
483
|
if (count === undefined) {
|
|
484
|
+
// 最大重试次数
|
|
316
485
|
count = MaxRetryCount;
|
|
317
486
|
}
|
|
318
487
|
count--;
|
|
@@ -329,9 +498,11 @@ let SpineSystem = class SpineSystem extends Renderer {
|
|
|
329
498
|
this.remove(changed);
|
|
330
499
|
const container = (_b = (_a = this.renderSystem) === null || _a === void 0 ? void 0 : _a.containerManager) === null || _b === void 0 ? void 0 : _b.getContainer(changed.gameObject.id);
|
|
331
500
|
if (!container) {
|
|
501
|
+
// console.warn('添加spine的container不存在');
|
|
332
502
|
return;
|
|
333
503
|
}
|
|
334
504
|
component.lastResource = component.resource;
|
|
505
|
+
// @ts-ignore
|
|
335
506
|
const armature = new this.pixiSpine.Spine({
|
|
336
507
|
skeletonData: spineData,
|
|
337
508
|
autoUpdate: false,
|
|
@@ -343,23 +514,30 @@ let SpineSystem = class SpineSystem extends Renderer {
|
|
|
343
514
|
armature.y = tran.size.height * tran.origin.y;
|
|
344
515
|
}
|
|
345
516
|
container.addChildAt(armature, 0);
|
|
517
|
+
/** 保证第一帧显示正常 */
|
|
346
518
|
armature.update();
|
|
347
519
|
component.armature = armature;
|
|
520
|
+
// @ts-ignore
|
|
348
521
|
component.emit('loaded', { resource: component.resource });
|
|
349
522
|
armature.state.addListener({
|
|
523
|
+
// @ts-ignore
|
|
350
524
|
start: (track, event) => {
|
|
351
525
|
component.emit('start', { track, name: track.animation.name });
|
|
352
526
|
},
|
|
527
|
+
// @ts-ignore
|
|
353
528
|
complete: (track, event) => {
|
|
354
529
|
component.emit('complete', { track, name: track.animation.name });
|
|
355
530
|
},
|
|
531
|
+
// @ts-ignore
|
|
356
532
|
interrupt: (track, event) => {
|
|
357
533
|
component.emit('interrupt', { track, name: track.animation.name });
|
|
358
534
|
},
|
|
359
|
-
end: (track,
|
|
535
|
+
end: (track, // @ts-ignore
|
|
536
|
+
event) => {
|
|
360
537
|
component.emit('end', { track, name: track.animation.name });
|
|
361
538
|
},
|
|
362
539
|
event: (track, event) => {
|
|
540
|
+
// @ts-ignore
|
|
363
541
|
component.emit('event', track, event);
|
|
364
542
|
},
|
|
365
543
|
});
|
|
@@ -382,9 +560,11 @@ let SpineSystem = class SpineSystem extends Renderer {
|
|
|
382
560
|
}
|
|
383
561
|
if (component.armature) {
|
|
384
562
|
component.armature.destroy({ children: true });
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
563
|
+
if (!component.keepResource) {
|
|
564
|
+
const res = yield resource.getResource(component.lastResource);
|
|
565
|
+
((_d = (_c = res.data) === null || _c === void 0 ? void 0 : _c.image) === null || _d === void 0 ? void 0 : _d.src) || ((_f = (_e = res.data) === null || _e === void 0 ? void 0 : _e.image) === null || _f === void 0 ? void 0 : _f.label);
|
|
566
|
+
releaseSpineData(res);
|
|
567
|
+
}
|
|
388
568
|
}
|
|
389
569
|
component.armature = null;
|
|
390
570
|
delete this.armatures[changed.gameObject.id];
|
|
@@ -392,6 +572,7 @@ let SpineSystem = class SpineSystem extends Renderer {
|
|
|
392
572
|
});
|
|
393
573
|
}
|
|
394
574
|
};
|
|
575
|
+
/** 系统名称 */
|
|
395
576
|
SpineSystem.systemName = 'SpineSystem';
|
|
396
577
|
SpineSystem = __decorate([
|
|
397
578
|
decorators.componentObserver({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eva/spine-base",
|
|
3
|
-
"version": "2.0.1-beta.
|
|
3
|
+
"version": "2.0.1-beta.31",
|
|
4
4
|
"description": "@eva/spine-base",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "dist/spine-base.esm.js",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"homepage": "https://eva.js.org",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@eva/eva.js": "2.0.1-beta.
|
|
22
|
-
"@eva/plugin-renderer": "2.0.1-beta.
|
|
21
|
+
"@eva/eva.js": "2.0.1-beta.31",
|
|
22
|
+
"@eva/plugin-renderer": "2.0.1-beta.31",
|
|
23
23
|
"@eva/inspector-decorator": "^0.0.5",
|
|
24
24
|
"pixi.js": "^8.8.1"
|
|
25
25
|
}
|