@eva/spine-base 2.1.0-beta.11 → 2.1.0-beta.12

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/README.md CHANGED
@@ -4,3 +4,19 @@ More Introduction
4
4
 
5
5
  - [EN](https://eva.js.org)
6
6
  - [中文](https://eva-engine.gitee.io)
7
+
8
+ ## Slot object readiness
9
+
10
+ `Spine#addSlotObject(slot, gameObject, options)` is safe to call before the
11
+ Spine armature, renderer container manager, or target GameObject container is
12
+ ready. The component keeps the latest request for each GameObject and mounts it
13
+ once all three dependencies are available.
14
+
15
+ - Repeating the call for one GameObject replaces its previous pending or mounted
16
+ slot request.
17
+ - `removeSlotObject(gameObject)` cancels both pending and mounted requests.
18
+ - Destroying the Spine component destroys pending and mounted slot GameObjects.
19
+ - Invalid slots are discarded with a warning after skeleton metadata becomes
20
+ available.
21
+
22
+ Do not force a resource reload merely to retry a slot mount.
@@ -182,6 +182,7 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
182
182
  }
183
183
  }
184
184
  this.waitExecuteInfos = [];
185
+ this._flushPendingSlotObjects();
185
186
  }
186
187
  get armature() {
187
188
  return this._armature;
@@ -212,8 +213,8 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
212
213
  }
213
214
  destroy() {
214
215
  this.onDestroy();
216
+ this._destroySlotGameObjects();
215
217
  if (this.armature && !this.armature.destroyed) {
216
- this._destroySlotGameObjects();
217
218
  this.armature.destroy({
218
219
  children: true
219
220
  });
@@ -324,24 +325,22 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
324
325
  return this.armature.skeleton.findBone(boneName);
325
326
  }
326
327
  addSlotObject(slot, gameObject, options) {
327
- if (!this.armature) {
328
- console.warn('Spine armature is not ready, cannot addSlotObject');
329
- return;
330
- }
331
- if (!this._containerManager) {
332
- console.warn('ContainerManager is not available');
328
+ if (this.destroied) {
329
+ console.warn('Spine component has been destroyed, cannot addSlotObject');
333
330
  return;
334
331
  }
335
- const container = this._containerManager.getContainer(gameObject.id);
336
- if (!container) {
337
- this._pendingSlotObjects.push({
338
- slot,
339
- gameObject,
340
- options
341
- });
332
+ if (!gameObject || gameObject.destroyed) {
333
+ console.warn('Cannot add a destroyed or missing GameObject to a Spine slot');
342
334
  return;
343
335
  }
344
- this._doAddSlotObject(slot, gameObject, container, options);
336
+ this._pendingSlotObjects = this._pendingSlotObjects.filter(pending => pending.gameObject !== gameObject);
337
+ this._detachSlotObject(gameObject);
338
+ this._pendingSlotObjects.push({
339
+ slot,
340
+ gameObject,
341
+ options
342
+ });
343
+ this._flushPendingSlotObjects();
345
344
  }
346
345
  _doAddSlotObject(slot, gameObject, container, options) {
347
346
  const wrapper = new pixi_js.Container();
@@ -353,6 +352,31 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
353
352
  });
354
353
  this._syncTransformTree(gameObject);
355
354
  }
355
+ _isSlotAvailable(slot) {
356
+ var _a, _b, _c, _d;
357
+ if (typeof slot === 'number') {
358
+ if (!Number.isInteger(slot) || slot < 0) return false;
359
+ const slots = (_b = (_a = this.armature) === null || _a === void 0 ? void 0 : _a.skeleton) === null || _b === void 0 ? void 0 : _b.slots;
360
+ return !Array.isArray(slots) || slot < slots.length;
361
+ }
362
+ if (typeof slot !== 'string' || slot.length === 0) return false;
363
+ const findSlot = (_d = (_c = this.armature) === null || _c === void 0 ? void 0 : _c.skeleton) === null || _d === void 0 ? void 0 : _d.findSlot;
364
+ return typeof findSlot !== 'function' || Boolean(findSlot.call(this.armature.skeleton, slot));
365
+ }
366
+ _detachSlotObject(gameObject) {
367
+ const entry = this._slotGameObjects.get(gameObject);
368
+ if (!entry) return;
369
+ if (this.armature && !this.armature.destroyed) {
370
+ this.armature.removeSlotObject(entry.wrapper);
371
+ }
372
+ for (const child of [...entry.wrapper.children]) {
373
+ entry.wrapper.removeChild(child);
374
+ }
375
+ entry.wrapper.destroy({
376
+ children: false
377
+ });
378
+ this._slotGameObjects.delete(gameObject);
379
+ }
356
380
  _syncTransformTree(gameObject) {
357
381
  var _a;
358
382
  if (!this._containerManager) return;
@@ -369,39 +393,39 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
369
393
  }
370
394
  }
371
395
  _flushPendingSlotObjects() {
372
- if (this._pendingSlotObjects.length === 0) return;
396
+ if (this.destroied || this._pendingSlotObjects.length === 0) return;
373
397
  if (!this.armature || !this._containerManager) return;
374
398
  const still = [];
375
- for (const pending of this._pendingSlotObjects) {
399
+ const pendingSlotObjects = this._pendingSlotObjects;
400
+ this._pendingSlotObjects = [];
401
+ for (const pending of pendingSlotObjects) {
402
+ if (pending.gameObject.destroyed) continue;
403
+ if (!this._isSlotAvailable(pending.slot)) {
404
+ console.warn(`Spine slot "${pending.slot}" does not exist; pending slot object was discarded`);
405
+ continue;
406
+ }
376
407
  const container = this._containerManager.getContainer(pending.gameObject.id);
377
408
  if (container) {
378
- this._doAddSlotObject(pending.slot, pending.gameObject, container, pending.options);
409
+ try {
410
+ this._doAddSlotObject(pending.slot, pending.gameObject, container, pending.options);
411
+ } catch (error) {
412
+ console.warn(`Failed to add GameObject to Spine slot "${pending.slot}"`, error);
413
+ }
379
414
  } else {
380
415
  still.push(pending);
381
416
  }
382
417
  }
383
- this._pendingSlotObjects = still;
418
+ this._pendingSlotObjects.push(...still);
384
419
  }
385
420
  removeSlotObject(gameObject) {
386
421
  this._pendingSlotObjects = this._pendingSlotObjects.filter(p => p.gameObject !== gameObject);
387
- const entry = this._slotGameObjects.get(gameObject);
388
- if (entry && this.armature) {
389
- this.armature.removeSlotObject(entry.wrapper);
390
- entry.wrapper.destroy({
391
- children: false
392
- });
393
- }
394
- this._slotGameObjects.delete(gameObject);
422
+ this._detachSlotObject(gameObject);
395
423
  }
396
424
  _destroySlotGameObjects() {
397
- for (const [gameObject, entry] of this._slotGameObjects) {
425
+ const gameObjects = new Set([...this._slotGameObjects.keys(), ...this._pendingSlotObjects.map(pending => pending.gameObject)]);
426
+ for (const gameObject of gameObjects) {
427
+ this._detachSlotObject(gameObject);
398
428
  if (!gameObject.destroyed) {
399
- if (this.armature) {
400
- this.armature.removeSlotObject(entry.wrapper);
401
- }
402
- entry.wrapper.destroy({
403
- children: false
404
- });
405
429
  gameObject.destroy();
406
430
  }
407
431
  }
@@ -453,17 +477,25 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
453
477
  return data.spineData;
454
478
  });
455
479
  }
456
- function releaseSpineData(res, _imageSrc) {
480
+ function releaseSpineData(res, _imageSrc, destroyResource = true) {
457
481
  const resourceName = res.name;
458
482
  const data = dataMap[resourceName];
459
483
  if (!data) {
460
484
  return;
461
485
  }
462
486
  data.ref--;
487
+ if (data.ref > 0) {
488
+ return;
489
+ }
490
+ if (dataMap[resourceName] === data) {
491
+ delete dataMap[resourceName];
492
+ }
493
+ if (!destroyResource) {
494
+ return;
495
+ }
463
496
  setTimeout(() => __awaiter(this, void 0, void 0, function* () {
464
- if (data.ref <= 0) {
497
+ if (data.ref <= 0 && !dataMap[resourceName]) {
465
498
  eva_js.resource.destroy(resourceName);
466
- delete dataMap[resourceName];
467
499
  }
468
500
  }), 100);
469
501
  }
@@ -520,6 +552,7 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
520
552
  const armature = this.armatures[key];
521
553
  const component = this._spineComponents[key];
522
554
  if (!armature || armature.destroyed || (component === null || component === void 0 ? void 0 : component.destroied)) {
555
+ this.releaseComponentResource(component);
523
556
  delete this.armatures[key];
524
557
  delete this._spineComponents[key];
525
558
  continue;
@@ -529,6 +562,7 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
529
562
  for (let key in this._spineComponents) {
530
563
  const component = this._spineComponents[key];
531
564
  if (!component || component.destroied) {
565
+ this.releaseComponentResource(component);
532
566
  delete this._spineComponents[key];
533
567
  continue;
534
568
  }
@@ -639,7 +673,7 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
639
673
  this.add(changed);
640
674
  }
641
675
  remove(changed) {
642
- var _a, _b, _c, _d, _e, _f;
676
+ var _a, _b;
643
677
  return __awaiter(this, void 0, void 0, function* () {
644
678
  const gameObjectId = changed.gameObject.id;
645
679
  this.increaseAsyncId(gameObjectId);
@@ -661,19 +695,40 @@ var _EVA_IIFE_spineBase = function (exports, eva_js, pluginRenderer, pixi_js) {
661
695
  children: true
662
696
  });
663
697
  }
664
- if (!component.keepResource && component.lastResource) {
665
- try {
666
- const res = yield eva_js.resource.getResource(component.lastResource);
667
- const imageSrc = ((_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);
668
- releaseSpineData(res, imageSrc);
669
- } catch (error) {
670
- console.warn('Failed to release Spine resource', component.lastResource, error);
671
- }
672
- }
673
698
  }
699
+ this.releaseComponentResource(component);
674
700
  if (changed.type === eva_js.OBSERVER_TYPE.CHANGE) ;
675
701
  });
676
702
  }
703
+ onDestroy() {
704
+ for (const key of Object.keys(this._spineComponents)) {
705
+ const component = this._spineComponents[+key];
706
+ const armature = this.armatures[+key];
707
+ component === null || component === void 0 ? void 0 : component._destroySlotGameObjects();
708
+ if (armature && !armature.destroyed) {
709
+ armature.destroy({
710
+ children: true
711
+ });
712
+ }
713
+ if (component) {
714
+ component.armature = null;
715
+ }
716
+ this.releaseComponentResource(component);
717
+ }
718
+ this.armatures = {};
719
+ this._spineComponents = {};
720
+ this.asyncIdMap = {};
721
+ }
722
+ releaseComponentResource(component) {
723
+ const resourceName = component === null || component === void 0 ? void 0 : component.lastResource;
724
+ if (!component || !resourceName) {
725
+ return;
726
+ }
727
+ component.lastResource = '';
728
+ releaseSpineData({
729
+ name: resourceName
730
+ }, '', !component.keepResource);
731
+ }
677
732
  };
