@eva/spine-base 2.0.1-beta.9 → 2.0.2-beta.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.
@@ -4,22 +4,22 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var eva_js = require('@eva/eva.js');
6
6
  var pluginRenderer = require('@eva/plugin-renderer');
7
- var inspectorDecorator = require('@eva/inspector-decorator');
8
7
  var pixi_js = require('pixi.js');
8
+ var inspectorDecorator = require('@eva/inspector-decorator');
9
9
 
10
- /*! *****************************************************************************
11
- Copyright (c) Microsoft Corporation. All rights reserved.
12
- Licensed under the Apache License, Version 2.0 (the "License"); you may not use
13
- this file except in compliance with the License. You may obtain a copy of the
14
- License at http://www.apache.org/licenses/LICENSE-2.0
10
+ /******************************************************************************
11
+ Copyright (c) Microsoft Corporation.
15
12
 
16
- THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
18
- WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
19
- MERCHANTABLITY OR NON-INFRINGEMENT.
13
+ Permission to use, copy, modify, and/or distribute this software for any
14
+ purpose with or without fee is hereby granted.
20
15
 
21
- See the Apache Version 2.0 License for specific language governing permissions
22
- and limitations under the License.
16
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
17
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
19
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22
+ PERFORMANCE OF THIS SOFTWARE.
23
23
  ***************************************************************************** */
24
24
 
25
25
  function __decorate(decorators, target, key, desc) {
@@ -30,23 +30,98 @@ function __decorate(decorators, target, key, desc) {
30
30
  }
31
31
 
32
32
  function __awaiter(thisArg, _arguments, P, generator) {
33
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
33
34
  return new (P || (P = Promise))(function (resolve, reject) {
34
35
  function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
35
36
  function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
36
- function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
37
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
37
38
  step((generator = generator.apply(thisArg, _arguments || [])).next());
38
39
  });
39
- }
40
+ }
41
+
42
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
43
+ var e = new Error(message);
44
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
45
+ };
40
46
 
