import { _decorator, Component, Node, Sprite, director, SpriteFrame, sp, AssetManager, Asset, assetManager, isValid } from 'cc'; import { GEvent } from '../../mx/module/event/GEvent'; import { GBundle } from '../game/ConfigRes'; const { ccclass, property } = _decorator; /**加载 加载动画 皮肤的组件*/ @ccclass('lLoadSpine') export class lLoadSpine extends Component { @property(sp.Skeleton) kaichangSpine: sp.Skeleton = null; @property(sp.Skeleton) zhuizhuSpine: sp.Skeleton = null; path: string = ""; key: string = ""; animation: string = "开场动画"; skinName: string = "默认皮肤"; langSp: sp.Skeleton = null; curentSpineData: sp.SkeletonData = null; /** 已成功加载并挂上的资源路径,用于同皮肤重复进入时跳过后台 load */ private _lastLoadedAssetPath: string = ''; /** 上次已按此 itemid 应用过路径;节点隐藏时 off 了事件,靠它与 CurrentSkin 对比补换装 */ private _storedSkinItemId: number | undefined = undefined; private isLoading: boolean = false; // ✅ 添加加载锁 private abortController: { aborted: boolean } = null; // ✅ 添加取消机制 onLoad() { this.langSp = this.getComponent(sp.Skeleton); } onEnable() { GEvent.Ins.on(GEvent.CHANGE_SKIN, this.onSkinChange, this); const cur = gg.skin.CurrentSkin; if (this._storedSkinItemId !== cur) { this.applySkinPathAndReload(cur); } else { this.updateLang(); } } onDisable() { GEvent.Ins.off(GEvent.CHANGE_SKIN, this.onSkinChange, this); if (this.abortController) { this.abortController.aborted = true; } } /** RoleSkin 换装时发出 CHANGE_SKIN(皮肤 itemid) */ private onSkinChange(skinItemId?: number) { this.applySkinPathAndReload(skinItemId ?? gg.skin.CurrentSkin); } /** 按 itemid 更新 loadSpineName 并加载;换皮肤时的释放逻辑只在 updateLang 里处理 */ private applySkinPathAndReload(skinItemId: number) { let skinConfig = gg.skin.getSkinConfig(skinItemId); if (!skinConfig) { console.warn('lLoadSpine: 无皮肤配置 itemid=', skinItemId); return; } this._storedSkinItemId = skinItemId; this.path = skinConfig.loadSpineName; this.key = 'spine/入场加载动画'; this.updateLang(); } /** 与 load 时一致的 Spine 资源相对路径(不含扩展名) */ private getAssetPath(): string { const k = this.key || this.node.name; return `${k}/${this.path}`; } async updateLang() { // ✅ 1. 基础检查 if (!this.node.isValid || this.langSp == null) return; const assetPath = this.getAssetPath(); if ( this._lastLoadedAssetPath === assetPath && this.curentSpineData && isValid(this.curentSpineData) && this.langSp.skeletonData === this.curentSpineData ) { console.log("spineAsset 已加载", this.path, this.key); return; } // ✅ 2. 取消之前的加载 if (this.abortController) { this.abortController.aborted = true; } this.abortController = { aborted: false }; // ✅ 3. 防止并发加载 if (this.isLoading) return; this.isLoading = true; const currentAbort = this.abortController; try { console.log("更新 lLoadSpine 组件", this.path, this.key); let bundleName = GBundle.Comm2; let loop = this.langSp.loop; let bundle = await this.loadBundle(bundleName); // ✅ 4. 检查是否被取消或节点失效 if (currentAbort.aborted || !this.node.isValid) { this.isLoading = false; return; } if (!bundle) { this.isLoading = false; return; } let spineAsset = await this.loadRes(bundle, assetPath, sp.SkeletonData) as sp.SkeletonData; // ✅ 5. 再次检查 if (currentAbort.aborted || !this.node.isValid || !spineAsset) { if (spineAsset) spineAsset.decRef(); // 清理未使用的资源 this.isLoading = false; return; } if (this.curentSpineData == spineAsset) { this._lastLoadedAssetPath = assetPath; this.isLoading = false; return; } let oldres = this.curentSpineData; this.curentSpineData = spineAsset; spineAsset.addRef(); this._lastLoadedAssetPath = assetPath; console.log("spineAsset 加载完成", this.path, this.key); // ✅ 6. 设置资源 this.langSp.clearTrack(0); // ✅ 先清除动画 this.langSp.skeletonData = spineAsset; // ✅ 7. 再次检查节点有效性 if (this.node.isValid && this.animation) { //设置皮肤 this.langSp.setSkin(this.skinName); this.langSp.setAnimation(0, this.animation, loop); this.kaichangSpine.setAnimation(0, '开场动画', loop); this.zhuizhuSpine.setAnimation(0, '怪物追逐', loop); } // ✅ 8. 延迟释放旧资源(给 Spine 足够时间) if (oldres && oldres != spineAsset) { this.scheduleOnce(() => { if (oldres.refCount > 0) { oldres.decRef(); } }, 0.1); // ✅ 延迟 0.1 秒,确保 Spine 不再使用 } } catch (error) { console.log("更新 lLoadSpine 组件失败", error); } finally { this.isLoading = false; } } setKey(key: string) { this.key = key; this.updateLang(); } setPath(path: string) { this.path = path; this.updateLang(); } private async loadBundle(bundleName) { return new Promise((resolve, reject) => { let bundle = assetManager.getBundle(bundleName); if (bundle) { resolve(bundle); return; } assetManager.loadBundle(bundleName, (err, bundle) => { if (err) { console.log(`加载Bundle:${bundleName}错误`, err); resolve(null); } else { resolve(bundle); } }) }) } private async loadRes(bundle: AssetManager.Bundle, assetPath, type = Asset) { return new Promise((resolve, reject) => { let res = bundle.get(assetPath); if (res) { resolve(res); return; } bundle.load(assetPath, type, (err, asset) => { if (err) { console.log(`加载Bundle:${bundle.name},path:${assetPath}资源错误`, err); resolve(null); } else { resolve(asset); } }) }) } }