Files
pixelheros/assets/script/game/map/ChipComp.ts
pan d9bccd7c93 refactor: 将代码中的card_lv统一替换为rarity
本次提交完成了全项目范围内术语的统一替换,将原本指代卡牌品质/等级的card_lv字段全部重命名为rarity,对齐了设计文档中的命名规范,避免了术语混淆:
1.  更新了所有配置文件、组件类、数据结构中的字段定义
2.  同步修改了注释、文档说明中的相关表述
3.  修正了英雄、技能、装备卡牌的品质显示逻辑
4.  调整了背景颜色切换、属性计算等相关业务逻辑
2026-09-02 16:42:54 +08:00

135 lines
5.1 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.rarity 切换 **背景底框颜色**(BG_node 下的 green/blue/purple/yellow/red 子节点)。
*
* 说明:
* - 仅处理英雄展示,数据源为 heroSet 的 HeroInfo,不依赖 CardSet。
* - 图标采用 CHeroComp 同款方案:加载 idle AnimationClip 取首帧 SpriteFrame 静止展示,
* 通过 iconVisualToken 防止异步加载竞态。
*/
import { mLogger } from "../common/Logger";
import { _decorator, AnimationClip, Node, Sprite, resources, SpriteFrame } 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";
import { RARITY_BG_COLORS } from "../common/config/GameSet";
const { ccclass, property } = _decorator;
/**
* ChipComp —— 单张英雄卡图标/背景视图组件
*
* 挂载在图鉴卡预制体上,仅负责英雄图标(静态首帧)与背景底框的渲染。
*/
@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 渲染英雄卡(静态图标 + 背景底框)。
* @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;
// 背景:按 rarity 切换颜色底框(green/blue/purple/yellow/red)
if (this.BG_node) {
const color = RARITY_BG_COLORS[Math.min(Math.max(hero.rarity ?? 1, 1), 5) - 1];
this.BG_node.children.forEach(child => child.active = (child.name === color));
}
// 图标:idle 动画首帧静态展示
if (this.icon_node) {
this.updateHeroIcon(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) {
const sprite = this.icon_node.getComponent(Sprite) || this.icon_node.getComponentInChildren(Sprite);
if (sprite) sprite.spriteFrame = null;
}
this.node.active = false;
}
// ======================== 图标(静态首帧) ========================
/**
* 加载英雄 idle 动画剪辑,提取首帧 SpriteFrame 静止展示(零运行时动画开销)。
* 使用 token 做竞态保护,确保异步回调时不会覆盖已更新的图标。
*/
private updateHeroIcon(node: Node, uuid: number, token: number) {
if (!node || !node.isValid) return;
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
if (sprite) sprite.spriteFrame = null;
const hero = HeroInfo[uuid];
if (!hero) return;
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;
if (!node || !node.isValid) return;
const target = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
if (!target || !target.isValid) return;
// 帧动画数据存于 spriteFrame 轨道的 _channel._curve._values(裸 SpriteFrame 数组)
let firstFrame: SpriteFrame | undefined;
for (const track of clip.tracks) {
const curve = (track as unknown as { channel?: { curve?: { _values?: unknown[] } } }).channel?.curve;
const frame = curve?._values?.[0];
if (frame instanceof SpriteFrame) {
firstFrame = frame;
break;
}
}
if (firstFrame) {
target.spriteFrame = firstFrame;
}
});
}
// ======================== 生命周期钩子 ========================
/** ECS 组件移除时的释放钩子:销毁节点 */
reset() {
this.node.destroy();
}
}