47
+ /**
48
+ * Spine 骨骼动画组件
49
+ *
50
+ * Spine 组件用于播放 Esoteric Software 的 Spine 骨骼动画。
51
+ * 支持骨骼动画播放控制、动画混合、附件替换等高级功能,
52
+ * 适用于角色动画、复杂特效等需要骨骼动画的场景。
53
+ *
54
+ * 主要功能:
55
+ * - 骨骼动画播放和控制
56
+ * - 动画轨道管理(多动画并行)
57
+ * - 动画混合过渡
58
+ * - 骨骼和附件访问
59
+ * - 支持 Spine 3.6 和 3.8 版本
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * // 创建 Spine 动画
64
+ * const character = new GameObject('character');
65
+ * const spine = new Spine({
66
+ * resource: 'heroSpine', // Spine 资源
67
+ * animationName: 'idle', // 默认动画
68
+ * autoPlay: true, // 自动播放
69
+ * scale: 0.5 // 缩放比例
70
+ * });
71
+ * character.addComponent(spine);
72
+ *
73
+ * // 播放动画
74
+ * spine.play('walk', true); // 循环播放 walk 动画
75
+ *
76
+ * // 停止动画
77
+ * spine.stop();
78
+ *
79
+ * // 动画混合
80
+ * spine.setMix('idle', 'walk', 0.3); // 设置过渡时间
81
+ * spine.play('walk');
82
+ *
83
+ * // 添加动画队列
84
+ * spine.play('attack', false); // 播放攻击动画
85
+ * spine.addAnimation('idle', 0, true); // 攻击完成后回到 idle
86
+ *
87
+ * // 替换附件(换装)
88
+ * spine.setAttachment('weapon', 'sword'); // 将武器槽替换为剑
89
+ *
90
+ * // 访问骨骼
91
+ * const headBone = spine.getBone('head');
92
+ * if (headBone) {
93
+ * headBone.rotation = 15; // 旋转头部
94
+ * }
95
+ *
96
+ * // 多轨道动画
97
+ * spine.play('walk', true, 0); // 轨道0:身体动画
98
+ * spine.play('shoot', false, 1); // 轨道1:上半身动画
99
+ * ```
100
+ */
41
101
  class Spine extends eva_js.Component {
42
102
  constructor() {
43
103
  super(...arguments);
104
+ /** Spine 资源名称 */
44
105
  this.resource = '';
106
+ /** 动画缩放比例 */
45
107
  this.scale = 1;
108
+ /** 当前播放的动画名称 */
46
109
  this.animationName = '';
110
+ /** 是否自动播放动画 */
47
111
  this.autoPlay = true;
112
+ /** 是否保留资源(销毁时不释放) */
113
+ this.keepResource = false;
114
+ /** 挂载到插槽的 GameObject 映射(GameObject -> { slot, wrapper }) */
115
+ this._slotGameObjects = new Map();
116
+ /** 等待容器就绪的 slot 挂载请求 */
117
+ this._pendingSlotObjects = [];
118
+ /** 等待执行的动画操作队列 */
48
119
  this.waitExecuteInfos = [];
49
120
  }
121
+ /**
122
+ * 设置骨架实例
123
+ * 当骨架加载完成后自动执行等待队列中的动画操作
124
+ */
50
125
  set armature(val) {
51
126
  this._armature = val;
52
127
  if (!val)
@@ -65,17 +140,36 @@ class Spine extends eva_js.Component {
65
140
  }
66
141
  this.waitExecuteInfos = [];
67
142
  }
143
+ /** 获取骨架实例 */
68
144
  get armature() {
69
145
  return this._armature;
70
146
  }
147
+ /**
148
+ * 初始化组件
149
+ * @param obj - 初始化参数
150
+ * @param obj.resource - Spine 资源名称
151
+ * @param obj.animationName - 默认动画名称
152
+ * @param obj.scale - 缩放比例
153
+ * @param obj.autoPlay - 是否自动播放
154
+ */
71
155
  init(obj) {
72
156
  if (!obj)
73
157
  return;
74
158
  Object.assign(this, obj);
75
159
  }
160
+ /** 组件销毁时调用 */
76
161
  onDestroy() {
77
162
  this.destroied = true;
78
163
  }
164
+ /**
165
+ * 播放指定动画
166
+ *
167
+ * 如果骨架尚未加载完成,动画操作将被加入等待队列。
168
+ *
169
+ * @param name - 动画名称,不指定则使用 animationName 属性
170
+ * @param loopAnimation - 是否循环播放,默认跟随 autoPlay 属性
171
+ * @param track - 动画轨道编号,默认为 0
172
+ */
79
173
  play(name, loopAnimation, track) {
80
174
  try {
81
175
  const loop = loopAnimation !== null && loopAnimation !== void 0 ? loopAnimation : this.autoPlay;
@@ -85,6 +179,12 @@ class Spine extends eva_js.Component {
85
179
  this.waitExecuteInfos.push({
86
180
  playType: true,
87
181
  name,
182
+ /**
183
+ * 在 v1.2.2 之前,Spine 动画的 autoPlay 为 true,动画会循环播放 https://github.com/eva-engine/eva.js/pull/164/files#diff-46e9ae36c04e7a0abedc1e14fd9d1c4e81d8386e9bb851f85971ccdba8957804L131
184
+ * 在 v1.2.2 之前,Spine 动画在每加载完( armature 设置之前)调用 play 是不生效的, 在 v1.2.2 [#164](https://github.com/eva-engine/eva.js/pull/164) 解决了这个问题
185
+ * 解决了不生效的问题以后,加载完成之前调用 play 默认循环是false,导致 autoPlay 下本来循环动画不循环了,和之前表现不一致
186
+ * 为了解决这个问题,在 autoPlay 的情况下,未加载完之前调用 play ,默认循环播放,除非设置不循环参数
187
+ */
88
188
  loop,
89
189
  track,
90
190
  });
@@ -100,6 +200,13 @@ class Spine extends eva_js.Component {
100
200
  console.log(e);
101
201
  }
102
202
  }
203
+ /**
204
+ * 停止指定轨道的动画
205
+ *
206
+ * 如果骨架尚未加载完成,停止操作将被加入等待队列。
207
+ *
208
+ * @param track - 动画轨道编号,默认为 0
209
+ */
103
210
  stop(track) {
104
211
  if (!this.armature) {
105
212
  this.waitExecuteInfos.push({
@@ -113,6 +220,16 @@ class Spine extends eva_js.Component {
113
220
  }
114
221
  this.armature.state.setEmptyAnimation(track, 0);
115
222
  }
223
+ /**
224
+ * 在当前动画之后添加新动画到队列
225
+ *
226
+ * 用于创建动画序列,当前动画播放完毕后自动播放下一个动画。
227
+ *
228
+ * @param name - 动画名称
229
+ * @param delay - 延迟时间(秒)
230
+ * @param loop - 是否循环播放
231
+ * @param track - 动画轨道编号,默认为 0
232
+ */
116
233
  addAnimation(name, delay, loop, track) {
117
234
  try {
118
235
  if (!this.armature) {
@@ -128,12 +245,27 @@ class Spine extends eva_js.Component {
128
245
  console.log(e);
129
246
  }
130
247
  }
248
+ /**
249
+ * 设置两个动画之间的混合过渡时间
250
+ *
251
+ * 当从一个动画切换到另一个动画时,会在指定时间内进行平滑过渡。
252
+ *
253
+ * @param from - 起始动画名称
254
+ * @param to - 目标动画名称
255
+ * @param duration - 过渡时长(秒)
256
+ */
131
257
  setMix(from, to, duration) {
132
258
  if (!this.armature) ;
133
259
  else {
134
260
  this.armature.state.data.setMix(from, to, duration);
135
261
  }
136
262
  }
263
+ /**
264
+ * 获取指定轨道当前播放的动画名称
265
+ *
266
+ * @param track - 动画轨道编号,默认为 0
267
+ * @returns 动画名称,如果未找到则返回 undefined
268
+ */
137
269
  getAnim(track = 0) {
138
270
  try {
139
271
  if (!this.armature) {
@@ -146,25 +278,159 @@ class Spine extends eva_js.Component {
146
278
  console.log(e);
147
279
  }
148
280
  }
281
+ /**
282
+ * 设置默认的动画混合时间
283
+ *
284
+ * 当没有为特定动画对指定混合时间时,将使用此默认值。
285
+ *
286
+ * @param duration - 默认混合时长(秒)
287
+ */
149
288
  setDefaultMix(duration) {
150
289
  if (!this.armature) ;
151
290
  else {
152
291
  this.armature.state.data.defaultMix = duration;
153
292
  }
154
293
  }
294
+ /**
295
+ * 替换指定插槽的附件
296
+ *
297
+ * 用于换装、武器切换等场景。
298
+ *
299
+ * @param slotName - 插槽名称
300
+ * @param attachmentName - 附件名称
301
+ */
155
302
  setAttachment(slotName, attachmentName) {
156
303
  if (!this.armature) {
157
304
  return;
158
305
  }
159
306
  this.armature.skeleton.setAttachment(slotName, attachmentName);
160
307
  }
308
+ /**
309
+ * 获取指定名称的骨骼
310
+ *
311
+ * 可用于直接操作骨骼的位置、旋转、缩放等属性。
312
+ *
313
+ * @param boneName - 骨骼名称
314
+ * @returns 骨骼对象,如果未找到则返回 undefined
315
+ */
161
316
  getBone(boneName) {
162
317
  if (!this.armature) {
163
318
  return;
164
319
  }
165
320
  return this.armature.skeleton.findBone(boneName);
166
321
  }
322
+ /**
323
+ * 将一个 GameObject 挂载到 Spine 的指定插槽上
324
+ *
325
+ * 挂载后 GameObject 会跟随骨骼运动。当 Spine 组件销毁时,
326
+ * 挂载的 GameObject 也会被自动销毁。
327
+ *
328
+ * @param slot - 插槽名称或索引
329
+ * @param gameObject - 要挂载的 GameObject
330
+ * @param options - 可选配置
331
+ * @param options.followAttachmentTimeline - 是否跟随插槽的附件时间线
332
+ */
333
+ addSlotObject(slot, gameObject, options) {
334
+ if (!this.armature) {
335
+ console.warn('Spine armature is not ready, cannot addSlotObject');
336
+ return;
337
+ }
338
+ if (!this._containerManager) {
339
+ console.warn('ContainerManager is not available');
340
+ return;
341
+ }
342
+ const container = this._containerManager.getContainer(gameObject.id);
343
+ if (!container) {
344
+ // 容器尚未就绪,加入 pending 队列,等待下一帧自动处理
345
+ this._pendingSlotObjects.push({ slot, gameObject, options });
346
+ return;
347
+ }
348
+ this._doAddSlotObject(slot, gameObject, container, options);
349
+ }
350
+ _doAddSlotObject(slot, gameObject, container, options) {
351
+ // 创建 wrapper 容器:Spine 骨骼矩阵作用在 wrapper 上,
352
+ // gameObject 的 container 作为子节点,其 transform 作为相对 slot 的局部偏移
353
+ const wrapper = new pixi_js.Container();
354
+ wrapper.addChild(container);
355
+ this.armature.addSlotObject(slot, wrapper, options);
356
+ this._slotGameObjects.set(gameObject, { slot, wrapper });
357
+ // slot object 可能不在 game.gameObjects 中,RendererSystem 不会自动同步 transform
358
+ // 手动同步 gameObject 及其子树的 transform 到 container
359
+ this._syncTransformTree(gameObject);
360
+ }
361
+ /**
362
+ * 递归同步 gameObject 及其子树的 transform 到对应的渲染容器
363
+ */
364
+ _syncTransformTree(gameObject) {
365
+ var _a;
366
+ if (!this._containerManager)
367
+ return;
368
+ this._containerManager.updateTransform({
369
+ name: gameObject.id,
370
+ transform: gameObject.transform,
371
+ });
372
+ if ((_a = gameObject.transform) === null || _a === void 0 ? void 0 : _a.children) {
373
+ for (const childTransform of gameObject.transform.children) {
374
+ if (childTransform.gameObject) {
375
+ this._syncTransformTree(childTransform.gameObject);
376
+ }
377
+ }
378
+ }
379
+ }
380
+ /**
381
+ * 处理等待容器就绪的 slot 挂载请求(由 SpineSystem 每帧调用)
382
+ */
383
+ _flushPendingSlotObjects() {
384
+ if (this._pendingSlotObjects.length === 0)
385
+ return;
386
+ if (!this.armature || !this._containerManager)
387
+ return;
388
+ const still = [];
389
+ for (const pending of this._pendingSlotObjects) {
390
+ const container = this._containerManager.getContainer(pending.gameObject.id);
391
+ if (container) {
392
+ this._doAddSlotObject(pending.slot, pending.gameObject, container, pending.options);
393
+ }
394
+ else {
395
+ still.push(pending);
396
+ }
397
+ }
398
+ this._pendingSlotObjects = still;
399
+ }
400
+ /**
401
+ * 从插槽上移除挂载的 GameObject
402
+ *
403
+ * @param gameObject - 要移除的 GameObject
404
+ */
405
+ removeSlotObject(gameObject) {
406
+ // 从 pending 队列中移除
407
+ this._pendingSlotObjects = this._pendingSlotObjects.filter(p => p.gameObject !== gameObject);
408
+ const entry = this._slotGameObjects.get(gameObject);
409
+ if (entry && this.armature) {
410
+ this.armature.removeSlotObject(entry.wrapper);
411
+ entry.wrapper.destroy({ children: false });
412
+ }
413
+ this._slotGameObjects.delete(gameObject);
414
+ }
415
+ /**
416
+ * 销毁所有挂载到插槽的 GameObject(内部使用)
417
+ */
418
+ _destroySlotGameObjects() {
419
+ for (const [gameObject, entry] of this._slotGameObjects) {
420
+ if (!gameObject.destroyed) {
421
+ // 先从 spine 插槽移除 wrapper,避免 destroy 时重复操作
422
+ if (this.armature) {
423
+ this.armature.removeSlotObject(entry.wrapper);
424
+ }
425
+ entry.wrapper.destroy({ children: false });
426
+ gameObject.destroy();
427
+ }
428
+ }
429
+ this._slotGameObjects.clear();
430
+ this._pendingSlotObjects = [];
431
+ }
167
432
  }
433
+ /** 组件名称 */
168
434
  Spine.componentName = 'Spine';
169
435
  __decorate([
170
436
  inspectorDecorator.type('string')
@@ -177,7 +443,10 @@ __decorate([
177
443
  ], Spine.prototype, "animationName", void 0);
178
444
  __decorate([
179
445
  inspectorDecorator.type('boolean')
180
- ], Spine.prototype, "autoPlay", void 0);
446
+ ], Spine.prototype, "autoPlay", void 0);
447
+ __decorate([
448
+ inspectorDecorator.type('boolean')
449
+ ], Spine.prototype, "keepResource", void 0);
181
450
 
182
451
  let dataMap = {};
183
452
  function createSpineData(name, data, scale, pixiSpine) {
@@ -217,14 +486,6 @@ function releaseSpineData(res, _imageSrc) {
217
486
  data.ref--;
218
487
  setTimeout(() => __awaiter(this, void 0, void 0, function* () {
219
488
  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
489
  eva_js.resource.destroy(resourceName);
229
490
  delete dataMap[resourceName];
230
491
  }
@@ -232,17 +493,41 @@ function releaseSpineData(res, _imageSrc) {
232
493
  }
233
494
 
234
495
  const MaxRetryCount = 20;
496
+ /**
497
+ * Spine 骨骼动画系统
498
+ *
499
+ * SpineSystem 负责管理所有 Spine 组件的骨架创建、动画更新和资源管理。
500
+ * 系统会监听 Spine 组件的变化,自动加载骨骼数据并创建动画实例,
501
+ * 并在每帧更新所有活跃的 Spine 动画。
502
+ *
503
+ * 主要功能:
504
+ * - 骨骼数据加载和缓存
505
+ * - 动画实例创建和销毁
506
+ * - 每帧动画状态更新
507
+ * - WebGL 上下文恢复处理
508
+ * - 资源重试机制
509
+ */
235
510
  let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
236
511
  constructor() {
237
512
  super(...arguments);
513
+ /** 骨架实例映射表(游戏对象 ID -> 骨架容器) */
238
514
  this.armatures = {};
515
+ /** Spine 组件实例映射(游戏对象 ID -> Spine 组件) */
516
+ this._spineComponents = {};
239
517
  }
518
+ /**
519
+ * 初始化系统
520
+ * @param obj - 初始化参数
521
+ * @param obj.pixiSpine - PixiJS Spine 插件实例
522
+ */
240
523
  init({ pixiSpine }) {
241
524
  this.renderSystem = this.game.getSystem(pluginRenderer.RendererSystem);
242
525
  this.renderSystem.rendererManager.register(this);
243
526
  this.pixiSpine = pixiSpine;
244
527
  this.game.canvas.addEventListener('webglcontextrestored', () => {
528
+ // 重建所有spine
245
529
  const objs = this.game.gameObjects;
530
+ // clearCache();
246
531
  let toAdd = [];
247
532
  for (let k in this.armatures) {
248
533
  const id = +k;
@@ -275,10 +560,20 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
275
560
  }, 1000);
276
561
  }, false);
277
562
  }
563
+ /**
564
+ * 每帧更新所有 Spine 动画
565
+ * @param e - 更新参数,包含帧间隔时间
566
+ */
278
567
  update(e) {
279
568
  for (let key in this.armatures) {
569
+ // TODO: 类型
570
+ // @ts-ignore
280
571
  this.armatures[key].update(e.deltaTime * 0.001);
281
572
  }
573
+ // 处理等待容器就绪的 slot 挂载请求
574
+ for (let key in this._spineComponents) {
575
+ this._spineComponents[key]._flushPendingSlotObjects();
576
+ }
282
577
  super.update();
283
578
  }
284
579
  componentChanged(changed) {
@@ -301,7 +596,7 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
301
596
  });
302
597
  }
303
598
  add(changed, count) {
304
- var _a, _b;
599
+ var _a, _b, _c;
305
600
  return __awaiter(this, void 0, void 0, function* () {
306
601
  const component = changed.component;
307
602
  clearTimeout(component.addHandler);
@@ -317,6 +612,7 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
317
612
  component.addHandler = setTimeout(() => {
318
613
  if (!component.destroied) {
319
614
  if (count === undefined) {
615
+ // 最大重试次数
320
616
  count = MaxRetryCount;
321
617
  }
322
618
  count--;
@@ -333,37 +629,48 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
333
629
  this.remove(changed);
334
630
  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
631
  if (!container) {
632
+ // console.warn('添加spine的container不存在');
336
633
  return;
337
634
  }
338
635
  component.lastResource = component.resource;
636
+ // @ts-ignore
339
637
  const armature = new this.pixiSpine.Spine({
340
638
  skeletonData: spineData,
341
639
  autoUpdate: false,
342
640
  });
343
641
  this.armatures[changed.gameObject.id] = armature;
642
+ this._spineComponents[changed.gameObject.id] = component;
344
643
  if (changed.gameObject && changed.gameObject.transform) {
345
644
  const tran = changed.gameObject.transform;
346
645
  armature.x = tran.size.width * tran.origin.x;
347
646
  armature.y = tran.size.height * tran.origin.y;
348
647
  }
349
648
  container.addChildAt(armature, 0);
649
+ /** 保证第一帧显示正常 */
350
650
  armature.update();
651
+ component._containerManager = (_c = this.renderSystem) === null || _c === void 0 ? void 0 : _c.containerManager;
351
652
  component.armature = armature;
653
+ // @ts-ignore
352
654
  component.emit('loaded', { resource: component.resource });
353
655
  armature.state.addListener({
656
+ // @ts-ignore
354
657
  start: (track, event) => {
355
658
  component.emit('start', { track, name: track.animation.name });
356
659
  },
660
+ // @ts-ignore
357
661
  complete: (track, event) => {
358
662
  component.emit('complete', { track, name: track.animation.name });
359
663
  },
664
+ // @ts-ignore
360
665
  interrupt: (track, event) => {
361
666
  component.emit('interrupt', { track, name: track.animation.name });
362
667
  },
363
- end: (track, event) => {
668
+ end: (track, // @ts-ignore
669
+ event) => {
364
670
  component.emit('end', { track, name: track.animation.name });
365
671
  },
366
672
  event: (track, event) => {
673
+ // @ts-ignore
367
674
  component.emit('event', track, event);
368
675
  },
369
676
  });
@@ -385,17 +692,23 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
385
692
  container.removeChild(armature);
386
693
  }
387
694
  if (component.armature) {
695
+ // 销毁所有挂载到插槽的 GameObject
696
+ component._destroySlotGameObjects();
388
697
  component.armature.destroy({ children: true });
389
- const res = yield eva_js.resource.getResource(component.lastResource);
390
- ((_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);
391
- releaseSpineData(res);
698
+ if (!component.keepResource) {
699
+ const res = yield eva_js.resource.getResource(component.lastResource);
700
+ ((_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);
701
+ releaseSpineData(res);
702
+ }
392
703
  }
393
704
  component.armature = null;
394
705
  delete this.armatures[changed.gameObject.id];
706
+ delete this._spineComponents[changed.gameObject.id];
395
707
  if (changed.type === eva_js.OBSERVER_TYPE.CHANGE) ;
396
708
  });
397
709
  }
398
710
  };
711
+ /** 系统名称 */
399
712
  SpineSystem.systemName = 'SpineSystem';
400
713
  SpineSystem = __decorate([
401
714
  eva_js.decorators.componentObserver({
@@ -1,16 +1 @@
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"),r=require("pixi.js");
2
- /*! *****************************************************************************
3
- Copyright (c) Microsoft Corporation. All rights reserved.
4
- Licensed under the Apache License, Version 2.0 (the "License"); you may not use
5
- this file except in compliance with the License. You may obtain a copy of the
6
- License at http://www.apache.org/licenses/LICENSE-2.0
7
-
8
- THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
9
- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
10
- WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
11
- MERCHANTABLITY OR NON-INFRINGEMENT.
12
-
13
- See the Apache Version 2.0 License for specific language governing permissions
14
- and limitations under the License.
15
- ***************************************************************************** */
16
- function i(e,t,a,r){var i,s=arguments.length,n=s<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,a):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(e,t,a,r);else for(var o=e.length-1;o>=0;o--)(i=e[o])&&(n=(s<3?i(n):s>3?i(t,a,n):i(t,a))||n);return s>3&&n&&Object.defineProperty(t,a,n),n}function s(e,t,a,r){return new(a||(a=Promise))((function(i,s){function n(e){try{c(r.next(e))}catch(e){s(e)}}function o(e){try{c(r.throw(e))}catch(e){s(e)}}function c(e){e.done?i(e.value):new a((function(t){t(e.value)})).then(n,o)}c((r=r.apply(e,t||[])).next())}))}class n extends e.Component{constructor(){super(...arguments),this.resource="",this.scale=1,this.animationName="",this.autoPlay=!0,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:r}=e;this.play(t,a,r)}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 r=null!=t?t:this.autoPlay;e&&(this.animationName=e),this.armature?(void 0===a&&(a=0),this.armature.state.setAnimation(a,this.animationName,r)):this.waitExecuteInfos.push({playType:!0,name:e,loop:r,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,r){try{this.armature&&(void 0===r&&(r=0),this.armature.state.addAnimation(r,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);let o={};function c(e,t,a){return s(this,void 0,void 0,(function*(){let r=o[e.name];if(!r)if(e.complete)r=function(e,t,a,r){const i=t.ske,s=t.atlas,n=new r.AtlasAttachmentLoader(s),c=i instanceof Uint8Array?new r.SkeletonBinary(n):new r.SkeletonJson(n);c.scale=a||1;const m={spineData:c.readSkeletonData(i),ref:0,imageSrc:t.image.label};return o[e]=m,m}(e.name,e.data,t,a);else if(!r)return;return r.ref++,r.spineData}))}let m=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 r in this.armatures){const i=+r;for(let r=0;r<t.length;++r){let s=t[r];if(s.id===i){let t=s.getComponent(n);t&&(this.remove({type:e.OBSERVER_TYPE.REMOVE,gameObject:s,component:t,componentName:n.componentName}),a.push({type:e.OBSERVER_TYPE.ADD,gameObject:s,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 s(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 r,i;return s(this,void 0,void 0,(function*(){const s=t.component;clearTimeout(s.addHandler);const n=t.gameObject.id,o=this.increaseAsyncId(n),m=yield e.resource.getResource(s.resource);if(!this.validateAsyncId(n,o))return;const u=yield c(m,s.scale,this.pixiSpine);if(!this.validateAsyncId(n,o))return;if(!u)return void(s.addHandler=setTimeout((()=>{s.destroied||(void 0===a&&(a=20),--a>0?this.add(t,a):console.log("retry exceed max times",s.resource))}),1e3));this.remove(t);const d=null===(i=null===(r=this.renderSystem)||void 0===r?void 0:r.containerManager)||void 0===i?void 0:i.getContainer(t.gameObject.id);if(!d)return;s.lastResource=s.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(),s.armature=l,s.emit("loaded",{resource:s.resource}),l.state.addListener({start:(e,t)=>{s.emit("start",{track:e,name:e.animation.name})},complete:(e,t)=>{s.emit("complete",{track:e,name:e.animation.name})},interrupt:(e,t)=>{s.emit("interrupt",{track:e,name:e.animation.name})},end:(e,t)=>{s.emit("end",{track:e,name:e.animation.name})},event:(e,t)=>{s.emit("event",e,t)}})}))}change(e){this.remove(e),this.add(e)}remove(t){var a,i,n,c,m,u;return s(this,void 0,void 0,(function*(){this.increaseAsyncId(t.gameObject.id);const d=t.component;clearTimeout(d.addHandler);const l=this.armatures[t.gameObject.id],h=null===(i=null===(a=this.renderSystem)||void 0===a?void 0:a.containerManager)||void 0===i?void 0:i.getContainer(t.gameObject.id);if(h&&l&&h.removeChild(l),d.armature){d.armature.destroy({children:!0});const t=yield e.resource.getResource(d.lastResource);(null===(c=null===(n=t.data)||void 0===n?void 0:n.image)||void 0===c?void 0:c.src)||null===(u=null===(m=t.data)||void 0===m?void 0:m.image)||void 0===u||u.label,function(t){const a=t.name,i=o[a];i&&(i.ref--,setTimeout((()=>s(this,void 0,void 0,(function*(){if(i.ref<=0){yield r.Assets.unload([t.src.image.url,t.src.atlas.url,t.src.ske.url]);const i=r.Assets.resolver;delete i._assetMap[t.src.image.url],delete i._assetMap[t.src.atlas.url],delete i._assetMap[t.src.ske.url],delete i._resolverHash[t.src.image.url],delete i._resolverHash[t.src.atlas.url],delete i._resolverHash[t.src.ske.url],e.resource.destroy(a),delete o[a]}}))),100))}(t)}d.armature=null,delete this.armatures[t.gameObject.id],t.type,e.OBSERVER_TYPE.CHANGE}))}};m.systemName="SpineSystem",m=i([e.decorators.componentObserver({Spine:["resource"]})],m);var u=m;e.resource.registerResourceType("SPINE"),exports.Spine=n,exports.SpineSystem=u;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@eva/eva.js"),t=require("@eva/plugin-renderer"),r=require("pixi.js"),n=require("@eva/inspector-decorator");function a(e,t,r,n){var a,i=arguments.length,o=i<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,r):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,r,n);else for(var s=e.length-1;s>=0;s--)(a=e[s])&&(o=(i<3?a(o):i>3?a(t,r,o):a(t,r))||o);return i>3&&o&&Object.defineProperty(t,r,o),o}function i(e,t,r,n){return new(r||(r=Promise))(function(a,i){function o(e){try{c(n.next(e))}catch(e){i(e)}}function s(e){try{c(n.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?a(e.value):(t=e.value,t instanceof r?t:new r(function(e){e(t)})).then(o,s)}c((n=n.apply(e,t||[])).next())})}"function"==typeof SuppressedError&&SuppressedError;class o extends e.Component{constructor(){super(...arguments),this.resource="",this.scale=1,this.animationName="",this.autoPlay=!0,this.keepResource=!1,this._slotGameObjects=new Map,this._pendingSlotObjects=[],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:r,track:n}=e;this.play(t,r,n)}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,r){try{const n=null!=t?t:this.autoPlay;e&&(this.animationName=e),this.armature?(void 0===r&&(r=0),this.armature.state.setAnimation(r,this.animationName,n)):this.waitExecuteInfos.push({playType:!0,name:e,loop:n,track:r})}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,r,n){try{this.armature&&(void 0===n&&(n=0),this.armature.state.addAnimation(n,e,r,t))}catch(e){console.log(e)}}setMix(e,t,r){this.armature&&this.armature.state.data.setMix(e,t,r)}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)}addSlotObject(e,t,r){if(!this.armature)return void console.warn("Spine armature is not ready, cannot addSlotObject");if(!this._containerManager)return void console.warn("ContainerManager is not available");const n=this._containerManager.getContainer(t.id);n?this._doAddSlotObject(e,t,n,r):this._pendingSlotObjects.push({slot:e,gameObject:t,options:r})}_doAddSlotObject(e,t,n,a){const i=new r.Container;i.addChild(n),this.armature.addSlotObject(e,i,a),this._slotGameObjects.set(t,{slot:e,wrapper:i}),this._syncTransformTree(t)}_syncTransformTree(e){var t;if(this._containerManager&&(this._containerManager.updateTransform({name:e.id,transform:e.transform}),null===(t=e.transform)||void 0===t?void 0:t.children))for(const t of e.transform.children)t.gameObject&&this._syncTransformTree(t.gameObject)}_flushPendingSlotObjects(){if(0===this._pendingSlotObjects.length)return;if(!this.armature||!this._containerManager)return;const e=[];for(const t of this._pendingSlotObjects){const r=this._containerManager.getContainer(t.gameObject.id);r?this._doAddSlotObject(t.slot,t.gameObject,r,t.options):e.push(t)}this._pendingSlotObjects=e}removeSlotObject(e){this._pendingSlotObjects=this._pendingSlotObjects.filter(t=>t.gameObject!==e);const t=this._slotGameObjects.get(e);t&&this.armature&&(this.armature.removeSlotObject(t.wrapper),t.wrapper.destroy({children:!1})),this._slotGameObjects.delete(e)}_destroySlotGameObjects(){for(const[e,t]of this._slotGameObjects)e.destroyed||(this.armature&&this.armature.removeSlotObject(t.wrapper),t.wrapper.destroy({children:!1}),e.destroy());this._slotGameObjects.clear(),this._pendingSlotObjects=[]}}o.componentName="Spine",a([n.type("string")],o.prototype,"resource",void 0),a([n.type("number")],o.prototype,"scale",void 0),a([n.type("string")],o.prototype,"animationName",void 0),a([n.type("boolean")],o.prototype,"autoPlay",void 0),a([n.type("boolean")],o.prototype,"keepResource",void 0);let s={};function c(e,t,r){return i(this,void 0,void 0,function*(){let n=s[e.name];if(!n)if(e.complete)n=function(e,t,r,n){const a=t.ske,i=t.atlas,o=new n.AtlasAttachmentLoader(i),c=a instanceof Uint8Array?new n.SkeletonBinary(o):new n.SkeletonJson(o);c.scale=r||1;const d={spineData:c.readSkeletonData(a),ref:0,imageSrc:t.image.label};return s[e]=d,d}(e.name,e.data,t,r);else if(!n)return;return n.ref++,n.spineData})}let d=class extends t.Renderer{constructor(){super(...arguments),this.armatures={},this._spineComponents={}}init({pixiSpine:r}){this.renderSystem=this.game.getSystem(t.RendererSystem),this.renderSystem.rendererManager.register(this),this.pixiSpine=r,this.game.canvas.addEventListener("webglcontextrestored",()=>{const t=this.game.gameObjects;let r=[];for(let n in this.armatures){const a=+n;for(let n=0;n<t.length;++n){let i=t[n];if(i.id===a){let t=i.getComponent(o);t&&(this.remove({type:e.OBSERVER_TYPE.REMOVE,gameObject:i,component:t,componentName:o.componentName}),r.push({type:e.OBSERVER_TYPE.ADD,gameObject:i,component:t,componentName:o.componentName}));break}}}setTimeout(()=>{r.forEach(e=>{this.add(e)})},1e3)},!1)}update(e){for(let t in this.armatures)this.armatures[t].update(.001*e.deltaTime);for(let e in this._spineComponents)this._spineComponents[e]._flushPendingSlotObjects();super.update()}componentChanged(t){return i(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,r){var n,a,o;return i(this,void 0,void 0,function*(){const i=t.component;clearTimeout(i.addHandler);const s=t.gameObject.id,d=this.increaseAsyncId(s),m=yield e.resource.getResource(i.resource);if(!this.validateAsyncId(s,d))return;const l=yield c(m,i.scale,this.pixiSpine);if(!this.validateAsyncId(s,d))return;if(!l)return void(i.addHandler=setTimeout(()=>{i.destroied||(void 0===r&&(r=20),--r>0?this.add(t,r):console.log("retry exceed max times",i.resource))},1e3));this.remove(t);const u=null===(a=null===(n=this.renderSystem)||void 0===n?void 0:n.containerManager)||void 0===a?void 0:a.getContainer(t.gameObject.id);if(!u)return;i.lastResource=i.resource;const h=new this.pixiSpine.Spine({skeletonData:l,autoUpdate:!1});if(this.armatures[t.gameObject.id]=h,this._spineComponents[t.gameObject.id]=i,t.gameObject&&t.gameObject.transform){const e=t.gameObject.transform;h.x=e.size.width*e.origin.x,h.y=e.size.height*e.origin.y}u.addChildAt(h,0),h.update(),i._containerManager=null===(o=this.renderSystem)||void 0===o?void 0:o.containerManager,i.armature=h,i.emit("loaded",{resource:i.resource}),h.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(t){var r,n,a,o,c,d;return i(this,void 0,void 0,function*(){this.increaseAsyncId(t.gameObject.id);const m=t.component;clearTimeout(m.addHandler);const l=this.armatures[t.gameObject.id],u=null===(n=null===(r=this.renderSystem)||void 0===r?void 0:r.containerManager)||void 0===n?void 0:n.getContainer(t.gameObject.id);if(u&&l&&u.removeChild(l),m.armature&&(m._destroySlotGameObjects(),m.armature.destroy({children:!0}),!m.keepResource)){const t=yield e.resource.getResource(m.lastResource);(null===(o=null===(a=t.data)||void 0===a?void 0:a.image)||void 0===o?void 0:o.src)||null===(d=null===(c=t.data)||void 0===c?void 0:c.image)||void 0===d||d.label,function(t){const r=t.name,n=s[r];n&&(n.ref--,setTimeout(()=>i(this,void 0,void 0,function*(){n.ref<=0&&(e.resource.destroy(r),delete s[r])}),100))}(t)}m.armature=null,delete this.armatures[t.gameObject.id],delete this._spineComponents[t.gameObject.id],t.type,e.OBSERVER_TYPE.CHANGE})}};d.systemName="SpineSystem",d=a([e.decorators.componentObserver({Spine:["resource"]})],d);var m=d;e.resource.registerResourceType("SPINE"),exports.Spine=o,exports.SpineSystem=m;