678
733
  SpineSystem.systemName = 'SpineSystem';
679
734
  SpineSystem = __decorate([eva_js.decorators.componentObserver({
@@ -1 +1 @@
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 i in n)({}).hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},_extends.apply(null,arguments)}globalThis.EVA=globalThis.EVA||{},globalThis.EVA.plugin=globalThis.EVA.plugin||{};var _EVA_IIFE_spineBase=function(e,t,n,i){"use strict";function r(e,t,n,i){var r,s=arguments.length,o=s<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(s<3?r(o):s>3?r(t,n,o):r(t,n))||o);return s>3&&o&&Object.defineProperty(t,n,o),o}function s(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function o(e,t,n,i){return new(n||(n=Promise))(function(r,s){function o(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(o,a)}c((i=i.apply(e,t||[])).next())})}"function"==typeof SuppressedError&&SuppressedError,"function"==typeof SuppressedError&&SuppressedError;class a extends Error{constructor(){super("Symbol keys are not supported yet!"),Object.setPrototypeOf(this,new.target.prototype)}}const c="IDE_PROPERTY_METADATA";function l(e,t,n,i){let r=Reflect.getMetadata("design:type",e,t),s=r===Array;const o=function(e){return e===String?"string":e===Number?"number":e===Boolean?"boolean":"unknown"}(r);if("unknown"!==o&&(r=o),i){const e=i();Array.isArray(e)?(s=!0,r=e[0]):r=e}const a=Reflect.getMetadata(c,e.constructor)||{},l=_extends(_extends(_extends({},a[t]||{}),{type:r,isArray:s}),n);a[t]=l,Reflect.defineMetadata(c,a,e.constructor);!function(e,t,n){const i=e.constructor,r=i.IDEProps||{};r[t]=_extends(_extends({key:t},r[t]),n),i.IDEProps=r}(e,t,function(e,t){var n={};for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&t.indexOf(i)<0&&(n[i]=e[i]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var r=0;for(i=Object.getOwnPropertySymbols(e);r<i.length;r++)t.indexOf(i[r])<0&&Object.prototype.propertyIsEnumerable.call(e,i[r])&&(n[i[r]]=e[i[r]])}return n}(l,["isArray"]))}function d(e){return t={type:e},(e,i)=>{if("symbol"==typeof i)throw new a;const{options:r,returnTypeFunc:s}=function(e,t){return"function"==typeof e?{returnTypeFunc:e,options:t||{}}:{options:e||{}}}(t,n);l(e,i,r,s)};var t,n}var u;!function(e){e[e.Edit=2]="Edit",e[e.Game=4]="Game",e[e.All=6]="All"}(u||(u={}));class m extends t.Component{constructor(e){super(e),this.resource="",this.scale=1,this.animationName="",this.autoPlay=!0,this.keepResource=!1,this.timeScale=1,this.skin="",this._slotGameObjects=new Map,this._pendingSlotObjects=[],this.waitExecuteInfos=[],this.listenerBound=!1,this.paused=!1,this.init(e)}set armature(e){if(this._armature=e,e){this.applyTimeScale(),this.skin&&this.setSkin(this.skin),this.autoPlay&&this.play(this.animationName);for(const e of this.waitExecuteInfos)if(e.playType){const{name:t,loop:n,track:i}=e;this.play(t,n,i)}else this.stop(e.track);this.waitExecuteInfos=[]}}get armature(){return this._armature}init(e){e&&_extends(this,e),this.listenerBound||(this.listenerBound=!0,this.on("start",e=>{var t;null===(t=this.onStart)||void 0===t||t.call(this,e)}),this.on("complete",e=>{var t;null===(t=this.onComplete)||void 0===t||t.call(this,e)}),this.on("event",(e,t)=>{var n;null===(n=this.onEvent)||void 0===n||n.call(this,e,t)}))}onDestroy(){this.destroied=!0}load(){return this.armature}destroy(){this.onDestroy(),this.armature&&!this.armature.destroyed&&(this._destroySlotGameObjects(),this.armature.destroy({children:!0})),this.armature=null}pause(){this.paused=!0,this.applyTimeScale()}resume(){this.paused=!1,this.applyTimeScale()}setSkin(e){var t,n,i,r,s;if(this.skin=e,null===(t=this.armature)||void 0===t?void 0:t.skeleton){if(this.armature.skeleton.setSkinByName)this.armature.skeleton.setSkinByName(e);else if(this.armature.skeleton.setSkin){const t=(null===(i=null===(n=this.armature.skeleton.data)||void 0===n?void 0:n.findSkin)||void 0===i?void 0:i.call(n,e))||e;this.armature.skeleton.setSkin(t)}null===(s=(r=this.armature.skeleton).setSlotsToSetupPose)||void 0===s||s.call(r)}}play(e,t,n){try{const i=null!=t?t:this.autoPlay;e&&(this.animationName=e),this.armature?(void 0===n&&(n=0),this.applyTimeScale(),this.armature.state.setAnimation(n,this.animationName,i)):this.waitExecuteInfos.push({playType:!0,name:e,loop:i,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})}applyTimeScale(){var e;(null===(e=this.armature)||void 0===e?void 0:e.state)&&(this.armature.state.timeScale=this.paused?0:this.timeScale)}addAnimation(e,t,n,i){try{this.armature&&(void 0===i&&(i=0),this.armature.state.addAnimation(i,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)}addSlotObject(e,t,n){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 i=this._containerManager.getContainer(t.id);i?this._doAddSlotObject(e,t,i,n):this._pendingSlotObjects.push({slot:e,gameObject:t,options:n})}_doAddSlotObject(e,t,n,r){const s=new i.Container;s.addChild(n),this.armature.addSlotObject(e,s,r),this._slotGameObjects.set(t,{slot:e,wrapper:s}),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 n=this._containerManager.getContainer(t.gameObject.id);n?this._doAddSlotObject(t.slot,t.gameObject,n,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=[]}}m.componentName="Spine",r([d("string"),s("design:type",String)],m.prototype,"resource",void 0),r([d("number"),s("design:type",Number)],m.prototype,"scale",void 0),r([d("string"),s("design:type",String)],m.prototype,"animationName",void 0),r([d("boolean"),s("design:type",Boolean)],m.prototype,"autoPlay",void 0),r([d("boolean"),s("design:type",Boolean)],m.prototype,"keepResource",void 0),r([d("number"),s("design:type",Number)],m.prototype,"timeScale",void 0),r([d("string"),s("design:type",String)],m.prototype,"skin",void 0);let p={};function h(e,t,n){return o(this,void 0,void 0,function*(){let i=p[e.name];if(!i)if(e.complete)i=function(e,t,n,i){const r=t.ske,s=t.atlas,o=new i.AtlasAttachmentLoader(s),a=r instanceof Uint8Array?new i.SkeletonBinary(o):new i.SkeletonJson(o);a.scale=n||1;const c={spineData:a.readSkeletonData(r),ref:0,imageSrc:(l=t.image,l?"string"==typeof l?l:l.label||(null===(d=l.source)||void 0===d?void 0:d.label)||(null===(m=null===(u=l.source)||void 0===u?void 0:u.resource)||void 0===m?void 0:m.src)||(null===(h=l.source)||void 0===h?void 0:h._sourceOrigin)||l.src||(null===(y=l.baseTexture)||void 0===y?void 0:y.cacheId)||"":"")};var l,d,u,m,h,y;return p[e]=c,c}(e.name,e.data,t,n);else if(!i)return;return i.ref++,i.spineData})}let y=class extends n.Renderer{constructor(){super(...arguments),this.armatures={},this._spineComponents={}}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 i in this.armatures){const r=+i;for(let i=0;i<e.length;++i){let s=e[i];if(s.id===r){let e=s.getComponent(m);e&&(this.remove({type:t.OBSERVER_TYPE.REMOVE,gameObject:s,component:e,componentName:m.componentName}),n.push({type:t.OBSERVER_TYPE.ADD,gameObject:s,component:e,componentName:m.componentName}));break}}}setTimeout(()=>{n.forEach(e=>{this.add(e)})},1e3)},!1)}update(e){super.update();for(let t in this.armatures){const n=this.armatures[t],i=this._spineComponents[t];!n||n.destroyed||(null==i?void 0:i.destroied)?(delete this.armatures[t],delete this._spineComponents[t]):n.update(.001*e.deltaTime)}for(let e in this._spineComponents){const t=this._spineComponents[e];t&&!t.destroied?t._flushPendingSlotObjects():delete this._spineComponents[e]}}componentChanged(e){return o(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 i,r,s;return o(this,void 0,void 0,function*(){const o=e.component;clearTimeout(o.addHandler);const a=e.gameObject.id,c=this.increaseAsyncId(a),l=yield t.resource.getResource(o.resource);if(!this.validateAsyncId(a,c)||o.destroied||e.gameObject.destroyed)return;const d=yield h(l,o.scale,this.pixiSpine);if(!this.validateAsyncId(a,c)||o.destroied||e.gameObject.destroyed)return;if(!d)return void(o.addHandler=setTimeout(()=>{o.destroied||e.gameObject.destroyed||(void 0===n&&(n=20),--n>0?this.add(e,n):console.log("retry exceed max times",o.resource))},1e3));this.remove(e);const u=null===(r=null===(i=this.renderSystem)||void 0===i?void 0:i.containerManager)||void 0===r?void 0:r.getContainer(e.gameObject.id);if(!u||o.destroied||e.gameObject.destroyed)return;o.lastResource=o.resource;const m=new this.pixiSpine.Spine({skeletonData:d,autoUpdate:!1});if(this.armatures[e.gameObject.id]=m,this._spineComponents[e.gameObject.id]=o,e.gameObject&&e.gameObject.transform){const t=e.gameObject.transform;m.x=t.size.width*t.origin.x,m.y=t.size.height*t.origin.y}u.addChildAt(m,0),m.update(),o._containerManager=null===(s=this.renderSystem)||void 0===s?void 0:s.containerManager,o.armature=m,o.emit("loaded",{resource:o.resource}),m.state.addListener({start:(e,t)=>{o.emit("start",{track:e,name:e.animation.name})},complete:(e,t)=>{o.emit("complete",{track:e,name:e.animation.name})},interrupt:(e,t)=>{o.emit("interrupt",{track:e,name:e.animation.name})},end:(e,t)=>{o.emit("end",{track:e,name:e.animation.name})},event:(e,t)=>{o.emit("event",e,t)}})})}change(e){this.remove(e),this.add(e)}remove(e){var n,i,r,s,a,c;return o(this,void 0,void 0,function*(){const l=e.gameObject.id;this.increaseAsyncId(l);const d=e.component;clearTimeout(d.addHandler);const u=this.armatures[l],m=null===(i=null===(n=this.renderSystem)||void 0===n?void 0:n.containerManager)||void 0===i?void 0:i.getContainer(l);m&&u&&m.removeChild(u);const h=d.armature;if(d.armature=null,delete this.armatures[l],delete this._spineComponents[l],h&&(d._destroySlotGameObjects(),h.destroyed||h.destroy({children:!0}),!d.keepResource&&d.lastResource))try{const e=yield t.resource.getResource(d.lastResource);(null===(s=null===(r=e.data)||void 0===r?void 0:r.image)||void 0===s?void 0:s.src)||(null===(c=null===(a=e.data)||void 0===a?void 0:a.image)||void 0===c||c.label);!function(e){const n=e.name,i=p[n];i&&(i.ref--,setTimeout(()=>o(this,void 0,void 0,function*(){i.ref<=0&&(t.resource.destroy(n),delete p[n])}),100))}(e)}catch(e){console.warn("Failed to release Spine resource",d.lastResource,e)}e.type,t.OBSERVER_TYPE.CHANGE})}};y.systemName="SpineSystem",y=r([t.decorators.componentObserver({Spine:["resource"]})],y);var f=y;return t.resource.registerResourceType("SPINE"),e.Spine=m,e.SpineSystem=f,Object.defineProperty(e,"__esModule",{value:!0}),e}({},EVA,EVA.plugin.renderer,PIXI);globalThis.EVA.plugin.spineBase=globalThis.EVA.plugin.spineBase||_EVA_IIFE_spineBase;
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 s in n)({}).hasOwnProperty.call(n,s)&&(e[s]=n[s])}return e},_extends.apply(null,arguments)}globalThis.EVA=globalThis.EVA||{},globalThis.EVA.plugin=globalThis.EVA.plugin||{};var _EVA_IIFE_spineBase=function(e,t,n,s){"use strict";function o(e,t,n,s){var o,i=arguments.length,r=i<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,n):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,n,s);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(r=(i<3?o(r):i>3?o(t,n,r):o(t,n))||r);return i>3&&r&&Object.defineProperty(t,n,r),r}function i(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function r(e,t,n,s){return new(n||(n=Promise))(function(o,i){function r(e){try{l(s.next(e))}catch(e){i(e)}}function a(e){try{l(s.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}l((s=s.apply(e,t||[])).next())})}"function"==typeof SuppressedError&&SuppressedError,"function"==typeof SuppressedError&&SuppressedError;class a extends Error{constructor(){super("Symbol keys are not supported yet!"),Object.setPrototypeOf(this,new.target.prototype)}}const l="IDE_PROPERTY_METADATA";function c(e,t,n,s){let o=Reflect.getMetadata("design:type",e,t),i=o===Array;const r=function(e){return e===String?"string":e===Number?"number":e===Boolean?"boolean":"unknown"}(o);if("unknown"!==r&&(o=r),s){const e=s();Array.isArray(e)?(i=!0,o=e[0]):o=e}const a=Reflect.getMetadata(l,e.constructor)||{},c=_extends(_extends(_extends({},a[t]||{}),{type:o,isArray:i}),n);a[t]=c,Reflect.defineMetadata(l,a,e.constructor);!function(e,t,n){const s=e.constructor,o=s.IDEProps||{};o[t]=_extends(_extends({key:t},o[t]),n),s.IDEProps=o}(e,t,function(e,t){var n={};for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.indexOf(s)<0&&(n[s]=e[s]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var o=0;for(s=Object.getOwnPropertySymbols(e);o<s.length;o++)t.indexOf(s[o])<0&&Object.prototype.propertyIsEnumerable.call(e,s[o])&&(n[s[o]]=e[s[o]])}return n}(c,["isArray"]))}function d(e){return t={type:e},(e,s)=>{if("symbol"==typeof s)throw new a;const{options:o,returnTypeFunc:i}=function(e,t){return"function"==typeof e?{returnTypeFunc:e,options:t||{}}:{options:e||{}}}(t,n);c(e,s,o,i)};var t,n}var u;!function(e){e[e.Edit=2]="Edit",e[e.Game=4]="Game",e[e.All=6]="All"}(u||(u={}));class m extends t.Component{constructor(e){super(e),this.resource="",this.scale=1,this.animationName="",this.autoPlay=!0,this.keepResource=!1,this.timeScale=1,this.skin="",this._slotGameObjects=new Map,this._pendingSlotObjects=[],this.waitExecuteInfos=[],this.listenerBound=!1,this.paused=!1,this.init(e)}set armature(e){if(this._armature=e,e){this.applyTimeScale(),this.skin&&this.setSkin(this.skin),this.autoPlay&&this.play(this.animationName);for(const e of this.waitExecuteInfos)if(e.playType){const{name:t,loop:n,track:s}=e;this.play(t,n,s)}else this.stop(e.track);this.waitExecuteInfos=[],this._flushPendingSlotObjects()}}get armature(){return this._armature}init(e){e&&_extends(this,e),this.listenerBound||(this.listenerBound=!0,this.on("start",e=>{var t;null===(t=this.onStart)||void 0===t||t.call(this,e)}),this.on("complete",e=>{var t;null===(t=this.onComplete)||void 0===t||t.call(this,e)}),this.on("event",(e,t)=>{var n;null===(n=this.onEvent)||void 0===n||n.call(this,e,t)}))}onDestroy(){this.destroied=!0}load(){return this.armature}destroy(){this.onDestroy(),this._destroySlotGameObjects(),this.armature&&!this.armature.destroyed&&this.armature.destroy({children:!0}),this.armature=null}pause(){this.paused=!0,this.applyTimeScale()}resume(){this.paused=!1,this.applyTimeScale()}setSkin(e){var t,n,s,o,i;if(this.skin=e,null===(t=this.armature)||void 0===t?void 0:t.skeleton){if(this.armature.skeleton.setSkinByName)this.armature.skeleton.setSkinByName(e);else if(this.armature.skeleton.setSkin){const t=(null===(s=null===(n=this.armature.skeleton.data)||void 0===n?void 0:n.findSkin)||void 0===s?void 0:s.call(n,e))||e;this.armature.skeleton.setSkin(t)}null===(i=(o=this.armature.skeleton).setSlotsToSetupPose)||void 0===i||i.call(o)}}play(e,t,n){try{const s=null!=t?t:this.autoPlay;e&&(this.animationName=e),this.armature?(void 0===n&&(n=0),this.applyTimeScale(),this.armature.state.setAnimation(n,this.animationName,s)):this.waitExecuteInfos.push({playType:!0,name:e,loop:s,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})}applyTimeScale(){var e;(null===(e=this.armature)||void 0===e?void 0:e.state)&&(this.armature.state.timeScale=this.paused?0:this.timeScale)}addAnimation(e,t,n,s){try{this.armature&&(void 0===s&&(s=0),this.armature.state.addAnimation(s,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)}addSlotObject(e,t,n){this.destroied?console.warn("Spine component has been destroyed, cannot addSlotObject"):t&&!t.destroyed?(this._pendingSlotObjects=this._pendingSlotObjects.filter(e=>e.gameObject!==t),this._detachSlotObject(t),this._pendingSlotObjects.push({slot:e,gameObject:t,options:n}),this._flushPendingSlotObjects()):console.warn("Cannot add a destroyed or missing GameObject to a Spine slot")}_doAddSlotObject(e,t,n,o){const i=new s.Container;i.addChild(n),this.armature.addSlotObject(e,i,o),this._slotGameObjects.set(t,{slot:e,wrapper:i}),this._syncTransformTree(t)}_isSlotAvailable(e){var t,n,s,o;if("number"==typeof e){if(!Number.isInteger(e)||e<0)return!1;const s=null===(n=null===(t=this.armature)||void 0===t?void 0:t.skeleton)||void 0===n?void 0:n.slots;return!Array.isArray(s)||e<s.length}if("string"!=typeof e||0===e.length)return!1;const i=null===(o=null===(s=this.armature)||void 0===s?void 0:s.skeleton)||void 0===o?void 0:o.findSlot;return"function"!=typeof i||Boolean(i.call(this.armature.skeleton,e))}_detachSlotObject(e){const t=this._slotGameObjects.get(e);if(t){this.armature&&!this.armature.destroyed&&this.armature.removeSlotObject(t.wrapper);for(const e of[...t.wrapper.children])t.wrapper.removeChild(e);t.wrapper.destroy({children:!1}),this._slotGameObjects.delete(e)}}_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(this.destroied||0===this._pendingSlotObjects.length)return;if(!this.armature||!this._containerManager)return;const e=[],t=this._pendingSlotObjects;this._pendingSlotObjects=[];for(const n of t){if(n.gameObject.destroyed)continue;if(!this._isSlotAvailable(n.slot)){console.warn(`Spine slot "${n.slot}" does not exist; pending slot object was discarded`);continue}const t=this._containerManager.getContainer(n.gameObject.id);if(t)try{this._doAddSlotObject(n.slot,n.gameObject,t,n.options)}catch(e){console.warn(`Failed to add GameObject to Spine slot "${n.slot}"`,e)}else e.push(n)}this._pendingSlotObjects.push(...e)}removeSlotObject(e){this._pendingSlotObjects=this._pendingSlotObjects.filter(t=>t.gameObject!==e),this._detachSlotObject(e)}_destroySlotGameObjects(){const e=new Set([...this._slotGameObjects.keys(),...this._pendingSlotObjects.map(e=>e.gameObject)]);for(const t of e)this._detachSlotObject(t),t.destroyed||t.destroy();this._slotGameObjects.clear(),this._pendingSlotObjects=[]}}m.componentName="Spine",o([d("string"),i("design:type",String)],m.prototype,"resource",void 0),o([d("number"),i("design:type",Number)],m.prototype,"scale",void 0),o([d("string"),i("design:type",String)],m.prototype,"animationName",void 0),o([d("boolean"),i("design:type",Boolean)],m.prototype,"autoPlay",void 0),o([d("boolean"),i("design:type",Boolean)],m.prototype,"keepResource",void 0),o([d("number"),i("design:type",Number)],m.prototype,"timeScale",void 0),o([d("string"),i("design:type",String)],m.prototype,"skin",void 0);let h={};function p(e,t,n){return r(this,void 0,void 0,function*(){let s=h[e.name];if(!s)if(e.complete)s=function(e,t,n,s){const o=t.ske,i=t.atlas,r=new s.AtlasAttachmentLoader(i),a=o instanceof Uint8Array?new s.SkeletonBinary(r):new s.SkeletonJson(r);a.scale=n||1;const l={spineData:a.readSkeletonData(o),ref:0,imageSrc:(c=t.image,c?"string"==typeof c?c:c.label||(null===(d=c.source)||void 0===d?void 0:d.label)||(null===(m=null===(u=c.source)||void 0===u?void 0:u.resource)||void 0===m?void 0:m.src)||(null===(p=c.source)||void 0===p?void 0:p._sourceOrigin)||c.src||(null===(f=c.baseTexture)||void 0===f?void 0:f.cacheId)||"":"")};var c,d,u,m,p,f;return h[e]=l,l}(e.name,e.data,t,n);else if(!s)return;return s.ref++,s.spineData})}let f=class extends n.Renderer{constructor(){super(...arguments),this.armatures={},this._spineComponents={}}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 s in this.armatures){const o=+s;for(let s=0;s<e.length;++s){let i=e[s];if(i.id===o){let e=i.getComponent(m);e&&(this.remove({type:t.OBSERVER_TYPE.REMOVE,gameObject:i,component:e,componentName:m.componentName}),n.push({type:t.OBSERVER_TYPE.ADD,gameObject:i,component:e,componentName:m.componentName}));break}}}setTimeout(()=>{n.forEach(e=>{this.add(e)})},1e3)},!1)}update(e){super.update();for(let t in this.armatures){const n=this.armatures[t],s=this._spineComponents[t];!n||n.destroyed||(null==s?void 0:s.destroied)?(this.releaseComponentResource(s),delete this.armatures[t],delete this._spineComponents[t]):n.update(.001*e.deltaTime)}for(let e in this._spineComponents){const t=this._spineComponents[e];t&&!t.destroied?t._flushPendingSlotObjects():(this.releaseComponentResource(t),delete this._spineComponents[e])}}componentChanged(e){return r(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 s,o,i;return r(this,void 0,void 0,function*(){const r=e.component;clearTimeout(r.addHandler);const a=e.gameObject.id,l=this.increaseAsyncId(a),c=yield t.resource.getResource(r.resource);if(!this.validateAsyncId(a,l)||r.destroied||e.gameObject.destroyed)return;const d=yield p(c,r.scale,this.pixiSpine);if(!this.validateAsyncId(a,l)||r.destroied||e.gameObject.destroyed)return;if(!d)return void(r.addHandler=setTimeout(()=>{r.destroied||e.gameObject.destroyed||(void 0===n&&(n=20),--n>0?this.add(e,n):console.log("retry exceed max times",r.resource))},1e3));this.remove(e);const u=null===(o=null===(s=this.renderSystem)||void 0===s?void 0:s.containerManager)||void 0===o?void 0:o.getContainer(e.gameObject.id);if(!u||r.destroied||e.gameObject.destroyed)return;r.lastResource=r.resource;const m=new this.pixiSpine.Spine({skeletonData:d,autoUpdate:!1});if(this.armatures[e.gameObject.id]=m,this._spineComponents[e.gameObject.id]=r,e.gameObject&&e.gameObject.transform){const t=e.gameObject.transform;m.x=t.size.width*t.origin.x,m.y=t.size.height*t.origin.y}u.addChildAt(m,0),m.update(),r._containerManager=null===(i=this.renderSystem)||void 0===i?void 0:i.containerManager,r.armature=m,r.emit("loaded",{resource:r.resource}),m.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(e){var n,s;return r(this,void 0,void 0,function*(){const o=e.gameObject.id;this.increaseAsyncId(o);const i=e.component;clearTimeout(i.addHandler);const r=this.armatures[o],a=null===(s=null===(n=this.renderSystem)||void 0===n?void 0:n.containerManager)||void 0===s?void 0:s.getContainer(o);a&&r&&a.removeChild(r);const l=i.armature;i.armature=null,delete this.armatures[o],delete this._spineComponents[o],l&&(i._destroySlotGameObjects(),l.destroyed||l.destroy({children:!0})),this.releaseComponentResource(i),e.type,t.OBSERVER_TYPE.CHANGE})}onDestroy(){for(const e of Object.keys(this._spineComponents)){const t=this._spineComponents[+e],n=this.armatures[+e];null==t||t._destroySlotGameObjects(),n&&!n.destroyed&&n.destroy({children:!0}),t&&(t.armature=null),this.releaseComponentResource(t)}this.armatures={},this._spineComponents={},this.asyncIdMap={}}releaseComponentResource(e){const n=null==e?void 0:e.lastResource;e&&n&&(e.lastResource="",function(e,n,s=!0){const o=e.name,i=h[o];i&&(i.ref--,i.ref>0||(h[o]===i&&delete h[o],s&&setTimeout(()=>r(this,void 0,void 0,function*(){i.ref<=0&&!h[o]&&t.resource.destroy(o)}),100)))}({name:n},0,!e.keepResource))}};f.systemName="SpineSystem",f=o([t.decorators.componentObserver({Spine:["resource"]})],f);var y=f;return t.resource.registerResourceType("SPINE"),e.Spine=m,e.SpineSystem=y,Object.defineProperty(e,"__esModule",{value:!0}),e}({},EVA,EVA.plugin.renderer,PIXI);globalThis.EVA.plugin.spineBase=globalThis.EVA.plugin.spineBase||_EVA_IIFE_spineBase;
@@ -119,7 +119,7 @@ class Spine extends eva_js.Component {
119
119
  this.timeScale = 1;
120
120
  /** 当前皮肤名称 */
121
121
  this.skin = '';
122
- /** 挂载到插槽的 GameObject 映射(GameObject -> { slot, wrapper }) */
122
+ /** 挂载到插槽的 GameObject 及对应 slot/wrapper 映射 */
123
123
  this._slotGameObjects = new Map();
124
124
  /** 等待容器就绪的 slot 挂载请求 */
125
125
  this._pendingSlotObjects = [];
@@ -129,10 +129,7 @@ class Spine extends eva_js.Component {
129
129
  this.paused = false;
130
130
  this.init(params);
131
131
  }
132
- /**
133
- * 设置骨架实例
134
- * 当骨架加载完成后自动执行等待队列中的动画操作
135
- */
132
+ // 设置骨架实例;骨架加载完成后自动执行等待队列中的动画操作。
136
133
  set armature(val) {
137
134
  this._armature = val;
138
135
  if (!val)
@@ -153,6 +150,7 @@ class Spine extends eva_js.Component {
153
150
  }
154
151
  }
155
152
  this.waitExecuteInfos = [];
153
+ this._flushPendingSlotObjects();
156
154
  }
157
155
  /** 获取骨架实例 */
158
156
  get armature() {
@@ -194,8 +192,8 @@ class Spine extends eva_js.Component {
194
192
  }
195
193
  destroy() {
196
194
  this.onDestroy();
195
+ this._destroySlotGameObjects();
197
196
  if (this.armature && !this.armature.destroyed) {
198
- this._destroySlotGameObjects();
199
197
  this.armature.destroy({ children: true });
200
198
  }
201
199
  this.armature = null;
@@ -399,21 +397,19 @@ class Spine extends eva_js.Component {
399
397
  * @param options.followAttachmentTimeline - 是否跟随插槽的附件时间线
400
398
  */
401
399
  addSlotObject(slot, gameObject, options) {
402
- if (!this.armature) {
403
- console.warn('Spine armature is not ready, cannot addSlotObject');
400
+ if (this.destroied) {
401
+ console.warn('Spine component has been destroyed, cannot addSlotObject');
404
402
  return;
405
403
  }
406
- if (!this._containerManager) {
407
- console.warn('ContainerManager is not available');
404
+ if (!gameObject || gameObject.destroyed) {
405
+ console.warn('Cannot add a destroyed or missing GameObject to a Spine slot');
408
406
  return;
409
407
  }
410
- const container = this._containerManager.getContainer(gameObject.id);
411
- if (!container) {
412
- // 容器尚未就绪,加入 pending 队列,等待下一帧自动处理
413
- this._pendingSlotObjects.push({ slot, gameObject, options });
414
- return;
415
- }
416
- this._doAddSlotObject(slot, gameObject, container, options);
408
+ // 同一 GameObject 只保留最后一次挂载意图,避免依赖分阶段就绪时重复挂载。
409
+ this._pendingSlotObjects = this._pendingSlotObjects.filter(pending => pending.gameObject !== gameObject);
410
+ this._detachSlotObject(gameObject);
411
+ this._pendingSlotObjects.push({ slot, gameObject, options });
412
+ this._flushPendingSlotObjects();
417
413
  }
418
414
  _doAddSlotObject(slot, gameObject, container, options) {
419
415
  // 创建 wrapper 容器:Spine 骨骼矩阵作用在 wrapper 上,
@@ -426,6 +422,32 @@ class Spine extends eva_js.Component {
426
422
  // 手动同步 gameObject 及其子树的 transform 到 container
427
423
  this._syncTransformTree(gameObject);
428
424
  }
425
+ _isSlotAvailable(slot) {
426
+ var _a, _b, _c, _d;
427
+ if (typeof slot === 'number') {
428
+ if (!Number.isInteger(slot) || slot < 0)
429
+ return false;
430
+ const slots = (_b = (_a = this.armature) === null || _a === void 0 ? void 0 : _a.skeleton) === null || _b === void 0 ? void 0 : _b.slots;
431
+ return !Array.isArray(slots) || slot < slots.length;
432
+ }
433
+ if (typeof slot !== 'string' || slot.length === 0)
434
+ return false;
435
+ const findSlot = (_d = (_c = this.armature) === null || _c === void 0 ? void 0 : _c.skeleton) === null || _d === void 0 ? void 0 : _d.findSlot;
436
+ return typeof findSlot !== 'function' || Boolean(findSlot.call(this.armature.skeleton, slot));
437
+ }
438
+ _detachSlotObject(gameObject) {
439
+ const entry = this._slotGameObjects.get(gameObject);
440
+ if (!entry)
441
+ return;
442
+ if (this.armature && !this.armature.destroyed) {
443
+ this.armature.removeSlotObject(entry.wrapper);
444
+ }
445
+ for (const child of [...entry.wrapper.children]) {
446
+ entry.wrapper.removeChild(child);
447
+ }
448
+ entry.wrapper.destroy({ children: false });
449
+ this._slotGameObjects.delete(gameObject);
450
+ }
429
451
  /**
430
452
  * 递归同步 gameObject 及其子树的 transform 到对应的渲染容器
431
453
  */
@@ -449,21 +471,34 @@ class Spine extends eva_js.Component {
449
471
  * 处理等待容器就绪的 slot 挂载请求(由 SpineSystem 每帧调用)
450
472
  */
451
473
  _flushPendingSlotObjects() {
452
- if (this._pendingSlotObjects.length === 0)
474
+ if (this.destroied || this._pendingSlotObjects.length === 0)
453
475
  return;
454
476
  if (!this.armature || !this._containerManager)
455
477
  return;
456
478
  const still = [];
457
- for (const pending of this._pendingSlotObjects) {
479
+ const pendingSlotObjects = this._pendingSlotObjects;
480
+ this._pendingSlotObjects = [];
481
+ for (const pending of pendingSlotObjects) {
482
+ if (pending.gameObject.destroyed)
483
+ continue;
484
+ if (!this._isSlotAvailable(pending.slot)) {
485
+ console.warn(`Spine slot "${pending.slot}" does not exist; pending slot object was discarded`);
486
+ continue;
487
+ }
458
488
  const container = this._containerManager.getContainer(pending.gameObject.id);
459
489
  if (container) {
460
- this._doAddSlotObject(pending.slot, pending.gameObject, container, pending.options);
490
+ try {
491
+ this._doAddSlotObject(pending.slot, pending.gameObject, container, pending.options);
492
+ }
493
+ catch (error) {
494
+ console.warn(`Failed to add GameObject to Spine slot "${pending.slot}"`, error);
495
+ }
461
496
  }
462
497
  else {
463
498
  still.push(pending);
464
499
  }
465
500
  }
466
- this._pendingSlotObjects = still;
501
+ this._pendingSlotObjects.push(...still);
467
502
  }
468
503
  /**
469
504
  * 从插槽上移除挂载的 GameObject
@@ -473,24 +508,19 @@ class Spine extends eva_js.Component {
473
508
  removeSlotObject(gameObject) {
474
509
  // 从 pending 队列中移除
475
510
  this._pendingSlotObjects = this._pendingSlotObjects.filter(p => p.gameObject !== gameObject);
476
- const entry = this._slotGameObjects.get(gameObject);
477
- if (entry && this.armature) {
478
- this.armature.removeSlotObject(entry.wrapper);
479
- entry.wrapper.destroy({ children: false });
480
- }
481
- this._slotGameObjects.delete(gameObject);
511
+ this._detachSlotObject(gameObject);
482
512
  }
483
513
  /**
484
514
  * 销毁所有挂载到插槽的 GameObject(内部使用)
485
515
  */
486
516
  _destroySlotGameObjects() {
487
- for (const [gameObject, entry] of this._slotGameObjects) {
517
+ const gameObjects = new Set([
518
+ ...this._slotGameObjects.keys(),
519
+ ...this._pendingSlotObjects.map(pending => pending.gameObject),
520
+ ]);
521
+ for (const gameObject of gameObjects) {
522
+ this._detachSlotObject(gameObject);
488
523
  if (!gameObject.destroyed) {
489
- // 先从 spine 插槽移除 wrapper,避免 destroy 时重复操作
490
- if (this.armature) {
491
- this.armature.removeSlotObject(entry.wrapper);
492
- }
493
- entry.wrapper.destroy({ children: false });
494
524
  gameObject.destroy();
495
525
  }
496
526
  }
@@ -574,17 +604,31 @@ function getSpineData(res, scale, pixiSpine) {
574
604
  return data.spineData;
575
605
  });
576
606
  }
577
- function releaseSpineData(res, _imageSrc) {
607
+ function releaseSpineData(res, _imageSrc, destroyResource = true) {
578
608
  const resourceName = res.name;
579
609
  const data = dataMap[resourceName];
580
610
  if (!data) {
581
611
  return;
582
612
  }
583
613
  data.ref--;
614
+ if (data.ref > 0) {
615
+ return;
616
+ }
617
+ // Parsed skeleton data owns attachment objects that point at Pixi textures.
618
+ // Armature teardown can invalidate those objects even when the raw resource
619
+ // is intentionally kept. Remove this generation immediately so the next
620
+ // EvaApp reparses the still-loaded atlas instead of rendering stale sources.
621
+ if (dataMap[resourceName] === data) {
622
+ delete dataMap[resourceName];
623
+ }
624
+ if (!destroyResource) {
625
+ return;
626
+ }
584
627
  setTimeout(() => __awaiter(this, void 0, void 0, function* () {
585
- if (data.ref <= 0) {
628
+ // A newer EvaApp may have rebuilt the same resource name while this delayed
629
+ // release was pending. Never unload the replacement generation's assets.
630
+ if (data.ref <= 0 && !dataMap[resourceName]) {
586
631
  eva_js.resource.destroy(resourceName);
587
- delete dataMap[resourceName];
588
632
  }
589
633
  }), 100);
590
634
  }
@@ -667,6 +711,7 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
667
711
  const armature = this.armatures[key];
668
712
  const component = this._spineComponents[key];
669
713
  if (!armature || armature.destroyed || (component === null || component === void 0 ? void 0 : component.destroied)) {
714
+ this.releaseComponentResource(component);
670
715
  delete this.armatures[key];
671
716
  delete this._spineComponents[key];
672
717
  continue;
@@ -679,6 +724,7 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
679
724
  for (let key in this._spineComponents) {
680
725
  const component = this._spineComponents[key];
681
726
  if (!component || component.destroied) {
727
+ this.releaseComponentResource(component);
682
728
  delete this._spineComponents[key];
683
729
  continue;
684
730
  }
@@ -790,7 +836,7 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
790
836
  this.add(changed);
791
837
  }
792
838
  remove(changed) {
793
- var _a, _b, _c, _d, _e, _f;
839
+ var _a, _b;
794
840
  return __awaiter(this, void 0, void 0, function* () {
795
841
  const gameObjectId = changed.gameObject.id;
796
842
  this.increaseAsyncId(gameObjectId);
@@ -811,20 +857,36 @@ let SpineSystem = class SpineSystem extends pluginRenderer.Renderer {
811
857
  if (!componentArmature.destroyed) {
812
858
  componentArmature.destroy({ children: true });
813
859
  }
814
- if (!component.keepResource && component.lastResource) {
815
- try {
816
- const res = yield eva_js.resource.getResource(component.lastResource);
817
- const imageSrc = ((_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);
818
- releaseSpineData(res, imageSrc);
819
- }
820
- catch (error) {
821
- console.warn('Failed to release Spine resource', component.lastResource, error);
822
- }
823
- }
824
860
  }
861
+ this.releaseComponentResource(component);
825
862
  if (changed.type === eva_js.OBSERVER_TYPE.CHANGE) ;
826
863
  });
827
864
  }
865
+ onDestroy() {
866
+ for (const key of Object.keys(this._spineComponents)) {
867
+ const component = this._spineComponents[+key];
868
+ const armature = this.armatures[+key];
869
+ component === null || component === void 0 ? void 0 : component._destroySlotGameObjects();
870
+ if (armature && !armature.destroyed) {
871
+ armature.destroy({ children: true });
872
+ }
873
+ if (component) {
874
+ component.armature = null;
875
+ }
876
+ this.releaseComponentResource(component);
877
+ }
878
+ this.armatures = {};
879
+ this._spineComponents = {};
880
+ this.asyncIdMap = {};
881
+ }
882
+ releaseComponentResource(component) {
883
+ const resourceName = component === null || component === void 0 ? void 0 : component.lastResource;
884
+ if (!component || !resourceName) {
885
+ return;
886
+ }
887
+ component.lastResource = '';
888
+ releaseSpineData({ name: resourceName }, '', !component.keepResource);
889
+ }
828
890
  };
829
891
  /** 系统名称 */
830
892
  SpineSystem.systemName = 'SpineSystem';
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@eva/eva.js"),t=require("@eva/plugin-renderer"),i=require("pixi.js"),s=require("@eva/inspector-decorator");function r(e,t,i,s){var r,n=arguments.length,a=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,i,s);else for(var o=e.length-1;o>=0;o--)(r=e[o])&&(a=(n<3?r(a):n>3?r(t,i,a):r(t,i))||a);return n>3&&a&&Object.defineProperty(t,i,a),a}function n(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function a(e,t,i,s){return new(i||(i=Promise))(function(r,n){function a(e){try{c(s.next(e))}catch(e){n(e)}}function o(e){try{c(s.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i(function(e){e(t)})).then(a,o)}c((s=s.apply(e,t||[])).next())})}"function"==typeof SuppressedError&&SuppressedError;class o extends e.Component{constructor(e){super(e),this.resource="",this.scale=1,this.animationName="",this.autoPlay=!0,this.keepResource=!1,this.timeScale=1,this.skin="",this._slotGameObjects=new Map,this._pendingSlotObjects=[],this.waitExecuteInfos=[],this.listenerBound=!1,this.paused=!1,this.init(e)}set armature(e){if(this._armature=e,e){this.applyTimeScale(),this.skin&&this.setSkin(this.skin),this.autoPlay&&this.play(this.animationName);for(const e of this.waitExecuteInfos)if(e.playType){const{name:t,loop:i,track:s}=e;this.play(t,i,s)}else this.stop(e.track);this.waitExecuteInfos=[]}}get armature(){return this._armature}init(e){e&&Object.assign(this,e),this.listenerBound||(this.listenerBound=!0,this.on("start",e=>{var t;null===(t=this.onStart)||void 0===t||t.call(this,e)}),this.on("complete",e=>{var t;null===(t=this.onComplete)||void 0===t||t.call(this,e)}),this.on("event",(e,t)=>{var i;null===(i=this.onEvent)||void 0===i||i.call(this,e,t)}))}onDestroy(){this.destroied=!0}load(){return this.armature}destroy(){this.onDestroy(),this.armature&&!this.armature.destroyed&&(this._destroySlotGameObjects(),this.armature.destroy({children:!0})),this.armature=null}pause(){this.paused=!0,this.applyTimeScale()}resume(){this.paused=!1,this.applyTimeScale()}setSkin(e){var t,i,s,r,n;if(this.skin=e,null===(t=this.armature)||void 0===t?void 0:t.skeleton){if(this.armature.skeleton.setSkinByName)this.armature.skeleton.setSkinByName(e);else if(this.armature.skeleton.setSkin){const t=(null===(s=null===(i=this.armature.skeleton.data)||void 0===i?void 0:i.findSkin)||void 0===s?void 0:s.call(i,e))||e;this.armature.skeleton.setSkin(t)}null===(n=(r=this.armature.skeleton).setSlotsToSetupPose)||void 0===n||n.call(r)}}play(e,t,i){try{const s=null!=t?t:this.autoPlay;e&&(this.animationName=e),this.armature?(void 0===i&&(i=0),this.applyTimeScale(),this.armature.state.setAnimation(i,this.animationName,s)):this.waitExecuteInfos.push({playType:!0,name:e,loop:s,track:i})}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})}applyTimeScale(){var e;(null===(e=this.armature)||void 0===e?void 0:e.state)&&(this.armature.state.timeScale=this.paused?0:this.timeScale)}addAnimation(e,t,i,s){try{this.armature&&(void 0===s&&(s=0),this.armature.state.addAnimation(s,e,i,t))}catch(e){console.log(e)}}setMix(e,t,i){this.armature&&this.armature.state.data.setMix(e,t,i)}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,i){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 s=this._containerManager.getContainer(t.id);s?this._doAddSlotObject(e,t,s,i):this._pendingSlotObjects.push({slot:e,gameObject:t,options:i})}_doAddSlotObject(e,t,s,r){const n=new i.Container;n.addChild(s),this.armature.addSlotObject(e,n,r),this._slotGameObjects.set(t,{slot:e,wrapper:n}),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 i=this._containerManager.getContainer(t.gameObject.id);i?this._doAddSlotObject(t.slot,t.gameObject,i,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",r([s.type("string"),n("design:type",String)],o.prototype,"resource",void 0),r([s.type("number"),n("design:type",Number)],o.prototype,"scale",void 0),r([s.type("string"),n("design:type",String)],o.prototype,"animationName",void 0),r([s.type("boolean"),n("design:type",Boolean)],o.prototype,"autoPlay",void 0),r([s.type("boolean"),n("design:type",Boolean)],o.prototype,"keepResource",void 0),r([s.type("number"),n("design:type",Number)],o.prototype,"timeScale",void 0),r([s.type("string"),n("design:type",String)],o.prototype,"skin",void 0);let c={};function d(e,t,i){return a(this,void 0,void 0,function*(){let s=c[e.name];if(!s)if(e.complete)s=function(e,t,i,s){const r=t.ske,n=t.atlas,a=new s.AtlasAttachmentLoader(n),o=r instanceof Uint8Array?new s.SkeletonBinary(a):new s.SkeletonJson(a);o.scale=i||1;const d={spineData:o.readSkeletonData(r),ref:0,imageSrc:(l=t.image,l?"string"==typeof l?l:l.label||(null===(m=l.source)||void 0===m?void 0:m.label)||(null===(h=null===(u=l.source)||void 0===u?void 0:u.resource)||void 0===h?void 0:h.src)||(null===(p=l.source)||void 0===p?void 0:p._sourceOrigin)||l.src||(null===(y=l.baseTexture)||void 0===y?void 0:y.cacheId)||"":"")};var l,m,u,h,p,y;return c[e]=d,d}(e.name,e.data,t,i);else if(!s)return;return s.ref++,s.spineData})}let l=class extends t.Renderer{constructor(){super(...arguments),this.armatures={},this._spineComponents={}}init({pixiSpine:i}){this.renderSystem=this.game.getSystem(t.RendererSystem),this.renderSystem.rendererManager.register(this),this.pixiSpine=i,this.game.canvas.addEventListener("webglcontextrestored",()=>{const t=this.game.gameObjects;let i=[];for(let s in this.armatures){const r=+s;for(let s=0;s<t.length;++s){let n=t[s];if(n.id===r){let t=n.getComponent(o);t&&(this.remove({type:e.OBSERVER_TYPE.REMOVE,gameObject:n,component:t,componentName:o.componentName}),i.push({type:e.OBSERVER_TYPE.ADD,gameObject:n,component:t,componentName:o.componentName}));break}}}setTimeout(()=>{i.forEach(e=>{this.add(e)})},1e3)},!1)}update(e){super.update();for(let t in this.armatures){const i=this.armatures[t],s=this._spineComponents[t];!i||i.destroyed||(null==s?void 0:s.destroied)?(delete this.armatures[t],delete this._spineComponents[t]):i.update(.001*e.deltaTime)}for(let e in this._spineComponents){const t=this._spineComponents[e];t&&!t.destroied?t._flushPendingSlotObjects():delete this._spineComponents[e]}}componentChanged(t){return a(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,i){var s,r,n;return a(this,void 0,void 0,function*(){const a=t.component;clearTimeout(a.addHandler);const o=t.gameObject.id,c=this.increaseAsyncId(o),l=yield e.resource.getResource(a.resource);if(!this.validateAsyncId(o,c)||a.destroied||t.gameObject.destroyed)return;const m=yield d(l,a.scale,this.pixiSpine);if(!this.validateAsyncId(o,c)||a.destroied||t.gameObject.destroyed)return;if(!m)return void(a.addHandler=setTimeout(()=>{a.destroied||t.gameObject.destroyed||(void 0===i&&(i=20),--i>0?this.add(t,i):console.log("retry exceed max times",a.resource))},1e3));this.remove(t);const u=null===(r=null===(s=this.renderSystem)||void 0===s?void 0:s.containerManager)||void 0===r?void 0:r.getContainer(t.gameObject.id);if(!u||a.destroied||t.gameObject.destroyed)return;a.lastResource=a.resource;const h=new this.pixiSpine.Spine({skeletonData:m,autoUpdate:!1});if(this.armatures[t.gameObject.id]=h,this._spineComponents[t.gameObject.id]=a,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(),a._containerManager=null===(n=this.renderSystem)||void 0===n?void 0:n.containerManager,a.armature=h,a.emit("loaded",{resource:a.resource}),h.state.addListener({start:(e,t)=>{a.emit("start",{track:e,name:e.animation.name})},complete:(e,t)=>{a.emit("complete",{track:e,name:e.animation.name})},interrupt:(e,t)=>{a.emit("interrupt",{track:e,name:e.animation.name})},end:(e,t)=>{a.emit("end",{track:e,name:e.animation.name})},event:(e,t)=>{a.emit("event",e,t)}})})}change(e){this.remove(e),this.add(e)}remove(t){var i,s,r,n,o,d;return a(this,void 0,void 0,function*(){const l=t.gameObject.id;this.increaseAsyncId(l);const m=t.component;clearTimeout(m.addHandler);const u=this.armatures[l],h=null===(s=null===(i=this.renderSystem)||void 0===i?void 0:i.containerManager)||void 0===s?void 0:s.getContainer(l);h&&u&&h.removeChild(u);const p=m.armature;if(m.armature=null,delete this.armatures[l],delete this._spineComponents[l],p&&(m._destroySlotGameObjects(),p.destroyed||p.destroy({children:!0}),!m.keepResource&&m.lastResource))try{const t=yield e.resource.getResource(m.lastResource);(null===(n=null===(r=t.data)||void 0===r?void 0:r.image)||void 0===n?void 0:n.src)||(null===(d=null===(o=t.data)||void 0===o?void 0:o.image)||void 0===d||d.label);!function(t){const i=t.name,s=c[i];s&&(s.ref--,setTimeout(()=>a(this,void 0,void 0,function*(){s.ref<=0&&(e.resource.destroy(i),delete c[i])}),100))}(t)}catch(e){console.warn("Failed to release Spine resource",m.lastResource,e)}t.type,e.OBSERVER_TYPE.CHANGE})}};l.systemName="SpineSystem",l=r([e.decorators.componentObserver({Spine:["resource"]})],l);var m=l;e.resource.registerResourceType("SPINE"),exports.Spine=o,exports.SpineSystem=m;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@eva/eva.js"),t=require("@eva/plugin-renderer"),s=require("pixi.js"),i=require("@eva/inspector-decorator");function n(e,t,s,i){var n,o=arguments.length,r=o<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,s):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,s,i);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(r=(o<3?n(r):o>3?n(t,s,r):n(t,s))||r);return o>3&&r&&Object.defineProperty(t,s,r),r}function o(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function r(e,t,s,i){return new(s||(s=Promise))(function(n,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?n(e.value):(t=e.value,t instanceof s?t:new s(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})}"function"==typeof SuppressedError&&SuppressedError;class a extends e.Component{constructor(e){super(e),this.resource="",this.scale=1,this.animationName="",this.autoPlay=!0,this.keepResource=!1,this.timeScale=1,this.skin="",this._slotGameObjects=new Map,this._pendingSlotObjects=[],this.waitExecuteInfos=[],this.listenerBound=!1,this.paused=!1,this.init(e)}set armature(e){if(this._armature=e,e){this.applyTimeScale(),this.skin&&this.setSkin(this.skin),this.autoPlay&&this.play(this.animationName);for(const e of this.waitExecuteInfos)if(e.playType){const{name:t,loop:s,track:i}=e;this.play(t,s,i)}else this.stop(e.track);this.waitExecuteInfos=[],this._flushPendingSlotObjects()}}get armature(){return this._armature}init(e){e&&Object.assign(this,e),this.listenerBound||(this.listenerBound=!0,this.on("start",e=>{var t;null===(t=this.onStart)||void 0===t||t.call(this,e)}),this.on("complete",e=>{var t;null===(t=this.onComplete)||void 0===t||t.call(this,e)}),this.on("event",(e,t)=>{var s;null===(s=this.onEvent)||void 0===s||s.call(this,e,t)}))}onDestroy(){this.destroied=!0}load(){return this.armature}destroy(){this.onDestroy(),this._destroySlotGameObjects(),this.armature&&!this.armature.destroyed&&this.armature.destroy({children:!0}),this.armature=null}pause(){this.paused=!0,this.applyTimeScale()}resume(){this.paused=!1,this.applyTimeScale()}setSkin(e){var t,s,i,n,o;if(this.skin=e,null===(t=this.armature)||void 0===t?void 0:t.skeleton){if(this.armature.skeleton.setSkinByName)this.armature.skeleton.setSkinByName(e);else if(this.armature.skeleton.setSkin){const t=(null===(i=null===(s=this.armature.skeleton.data)||void 0===s?void 0:s.findSkin)||void 0===i?void 0:i.call(s,e))||e;this.armature.skeleton.setSkin(t)}null===(o=(n=this.armature.skeleton).setSlotsToSetupPose)||void 0===o||o.call(n)}}play(e,t,s){try{const i=null!=t?t:this.autoPlay;e&&(this.animationName=e),this.armature?(void 0===s&&(s=0),this.applyTimeScale(),this.armature.state.setAnimation(s,this.animationName,i)):this.waitExecuteInfos.push({playType:!0,name:e,loop:i,track:s})}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})}applyTimeScale(){var e;(null===(e=this.armature)||void 0===e?void 0:e.state)&&(this.armature.state.timeScale=this.paused?0:this.timeScale)}addAnimation(e,t,s,i){try{this.armature&&(void 0===i&&(i=0),this.armature.state.addAnimation(i,e,s,t))}catch(e){console.log(e)}}setMix(e,t,s){this.armature&&this.armature.state.data.setMix(e,t,s)}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,s){this.destroied?console.warn("Spine component has been destroyed, cannot addSlotObject"):t&&!t.destroyed?(this._pendingSlotObjects=this._pendingSlotObjects.filter(e=>e.gameObject!==t),this._detachSlotObject(t),this._pendingSlotObjects.push({slot:e,gameObject:t,options:s}),this._flushPendingSlotObjects()):console.warn("Cannot add a destroyed or missing GameObject to a Spine slot")}_doAddSlotObject(e,t,i,n){const o=new s.Container;o.addChild(i),this.armature.addSlotObject(e,o,n),this._slotGameObjects.set(t,{slot:e,wrapper:o}),this._syncTransformTree(t)}_isSlotAvailable(e){var t,s,i,n;if("number"==typeof e){if(!Number.isInteger(e)||e<0)return!1;const i=null===(s=null===(t=this.armature)||void 0===t?void 0:t.skeleton)||void 0===s?void 0:s.slots;return!Array.isArray(i)||e<i.length}if("string"!=typeof e||0===e.length)return!1;const o=null===(n=null===(i=this.armature)||void 0===i?void 0:i.skeleton)||void 0===n?void 0:n.findSlot;return"function"!=typeof o||Boolean(o.call(this.armature.skeleton,e))}_detachSlotObject(e){const t=this._slotGameObjects.get(e);if(t){this.armature&&!this.armature.destroyed&&this.armature.removeSlotObject(t.wrapper);for(const e of[...t.wrapper.children])t.wrapper.removeChild(e);t.wrapper.destroy({children:!1}),this._slotGameObjects.delete(e)}}_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(this.destroied||0===this._pendingSlotObjects.length)return;if(!this.armature||!this._containerManager)return;const e=[],t=this._pendingSlotObjects;this._pendingSlotObjects=[];for(const s of t){if(s.gameObject.destroyed)continue;if(!this._isSlotAvailable(s.slot)){console.warn(`Spine slot "${s.slot}" does not exist; pending slot object was discarded`);continue}const t=this._containerManager.getContainer(s.gameObject.id);if(t)try{this._doAddSlotObject(s.slot,s.gameObject,t,s.options)}catch(e){console.warn(`Failed to add GameObject to Spine slot "${s.slot}"`,e)}else e.push(s)}this._pendingSlotObjects.push(...e)}removeSlotObject(e){this._pendingSlotObjects=this._pendingSlotObjects.filter(t=>t.gameObject!==e),this._detachSlotObject(e)}_destroySlotGameObjects(){const e=new Set([...this._slotGameObjects.keys(),...this._pendingSlotObjects.map(e=>e.gameObject)]);for(const t of e)this._detachSlotObject(t),t.destroyed||t.destroy();this._slotGameObjects.clear(),this._pendingSlotObjects=[]}}a.componentName="Spine",n([i.type("string"),o("design:type",String)],a.prototype,"resource",void 0),n([i.type("number"),o("design:type",Number)],a.prototype,"scale",void 0),n([i.type("string"),o("design:type",String)],a.prototype,"animationName",void 0),n([i.type("boolean"),o("design:type",Boolean)],a.prototype,"autoPlay",void 0),n([i.type("boolean"),o("design:type",Boolean)],a.prototype,"keepResource",void 0),n([i.type("number"),o("design:type",Number)],a.prototype,"timeScale",void 0),n([i.type("string"),o("design:type",String)],a.prototype,"skin",void 0);let c={};function d(e,t,s){return r(this,void 0,void 0,function*(){let i=c[e.name];if(!i)if(e.complete)i=function(e,t,s,i){const n=t.ske,o=t.atlas,r=new i.AtlasAttachmentLoader(o),a=n instanceof Uint8Array?new i.SkeletonBinary(r):new i.SkeletonJson(r);a.scale=s||1;const d={spineData:a.readSkeletonData(n),ref:0,imageSrc:(l=t.image,l?"string"==typeof l?l:l.label||(null===(m=l.source)||void 0===m?void 0:m.label)||(null===(h=null===(u=l.source)||void 0===u?void 0:u.resource)||void 0===h?void 0:h.src)||(null===(p=l.source)||void 0===p?void 0:p._sourceOrigin)||l.src||(null===(y=l.baseTexture)||void 0===y?void 0:y.cacheId)||"":"")};var l,m,u,h,p,y;return c[e]=d,d}(e.name,e.data,t,s);else if(!i)return;return i.ref++,i.spineData})}let l=class extends t.Renderer{constructor(){super(...arguments),this.armatures={},this._spineComponents={}}init({pixiSpine:s}){this.renderSystem=this.game.getSystem(t.RendererSystem),this.renderSystem.rendererManager.register(this),this.pixiSpine=s,this.game.canvas.addEventListener("webglcontextrestored",()=>{const t=this.game.gameObjects;let s=[];for(let i in this.armatures){const n=+i;for(let i=0;i<t.length;++i){let o=t[i];if(o.id===n){let t=o.getComponent(a);t&&(this.remove({type:e.OBSERVER_TYPE.REMOVE,gameObject:o,component:t,componentName:a.componentName}),s.push({type:e.OBSERVER_TYPE.ADD,gameObject:o,component:t,componentName:a.componentName}));break}}}setTimeout(()=>{s.forEach(e=>{this.add(e)})},1e3)},!1)}update(e){super.update();for(let t in this.armatures){const s=this.armatures[t],i=this._spineComponents[t];!s||s.destroyed||(null==i?void 0:i.destroied)?(this.releaseComponentResource(i),delete this.armatures[t],delete this._spineComponents[t]):s.update(.001*e.deltaTime)}for(let e in this._spineComponents){const t=this._spineComponents[e];t&&!t.destroied?t._flushPendingSlotObjects():(this.releaseComponentResource(t),delete this._spineComponents[e])}}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,s){var i,n,o;return r(this,void 0,void 0,function*(){const r=t.component;clearTimeout(r.addHandler);const a=t.gameObject.id,c=this.increaseAsyncId(a),l=yield e.resource.getResource(r.resource);if(!this.validateAsyncId(a,c)||r.destroied||t.gameObject.destroyed)return;const m=yield d(l,r.scale,this.pixiSpine);if(!this.validateAsyncId(a,c)||r.destroied||t.gameObject.destroyed)return;if(!m)return void(r.addHandler=setTimeout(()=>{r.destroied||t.gameObject.destroyed||(void 0===s&&(s=20),--s>0?this.add(t,s):console.log("retry exceed max times",r.resource))},1e3));this.remove(t);const u=null===(n=null===(i=this.renderSystem)||void 0===i?void 0:i.containerManager)||void 0===n?void 0:n.getContainer(t.gameObject.id);if(!u||r.destroied||t.gameObject.destroyed)return;r.lastResource=r.resource;const h=new this.pixiSpine.Spine({skeletonData:m,autoUpdate:!1});if(this.armatures[t.gameObject.id]=h,this._spineComponents[t.gameObject.id]=r,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(),r._containerManager=null===(o=this.renderSystem)||void 0===o?void 0:o.containerManager,r.armature=h,r.emit("loaded",{resource:r.resource}),h.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 s,i;return r(this,void 0,void 0,function*(){const n=t.gameObject.id;this.increaseAsyncId(n);const o=t.component;clearTimeout(o.addHandler);const r=this.armatures[n],a=null===(i=null===(s=this.renderSystem)||void 0===s?void 0:s.containerManager)||void 0===i?void 0:i.getContainer(n);a&&r&&a.removeChild(r);const c=o.armature;o.armature=null,delete this.armatures[n],delete this._spineComponents[n],c&&(o._destroySlotGameObjects(),c.destroyed||c.destroy({children:!0})),this.releaseComponentResource(o),t.type,e.OBSERVER_TYPE.CHANGE})}onDestroy(){for(const e of Object.keys(this._spineComponents)){const t=this._spineComponents[+e],s=this.armatures[+e];null==t||t._destroySlotGameObjects(),s&&!s.destroyed&&s.destroy({children:!0}),t&&(t.armature=null),this.releaseComponentResource(t)}this.armatures={},this._spineComponents={},this.asyncIdMap={}}releaseComponentResource(t){const s=null==t?void 0:t.lastResource;t&&s&&(t.lastResource="",function(t,s,i=!0){const n=t.name,o=c[n];o&&(o.ref--,o.ref>0||(c[n]===o&&delete c[n],i&&setTimeout(()=>r(this,void 0,void 0,function*(){o.ref<=0&&!c[n]&&e.resource.destroy(n)}),100)))}({name:s},0,!t.keepResource))}};l.systemName="SpineSystem",l=n([e.decorators.componentObserver({Spine:["resource"]})],l);var m=l;e.resource.registerResourceType("SPINE"),exports.Spine=a,exports.SpineSystem=m;
@@ -9,6 +9,14 @@ import { RendererManager } from '@eva/plugin-renderer';
9
9
  import { RendererSystem } from '@eva/plugin-renderer';
10
10
  import { UpdateParams } from '@eva/eva.js';
11
11
 
12
+ declare interface PendingSlotObject {
13
+ slot: number | string;
14
+ gameObject: GameObject;
15
+ options?: {
16
+ followAttachmentTimeline?: boolean;
17
+ };
18
+ }
19
+
12
20
  /**
13
21
  * Spine 骨骼动画组件
14
22
  *
@@ -90,25 +98,15 @@ export declare class Spine extends Component<SpineParams> {
90
98
  private _armature;
91
99
  /** 容器管理器引用(由 SpineSystem 设置) */
92
100
  _containerManager: any;
93
- /** 挂载到插槽的 GameObject 映射(GameObject -> { slot, wrapper }) */
101
+ /** 挂载到插槽的 GameObject 及对应 slot/wrapper 映射 */
94
102
  private _slotGameObjects;
95
103
  /** 等待容器就绪的 slot 挂载请求 */
96
- _pendingSlotObjects: {
97
- slot: number | string;
98
- gameObject: GameObject;
99
- options?: {
100
- followAttachmentTimeline?: boolean;
101
- };
102
- }[];
104
+ _pendingSlotObjects: PendingSlotObject[];
103
105
  /** 等待执行的动画操作队列 */
104
106
  private waitExecuteInfos;
105
107
  private listenerBound;
106
108
  private paused;
107
109
  constructor(params?: SpineParams);
108
- /**
109
- * 设置骨架实例
110
- * 当骨架加载完成后自动执行等待队列中的动画操作
111
- */
112
110
  set armature(val: any);
113
111
  /** 获取骨架实例 */
114
112
  get armature(): any;
@@ -222,6 +220,8 @@ export declare class Spine extends Component<SpineParams> {
222
220
  followAttachmentTimeline?: boolean;
223
221
  }): void;
224
222
  private _doAddSlotObject;
223
+ private _isSlotAvailable;
224
+ private _detachSlotObject;
225
225
  /**
226
226
  * 递归同步 gameObject 及其子树的 transform 到对应的渲染容器
227
227
  */
@@ -300,6 +300,8 @@ export declare class SpineSystem extends Renderer {
300
300
  add(changed: ComponentChanged, count?: number): Promise<void>;
301
301
  change(changed: ComponentChanged): void;
302
302
  remove(changed: ComponentChanged): Promise<void>;
303
+ onDestroy(): void;
304
+ private releaseComponentResource;
303
305
  }
304
306
 
305
307
  export { }
@@ -115,7 +115,7 @@ class Spine extends Component {
115
115
  this.timeScale = 1;
116
116
  /** 当前皮肤名称 */
117
117
  this.skin = '';
118
- /** 挂载到插槽的 GameObject 映射(GameObject -> { slot, wrapper }) */
118
+ /** 挂载到插槽的 GameObject 及对应 slot/wrapper 映射 */
119
119
  this._slotGameObjects = new Map();
120
120
  /** 等待容器就绪的 slot 挂载请求 */
121
121
  this._pendingSlotObjects = [];
@@ -125,10 +125,7 @@ class Spine extends Component {
125
125
  this.paused = false;
126
126
  this.init(params);
127
127
  }
128
- /**
129
- * 设置骨架实例
130
- * 当骨架加载完成后自动执行等待队列中的动画操作
131
- */
128
+ // 设置骨架实例;骨架加载完成后自动执行等待队列中的动画操作。
132
129
  set armature(val) {
133
130
  this._armature = val;
134
131
  if (!val)
@@ -149,6 +146,7 @@ class Spine extends Component {
149
146
  }
150
147
  }
151
148
  this.waitExecuteInfos = [];
149
+ this._flushPendingSlotObjects();
152
150
  }
153
151
  /** 获取骨架实例 */
154
152
  get armature() {
@@ -190,8 +188,8 @@ class Spine extends Component {
190
188
  }
191
189
  destroy() {
192
190
  this.onDestroy();
191
+ this._destroySlotGameObjects();
193
192
  if (this.armature && !this.armature.destroyed) {
194
- this._destroySlotGameObjects();
195
193
  this.armature.destroy({ children: true });
196
194
  }
197
195
  this.armature = null;
@@ -395,21 +393,19 @@ class Spine extends Component {
395
393
  * @param options.followAttachmentTimeline - 是否跟随插槽的附件时间线
396
394
  */
397
395
  addSlotObject(slot, gameObject, options) {
398
- if (!this.armature) {
399
- console.warn('Spine armature is not ready, cannot addSlotObject');
396
+ if (this.destroied) {
397
+ console.warn('Spine component has been destroyed, cannot addSlotObject');
400
398
  return;
401
399
  }
402
- if (!this._containerManager) {
403
- console.warn('ContainerManager is not available');
400
+ if (!gameObject || gameObject.destroyed) {
401
+ console.warn('Cannot add a destroyed or missing GameObject to a Spine slot');
404
402
  return;
405
403
  }
406
- const container = this._containerManager.getContainer(gameObject.id);
407
- if (!container) {
408
- // 容器尚未就绪,加入 pending 队列,等待下一帧自动处理
409
- this._pendingSlotObjects.push({ slot, gameObject, options });
410
- return;
411
- }
412
- this._doAddSlotObject(slot, gameObject, container, options);
404
+ // 同一 GameObject 只保留最后一次挂载意图,避免依赖分阶段就绪时重复挂载。
405
+ this._pendingSlotObjects = this._pendingSlotObjects.filter(pending => pending.gameObject !== gameObject);
406
+ this._detachSlotObject(gameObject);
407
+ this._pendingSlotObjects.push({ slot, gameObject, options });
408
+ this._flushPendingSlotObjects();
413
409
  }
414
410
  _doAddSlotObject(slot, gameObject, container, options) {
415
411
  // 创建 wrapper 容器:Spine 骨骼矩阵作用在 wrapper 上,
@@ -422,6 +418,32 @@ class Spine extends Component {
422
418
  // 手动同步 gameObject 及其子树的 transform 到 container
423
419
  this._syncTransformTree(gameObject);
424
420
  }
421
+ _isSlotAvailable(slot) {
422
+ var _a, _b, _c, _d;
423
+ if (typeof slot === 'number') {
424
+ if (!Number.isInteger(slot) || slot < 0)
425
+ return false;
426
+ const slots = (_b = (_a = this.armature) === null || _a === void 0 ? void 0 : _a.skeleton) === null || _b === void 0 ? void 0 : _b.slots;
427
+ return !Array.isArray(slots) || slot < slots.length;
428
+ }
429
+ if (typeof slot !== 'string' || slot.length === 0)
430
+ return false;
431
+ const findSlot = (_d = (_c = this.armature) === null || _c === void 0 ? void 0 : _c.skeleton) === null || _d === void 0 ? void 0 : _d.findSlot;
432
+ return typeof findSlot !== 'function' || Boolean(findSlot.call(this.armature.skeleton, slot));
433
+ }
434
+ _detachSlotObject(gameObject) {
435
+ const entry = this._slotGameObjects.get(gameObject);
436
+ if (!entry)
437
+ return;
438
+ if (this.armature && !this.armature.destroyed) {
439
+ this.armature.removeSlotObject(entry.wrapper);
440
+ }
441
+ for (const child of [...entry.wrapper.children]) {
442
+ entry.wrapper.removeChild(child);
443
+ }
444
+ entry.wrapper.destroy({ children: false });
445
+ this._slotGameObjects.delete(gameObject);
446
+ }
425
447
  /**
426
448
  * 递归同步 gameObject 及其子树的 transform 到对应的渲染容器
427
449
  */
@@ -445,21 +467,34 @@ class Spine extends Component {
445
467
  * 处理等待容器就绪的 slot 挂载请求(由 SpineSystem 每帧调用)
446
468
  */
447
469
  _flushPendingSlotObjects() {
448
- if (this._pendingSlotObjects.length === 0)
470
+ if (this.destroied || this._pendingSlotObjects.length === 0)
449
471
  return;
450
472
  if (!this.armature || !this._containerManager)
451
473
  return;
452
474
  const still = [];
453
- for (const pending of this._pendingSlotObjects) {
475
+ const pendingSlotObjects = this._pendingSlotObjects;
476
+ this._pendingSlotObjects = [];
477
+ for (const pending of pendingSlotObjects) {
478
+ if (pending.gameObject.destroyed)
479
+ continue;
480
+ if (!this._isSlotAvailable(pending.slot)) {
481
+ console.warn(`Spine slot "${pending.slot}" does not exist; pending slot object was discarded`);
482
+ continue;
483
+ }
454
484
  const container = this._containerManager.getContainer(pending.gameObject.id);
455
485
  if (container) {
456
- this._doAddSlotObject(pending.slot, pending.gameObject, container, pending.options);
486
+ try {
487
+ this._doAddSlotObject(pending.slot, pending.gameObject, container, pending.options);
488
+ }
489
+ catch (error) {
490
+ console.warn(`Failed to add GameObject to Spine slot "${pending.slot}"`, error);
491
+ }
457
492
  }
458
493
  else {
459
494
  still.push(pending);
460
495
  }
461
496
  }
462
- this._pendingSlotObjects = still;
497
+ this._pendingSlotObjects.push(...still);
463
498
  }
464
499
  /**
465
500
  * 从插槽上移除挂载的 GameObject
@@ -469,24 +504,19 @@ class Spine extends Component {
469
504
  removeSlotObject(gameObject) {
470
505
  // 从 pending 队列中移除
471
506
  this._pendingSlotObjects = this._pendingSlotObjects.filter(p => p.gameObject !== gameObject);
472
- const entry = this._slotGameObjects.get(gameObject);
473
- if (entry && this.armature) {
474
- this.armature.removeSlotObject(entry.wrapper);
475
- entry.wrapper.destroy({ children: false });
476
- }
477
- this._slotGameObjects.delete(gameObject);
507
+ this._detachSlotObject(gameObject);
478
508
  }
479
509
  /**
480
510
  * 销毁所有挂载到插槽的 GameObject(内部使用)
481
511
  */
482
512
  _destroySlotGameObjects() {
483
- for (const [gameObject, entry] of this._slotGameObjects) {
513
+ const gameObjects = new Set([
514
+ ...this._slotGameObjects.keys(),
515
+ ...this._pendingSlotObjects.map(pending => pending.gameObject),
516
+ ]);
517
+ for (const gameObject of gameObjects) {
518
+ this._detachSlotObject(gameObject);
484
519
  if (!gameObject.destroyed) {
485
- // 先从 spine 插槽移除 wrapper,避免 destroy 时重复操作
486
- if (this.armature) {
487
- this.armature.removeSlotObject(entry.wrapper);
488
- }
489
- entry.wrapper.destroy({ children: false });
490
520
  gameObject.destroy();
491
521
  }
492
522
  }
@@ -570,17 +600,31 @@ function getSpineData(res, scale, pixiSpine) {
570
600
  return data.spineData;
571
601
  });
572
602
  }
573
- function releaseSpineData(res, _imageSrc) {
603
+ function releaseSpineData(res, _imageSrc, destroyResource = true) {
574
604
  const resourceName = res.name;
575
605
  const data = dataMap[resourceName];
576
606
  if (!data) {
577
607
  return;
578
608
  }
579
609
  data.ref--;
610
+ if (data.ref > 0) {
611
+ return;
612
+ }
613
+ // Parsed skeleton data owns attachment objects that point at Pixi textures.
614
+ // Armature teardown can invalidate those objects even when the raw resource
615
+ // is intentionally kept. Remove this generation immediately so the next
616
+ // EvaApp reparses the still-loaded atlas instead of rendering stale sources.
617
+ if (dataMap[resourceName] === data) {
618
+ delete dataMap[resourceName];
619
+ }
620
+ if (!destroyResource) {
621
+ return;
622
+ }
580
623
  setTimeout(() => __awaiter(this, void 0, void 0, function* () {
581
- if (data.ref <= 0) {
624
+ // A newer EvaApp may have rebuilt the same resource name while this delayed
625
+ // release was pending. Never unload the replacement generation's assets.
626
+ if (data.ref <= 0 && !dataMap[resourceName]) {
582
627
  resource.destroy(resourceName);
583
- delete dataMap[resourceName];
584
628
  }
585
629
  }), 100);
586
630
  }
@@ -663,6 +707,7 @@ let SpineSystem = class SpineSystem extends Renderer {
663
707
  const armature = this.armatures[key];
664
708
  const component = this._spineComponents[key];
665
709
  if (!armature || armature.destroyed || (component === null || component === void 0 ? void 0 : component.destroied)) {
710
+ this.releaseComponentResource(component);
666
711
  delete this.armatures[key];
667
712
  delete this._spineComponents[key];
668
713
  continue;
@@ -675,6 +720,7 @@ let SpineSystem = class SpineSystem extends Renderer {
675
720
  for (let key in this._spineComponents) {
676
721
  const component = this._spineComponents[key];
677
722
  if (!component || component.destroied) {
723
+ this.releaseComponentResource(component);
678
724
  delete this._spineComponents[key];
679
725
  continue;
680
726
  }
@@ -786,7 +832,7 @@ let SpineSystem = class SpineSystem extends Renderer {
786
832
  this.add(changed);
787
833
  }
788
834
  remove(changed) {
789
- var _a, _b, _c, _d, _e, _f;
835
+ var _a, _b;
790
836
  return __awaiter(this, void 0, void 0, function* () {
791
837
  const gameObjectId = changed.gameObject.id;
792
838
  this.increaseAsyncId(gameObjectId);
@@ -807,20 +853,36 @@ let SpineSystem = class SpineSystem extends Renderer {
807
853
  if (!componentArmature.destroyed) {
808
854
  componentArmature.destroy({ children: true });
809
855
  }
810
- if (!component.keepResource && component.lastResource) {
811
- try {
812
- const res = yield resource.getResource(component.lastResource);
813
- const imageSrc = ((_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);
814
- releaseSpineData(res, imageSrc);
815
- }
816
- catch (error) {
817
- console.warn('Failed to release Spine resource', component.lastResource, error);
818
- }
819
- }
820
856
  }
857
+ this.releaseComponentResource(component);
821
858
  if (changed.type === OBSERVER_TYPE.CHANGE) ;
822
859
  });
823
860
  }
861
+ onDestroy() {
862
+ for (const key of Object.keys(this._spineComponents)) {
863
+ const component = this._spineComponents[+key];
864
+ const armature = this.armatures[+key];
865
+ component === null || component === void 0 ? void 0 : component._destroySlotGameObjects();
866
+ if (armature && !armature.destroyed) {
867
+ armature.destroy({ children: true });
868
+ }
869
+ if (component) {
870
+ component.armature = null;
871
+ }
872
+ this.releaseComponentResource(component);
873
+ }
874
+ this.armatures = {};
875
+ this._spineComponents = {};
876
+ this.asyncIdMap = {};
877
+ }
878
+ releaseComponentResource(component) {
879
+ const resourceName = component === null || component === void 0 ? void 0 : component.lastResource;
880
+ if (!component || !resourceName) {
881
+ return;
882
+ }
883
+ component.lastResource = '';
884
+ releaseSpineData({ name: resourceName }, '', !component.keepResource);
885
+ }
824
886
  };
825
887
  /** 系统名称 */
826
888
  SpineSystem.systemName = 'SpineSystem';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eva/spine-base",
3
- "version": "2.1.0-beta.11",
3
+ "version": "2.1.0-beta.12",
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.1.0-beta.11",
22
- "@eva/plugin-renderer": "2.1.0-beta.11",
21
+ "@eva/eva.js": "2.1.0-beta.12",
22
+ "@eva/plugin-renderer": "2.1.0-beta.12",
23
23
  "@eva/inspector-decorator": "^2.0.0-beta.0",
24
24
  "pixi.js": "^8.17.0"
25
25
  }