feat(card): 完善卡牌图标显示与锁定功能

- 为英雄卡牌添加动画图标显示,根据配置加载对应动画
- 修复锁定按钮显示逻辑,现在正确显示锁定/解锁状态
- 为技能、buff、英雄等卡牌类型添加图标解析功能
- 更新卡牌预制体,调整图标尺寸和锁定图标
- 删除未使用的动画资源文件
- 优化资源图集配置,调整精灵帧位置
This commit is contained in:
walkpan
2026-03-14 21:39:27 +08:00
parent c7248fe32a
commit 7d0dc04d01
14 changed files with 2027 additions and 5936 deletions

View File

@@ -1,9 +1,11 @@
import { mLogger } from "../common/Logger";
import { _decorator, EventTouch, Label, Node, NodeEventType, Sprite, SpriteAtlas, Tween, tween, UIOpacity, Vec3 } from "cc";
import { _decorator, Animation, AnimationClip, EventTouch, Label, Node, NodeEventType, Sprite, SpriteAtlas, Tween, tween, UIOpacity, 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 } from "../common/config/CardSet";
import { CardUseComp } from "./CardUseComp";
import { HeroInfo } from "../common/config/heroSet";
import { BuffsList, SkillSet } from "../common/config/SkillSet";
@@ -46,6 +48,7 @@ export class CardComp extends CCComp {
private restPosition: Vec3 = new Vec3();
private opacityComp: UIOpacity | null = null;
private cardUseComp: CardUseComp | null = null;
private iconVisualToken: number = 0;
onLoad() {
/** 初始阶段只做UI状态准备不触发业务逻辑 */
@@ -86,7 +89,31 @@ export class CardComp extends CCComp {
}
private updateIcon(node: Node, iconId: string) {
if (!node || !iconId) return;
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
if (!sprite) return;
if (this.uiconsAtlas) {
const frame = this.uiconsAtlas.getSpriteFrame(iconId);
if (frame) {
sprite.spriteFrame = frame;
} else {
sprite.spriteFrame = null;
}
return;
}
resources.load("gui/uicons", SpriteAtlas, (err, atlas) => {
if (err || !atlas) {
mLogger.log(this.debugMode, "CardComp", "load uicons atlas failed", err);
return;
}
this.uiconsAtlas = atlas;
const frame = atlas.getSpriteFrame(iconId);
if (frame) {
sprite.spriteFrame = frame;
} else {
sprite.spriteFrame = null;
}
});
}
/** 兼容旧接口:按索引更新卡牌(当前由 MissionCardComp 顺序分发) */
@@ -264,7 +291,7 @@ export class CardComp extends CCComp {
}
/** 点击锁控件:切换锁态;空槽不允许锁定 */
private onToggleLock(event?: Event) {
private onToggleLock(event?: EventTouch) {
if (!this.cardData) return;
this.isLocked = !this.isLocked;
this.updateLockUI();
@@ -272,13 +299,16 @@ export class CardComp extends CCComp {
uuid: this.card_uuid,
locked: this.isLocked
});
event?.stopPropagation();
const stopPropagation = (event as any)?.stopPropagation;
if (typeof stopPropagation === "function") {
stopPropagation.call(event);
}
}
/** 根据锁态刷新 Lock / unLock 显示 */
/** 根据锁态刷新 Lock / unLock 显示Lock=可点击上锁unLock=可点击解锁) */
private updateLockUI() {
if (this.Lock) this.Lock.active = this.isLocked;
if (this.unLock) this.unLock.active = !this.isLocked;
if (this.Lock) this.Lock.active = !this.isLocked;
if (this.unLock) this.unLock.active = this.isLocked;
}
/** 根据当前 cardData 渲染卡面文字与图标 */
@@ -287,13 +317,26 @@ export class CardComp extends CCComp {
this.applyEmptyUI();
return;
}
this.iconVisualToken += 1;
if (this.opacityComp) this.opacityComp.opacity = 255;
this.node.setPosition(this.restPosition);
this.setLabel(this.name_node, `${CardType[this.card_type]}-${this.card_uuid}`);
this.setLabel(this.cost_node, `${this.card_cost}`);
if (this.ap_node) this.ap_node.active = false;
if (this.hp_node) this.hp_node.active = false;
this.updateIcon(this.icon_node, `${this.card_uuid}`);
const iconNode = this.icon_node as Node;
if (this.card_type === CardType.Hero) {
this.updateHeroAnimation(iconNode, this.card_uuid, this.iconVisualToken);
return;
}
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 playRefreshAnim() {
@@ -337,11 +380,13 @@ export class CardComp extends CCComp {
/** 渲染空槽状态 */
private applyEmptyUI() {
this.iconVisualToken += 1;
this.setLabel(this.name_node, "");
this.setLabel(this.cost_node, "");
if (this.ap_node) this.ap_node.active = false;
if (this.hp_node) this.hp_node.active = false;
const sprite = this.icon_node?.getComponent(Sprite);
this.clearIconAnimation(this.icon_node as Node);
const sprite = this.icon_node?.getComponent(Sprite) || this.icon_node?.getComponentInChildren(Sprite);
if (sprite) sprite.spriteFrame = null;
}
@@ -363,6 +408,54 @@ export class CardComp extends CCComp {
return null;
}
private resolveCardIconId(type: CardType, uuid: number): string {
if (type === CardType.Skill) {
return SkillSet[uuid]?.icon || `${uuid}`;
}
if (type === CardType.Buff || type === CardType.Debuff) {
return BuffsList[uuid]?.icon || `${uuid}`;
}
if (type === CardType.Hero) {
return HeroInfo[uuid]?.icon || `${uuid}`;
}
return `${uuid}`;
}
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, "CardComp", `load hero animation failed ${uuid}`, err);
return;
}
if (token !== this.iconVisualToken || !this.cardData || this.card_type !== CardType.Hero || this.card_uuid !== uuid) {
return;
}
this.clearAnimationClips(anim);
anim.addClip(clip);
anim.play("idle");
});
}
private clearIconAnimation(node: Node) {
const anim = node?.getComponent(Animation);
if (!anim) return;
anim.stop();
this.clearAnimationClips(anim);
}
private clearAnimationClips(anim: Animation) {
const clips = (anim as any).clips as AnimationClip[] | undefined;
if (!clips || clips.length === 0) return;
[...clips].forEach(clip => anim.removeClip(clip, true));
}
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() {
this.node.destroy();