Files
pixelheros/assets/script/game/map/ChipComp.ts
panFD 612d611cc1 refactor(ChipComp): 重构英雄图鉴卡组件逻辑
1.  移除非英雄卡牌渲染能力,仅保留英雄卡展示逻辑
2.  简化背景框切换逻辑,使用常量映射直接匹配颜色节点
3.  清理冗余的卡牌配置依赖与工具方法
4.  统一对外接口为setHeroByUuid,移除旧的setData和setHeroIconByUuid
2026-08-23 18:11:40 +08:00

137 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file ChipComp.ts
* @description 英雄图鉴卡预制体组件UI 视图层·精简版)
*
* 职责:
* 1. 根据英雄 UUID 渲染 **图标**idle 动画)。
* 2. 根据 HeroInfo.card_lv 切换 **背景底框颜色**BG_node 下的 green/blue/purple/yellow/red 子节点)。
*
* 说明:
* - 仅处理英雄展示,数据源为 heroSet 的 HeroInfo不依赖 CardSet。
* - 通过 iconVisualToken 防止异步动画加载竞态。
*/
import { mLogger } from "../common/Logger";
import { _decorator, Animation, AnimationClip, Node, Sprite, Vec3, resources } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { HeroInfo } from "../common/config/heroSet";
const { ccclass, property } = _decorator;
/** card_lv → BG_node 子节点颜色名映射1=green 2=blue 3=purple 4=yellow 5=red */
const BG_COLORS = ["green", "blue", "purple", "yellow", "red"];
/**
* ChipComp —— 单张英雄卡图标/背景视图组件
*
* 挂载在图鉴卡预制体上仅负责英雄图标idle 动画)与背景底框的渲染。
*/
@ccclass('ChipComp')
@ecs.register('ChipComp', false)
export class ChipComp extends CCComp {
private debugMode: boolean = false;
// ======================== 编辑器绑定节点 ========================
@property(Node)
icon_node: Node = null!
@property(Node)
BG_node: Node = null!
// ======================== 运行时状态 ========================
card_uuid: number = 0
private iconVisualToken: number = 0;
// ======================== 对外接口 ========================
/**
* 通过英雄 UUID 渲染英雄卡idle 动画图标 + 背景底框)。
* @param uuid 英雄 UUID须存在于 HeroInfo
*/
setHeroByUuid(uuid: number) {
const hero = HeroInfo[uuid];
if (!hero) return;
this.card_uuid = uuid;
this.node.active = true;
this.iconVisualToken += 1;
// 背景:按 card_lv 切换颜色底框green/blue/purple/yellow/red
if (this.BG_node) {
const color = BG_COLORS[Math.min(Math.max(hero.card_lv ?? 1, 1), 5) - 1];
this.BG_node.children.forEach(child => child.active = (child.name === color));
}
// 图标idle 动画(镜像翻转,原图朝右,卡面需朝左)
if (this.icon_node) {
this.updateHeroAnimation(this.icon_node, uuid, this.iconVisualToken);
}
mLogger.log(this.debugMode, "ChipComp", "setHeroByUuid", { uuid });
}
/** 清空并重置 UI */
clear() {
this.card_uuid = 0;
this.iconVisualToken += 1;
if (this.BG_node) {
this.BG_node.children.forEach(child => child.active = false);
}
if (this.icon_node) {
this.clearIconAnimation(this.icon_node);
const sprite = this.icon_node.getComponent(Sprite) || this.icon_node.getComponentInChildren(Sprite);
if (sprite) sprite.spriteFrame = null;
}
this.node.active = false;
}
// ======================== 动画 ========================
/**
* 为英雄图标加载并播放 idle 动画。
* 使用 token 做竞态保护,确保异步回调时不会覆盖已更新的图标。
*/
private updateHeroAnimation(node: Node, uuid: number, token: number) {
const sprite = node?.getComponent(Sprite) || node?.getComponentInChildren(Sprite);
if (sprite) sprite.spriteFrame = null;
const hero = HeroInfo[uuid];
if (!hero) return;
const anim = node.getComponent(Animation) || node.addComponent(Animation);
this.clearAnimationClips(anim);
const path = `game/heros/hero/${hero.path}/idle`;
resources.load(path, AnimationClip, (err, clip) => {
if (err || !clip) {
mLogger.log(this.debugMode, "ChipComp", `load hero animation failed ${uuid}`, err);
return;
}
if (token !== this.iconVisualToken || this.card_uuid !== uuid) {
return;
}
this.clearAnimationClips(anim);
anim.addClip(clip);
anim.play("idle");
});
}
/** 清除图标节点上的动画(停止播放并移除所有 clip */
private clearIconAnimation(node: Node) {
const anim = node?.getComponent(Animation);
if (!anim) return;
anim.stop();
this.clearAnimationClips(anim);
}
/** 移除 Animation 组件上的全部 AnimationClip */
private clearAnimationClips(anim: Animation) {
const clips = anim.clips as AnimationClip[] | undefined;
if (!clips || clips.length === 0) return;
[...clips].forEach(clip => anim.removeClip(clip, true));
}
// ======================== 生命周期钩子 ========================
/** ECS 组件移除时的释放钩子:销毁节点 */
reset() {
this.node.destroy();
}
}