消消方块阵换皮表情
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

221 lines
7.2 KiB

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<AssetManager.Bundle>((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<Asset>((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);
}
})
})
}
}