refactor(ChipComp): 重构英雄图鉴卡组件逻辑

1.  移除非英雄卡牌渲染能力,仅保留英雄卡展示逻辑
2.  简化背景框切换逻辑,使用常量映射直接匹配颜色节点
3.  清理冗余的卡牌配置依赖与工具方法
4.  统一对外接口为setHeroByUuid,移除旧的setData和setHeroIconByUuid
This commit is contained in:
panFD
2026-08-23 18:11:40 +08:00
parent fce401bcf9
commit 612d611cc1

View File

@@ -3,34 +3,28 @@
* @description 英雄图鉴卡预制体组件UI 视图层·精简版)
*
* 职责:
* 1. 根据卡牌配置渲染 **图标**英雄卡为 idle 动画,其他卡为静态图集帧)。
* 2. 根据 card_lv / kind 渲染 **背景底框**
* 1. 根据英雄 UUID 渲染 **图标**idle 动画)。
* 2. 根据 HeroInfo.card_lv 切换 **背景底框颜色**BG_node 下的 green/blue/purple/yellow/red 子节点)
*
* 说明:
* - 仅保留图标与背景渲染能力,名称、费用、等级等展示由外部组件负责
* - 仅处理英雄展示,数据源为 heroSet 的 HeroInfo不依赖 CardSet
* - 通过 iconVisualToken 防止异步动画加载竞态。
*
* 依赖:
* - CardConfig / CardType / CKind / CardPoolListCardSet
* - HeroInfoheroSet、SkillSetSkillSet
* - smc —— 全局单例,访问图标图集缓存
*/
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 { CardConfig, CardType, CKind, CardPoolList } from "../common/config/CardSet";
import { CardBgComp } from "./CardBgComp";
import { HeroInfo } from "../common/config/heroSet";
import { SkillSet } from "../common/config/SkillSet";
import { smc } from "../common/SingletonModuleComp";
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 —— 单张卡图标/背景视图组件
* ChipComp —— 单张英雄卡图标/背景视图组件
*
* 挂载在图鉴卡预制体上,仅负责图标与背景的渲染。
* 挂载在图鉴卡预制体上,仅负责英雄图标idle 动画)与背景底框的渲染。
*/
@ccclass('ChipComp')
@ecs.register('ChipComp', false)
@@ -47,165 +41,53 @@ export class ChipComp extends CCComp {
// ======================== 运行时状态 ========================
card_uuid: number = 0
card_type: CardType = CardType.Hero
private cardData: CardConfig | null = null;
private iconVisualToken: number = 0;
// ======================== 生命周期 ========================
onLoad() {
if (!this.cardData) {
this.applyEmptyUI();
}
}
// ======================== 对外接口 ========================
/**
* 设置卡牌数据并渲染图标与背景
* @param data 卡牌配置数据
* 通过英雄 UUID 渲染英雄卡idle 动画图标 + 背景底框)
* @param uuid 英雄 UUID须存在于 HeroInfo
*/
setData(data: CardConfig) {
if (!data) return;
this.cardData = data;
this.card_uuid = data.uuid;
this.card_type = data.type;
this.node.active = true;
this.applyCardUI();
mLogger.log(this.debugMode, "ChipComp", "setData", {
uuid: this.card_uuid,
type: this.card_type
});
}
/**
* 通过英雄 UUID 直接渲染英雄图标(自动播放 idle 动画并应用背景)。
* 适用于碎片、头像、图鉴等纯展示场景。
* @param uuid 英雄 UUID
*/
setHeroIconByUuid(uuid: number) {
setHeroByUuid(uuid: number) {
const hero = HeroInfo[uuid];
if (!hero) return;
this.cardData = null;
this.card_uuid = uuid;
this.card_type = CardType.Hero;
this.node.active = true;
this.iconVisualToken += 1;
// 背景:按 HeroInfo.card_lv 应用英雄底框样式
// 背景:按 card_lv 切换颜色底框green/blue/purple/yellow/red
if (this.BG_node) {
const cardLv = hero.card_lv ?? 1;
const kindName = CKind[CKind.Hero];
this.BG_node.children.forEach(child => {
child.active = (child.name === kindName);
const bg = child.getComponent(CardBgComp);
if (bg) child.active ? bg.apply(cardLv) : bg.clear();
});
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 动画(镜像翻转)
// 图标idle 动画(镜像翻转,原图朝右,卡面需朝左
if (this.icon_node) {
this.icon_node.setScale(new Vec3(-1.5, 1.5, 1));
this.updateHeroAnimation(this.icon_node, uuid, this.iconVisualToken);
}
mLogger.log(this.debugMode, "ChipComp", "setHeroByUuid", { uuid });
}
/** 清空卡牌数据并重置 UI */
/** 清空并重置 UI */
clear() {
this.cardData = null;
this.card_uuid = 0;
this.card_type = CardType.Hero;
this.applyEmptyUI();
this.node.active = false;
}
// ======================== UI 渲染 ========================
/** 渲染图标与背景 */
private applyCardUI() {
if (!this.cardData) {
this.applyEmptyUI();
return;
}
this.iconVisualToken += 1;
const kindName = CKind[this.cardData.kind];
// 背景:按 kind 显示对应底框,按 card_lv 应用等级样式
if (this.BG_node) {
const bgLv = this.cardData.base_card_lv ?? this.cardData.card_lv;
this.BG_node.children.forEach(child => {
child.active = (child.name === kindName);
const bg = child.getComponent(CardBgComp);
if (bg) child.active ? bg.apply(bgLv) : bg.clear();
});
}
// 图标:英雄卡播放 idle 动画,其他卡使用静态图集帧
if (this.icon_node) {
const iconNode = this.icon_node;
if (this.card_type === CardType.Hero) {
// 英雄图标做镜像翻转(原图朝右,卡面需朝左)
iconNode.setScale(new Vec3(-1.5, 1.5, 1));
this.updateHeroAnimation(iconNode, this.card_uuid, this.iconVisualToken);
return;
}
iconNode.setScale(new Vec3(1, 1, 1));
this.clearIconAnimation(iconNode);
const iconId = this.resolveCardIconId(this.card_type, this.card_uuid);
if (iconId) {
this.updateIcon(iconNode, iconId);
} else {
const sprite = iconNode.getComponent(Sprite) || iconNode.getComponentInChildren(Sprite);
if (sprite) sprite.spriteFrame = null;
}
}
}
/** 渲染空槽状态:清空图标与背景 */
private applyEmptyUI() {
this.iconVisualToken += 1;
if (this.BG_node) {
this.BG_node.children.forEach(child => {
child.active = false;
const bg = child.getComponent(CardBgComp);
if (bg) bg.clear();
});
this.BG_node.children.forEach(child => child.active = false);
}
if (this.icon_node) {
this.icon_node.setScale(new Vec3(1, 1, 1));
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;
}
// ======================== 工具方法 ========================
/** 更新图标:从全局缓存图集中获取对应帧 */
private updateIcon(node: Node, iconId: string) {
if (!node || !iconId) return;
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
if (!sprite) return;
if (smc.uiconsAtlas) {
const frame = smc.uiconsAtlas.getSpriteFrame(iconId);
sprite.spriteFrame = frame || null;
}
}
/** 根据卡牌类型和 UUID 解析出图标 ID在 SpriteAtlas 中的帧名) */
private resolveCardIconId(type: CardType, uuid: number): string {
if (type === CardType.Skill) {
return SkillSet[uuid]?.icon || `${uuid}`;
}
if (type === CardType.Hero) {
return HeroInfo[uuid]?.icon || `${uuid}`;
}
return `${uuid}`;
}
// ======================== 动画 ========================
/**
* 为英雄图标加载并播放 idle 动画。
* 为英雄图标加载并播放 idle 动画。
* 使用 token 做竞态保护,确保异步回调时不会覆盖已更新的图标。
*/
private updateHeroAnimation(node: Node, uuid: number, token: number) {
@@ -221,7 +103,7 @@ export class ChipComp extends CCComp {
mLogger.log(this.debugMode, "ChipComp", `load hero animation failed ${uuid}`, err);
return;
}
if (token !== this.iconVisualToken || !this.cardData || this.card_type !== CardType.Hero || this.card_uuid !== uuid) {
if (token !== this.iconVisualToken || this.card_uuid !== uuid) {
return;
}
this.clearAnimationClips(anim);