From 354a2894e4b2e89db7a366bb333f49c9a908157f Mon Sep 17 00:00:00 2001 From: pan Date: Mon, 27 Jul 2026 14:27:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor(common):=20=E6=8A=BD=E7=A6=BB=E5=8D=A1?= =?UTF-8?q?=E7=89=8C=E5=9B=BE=E6=A0=87=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E4=B8=BA=E9=80=9A=E7=94=A8=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新建CardIconHelper工具类统一处理卡牌图标解析和设置逻辑 2. 移除SkillBoxComp、SCardComp、EquipListComp、ItemListComp中的重复图标处理代码 3. 统一所有卡牌组件的图标加载优先级和处理流程 4. 新增卡牌背景颜色随等级切换的通用逻辑 --- assets/script/game/common/CardIconHelper.ts | 59 ++++++++++++++++++ .../script/game/common/CardIconHelper.ts.meta | 9 +++ assets/script/game/map/EquipListComp.ts | 54 +++++++++++------ assets/script/game/map/ItemListComp.ts | 60 ++++++++++--------- assets/script/game/map/SCardComp.ts | 58 ++++++++++-------- assets/script/game/map/SkillBoxComp.ts | 38 +++++------- 6 files changed, 183 insertions(+), 95 deletions(-) create mode 100644 assets/script/game/common/CardIconHelper.ts create mode 100644 assets/script/game/common/CardIconHelper.ts.meta diff --git a/assets/script/game/common/CardIconHelper.ts b/assets/script/game/common/CardIconHelper.ts new file mode 100644 index 00000000..6e9b2b4a --- /dev/null +++ b/assets/script/game/common/CardIconHelper.ts @@ -0,0 +1,59 @@ +/** + * @file CardIconHelper.ts + * @description 卡牌图标解析与设置共享工具 + * + * 职责: + * 统一处理技能卡/装备卡/商品卡的图标 ID 解析与 SpriteFrame 设置, + * 避免 SkillBoxComp / SCardComp / ItemListComp / EquipListComp 重复维护相同逻辑。 + * + * 解析优先级: + * 1. cardData.icon(卡牌自定义图标,最高优先级) + * 2. 装备卡(trigger_type=Field 且有 field):取第一个 field 词条的 FieldSkillSet.icon + * 3. 技能卡(有 skill):取 SkillSet.icon + * 4. 兜底:skill uuid 或 card uuid 的字符串形式 + */ +import { Node, Sprite } from "cc"; +import { CardConfig, CardTriggerType } from "./config/CardSet"; +import { FieldSkillSet, SkillSet } from "./config/SkillSet"; +import { smc } from "./SingletonModuleComp"; + +/** + * 解析卡牌图标 ID + * @param cardData 卡牌配置 + * @returns 图标 ID(对应图集 frame 名),空字符串表示无法解析 + */ +export function getCardIconId(cardData: CardConfig | null): string { + if (!cardData) return ""; + + // 1. 卡牌自定义 icon 优先 + if (cardData.icon) return cardData.icon; + + // 2. 装备卡:取第一个 field 词条图标 + if (cardData.field && cardData.field.length > 0) { + const fieldUuid = cardData.field[0]; + return FieldSkillSet[fieldUuid]?.icon || `${fieldUuid}`; + } + + // 3. 技能卡:取 SkillSet.icon + if (cardData.skill) { + return SkillSet[cardData.skill]?.icon || `${cardData.skill}`; + } + + // 4. 兜底:uuid 字符串 + return `${cardData.uuid}`; +} + +/** + * 设置节点 Sprite 的图标 + * @param node 目标节点(自动查找自身或子节点的 Sprite) + * @param iconId 图标 ID(对应 smc.uiconsAtlas 中的 frame 名) + */ +export function setSpriteFrame(node: Node, iconId: string): void { + 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; + } +} diff --git a/assets/script/game/common/CardIconHelper.ts.meta b/assets/script/game/common/CardIconHelper.ts.meta new file mode 100644 index 00000000..5257c01a --- /dev/null +++ b/assets/script/game/common/CardIconHelper.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "436c57f4-4109-40cd-b7f4-01d9686f388c", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/game/map/EquipListComp.ts b/assets/script/game/map/EquipListComp.ts index 433624ab..ac04aea6 100644 --- a/assets/script/game/map/EquipListComp.ts +++ b/assets/script/game/map/EquipListComp.ts @@ -12,15 +12,15 @@ * EquipBoxComp 按 Field 类型被动驻场,属性加成由 FieldSkillHelper 全局聚合。 */ import { mLogger } from "../common/Logger"; -import { _decorator, Node, Sprite, Label, NodeEventType, Vec3, UIOpacity, tween, Tween } from "cc"; +import { _decorator, Node, Label, NodeEventType, Vec3, UIOpacity, tween, Tween } 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 } from "../common/config/CardSet"; import { EquipPoolList } from "../common/config/EquipSet"; import { FieldSkillSet } from "../common/config/SkillSet"; import { oops } from "db://oops-framework/core/Oops"; +import { getCardIconId, setSpriteFrame } from "../common/CardIconHelper"; import { GameEvent } from "../common/config/GameEvent"; -import { smc } from "../common/SingletonModuleComp"; import { UIID } from "../common/config/GameUIConfig"; import { MissionEconomy } from "./MissionEconomy"; @@ -43,7 +43,7 @@ export class EquipListComp extends CCComp { @property({ type: Node }) private icon_node: Node = null; - /** 背景节点 */ + /** 背景节点(含 green/blue/purple/red/yellow 颜色子节点,按 card_lv 激活) */ @property({ type: Node }) private bg_node: Node = null; @@ -166,6 +166,9 @@ export class EquipListComp extends CCComp { this.level_label.string = lv >= 2 ? "★".repeat(lv - 1) : ""; } + // 背景:按 card_lv 激活对应颜色子节点(1→green 2→blue 3→purple 4→red 5→yellow) + this.updateBgNode(); + // 属性词条(最多显示 3 条,对应 fied1/fied2/fied3) const fieldLabels = [this.fied1, this.fied2, this.fied3]; const fields = this.cardData.field || []; @@ -181,10 +184,12 @@ export class EquipListComp extends CCComp { } } - // 图标(优先用卡牌自定义 icon,否则取第一个 field 词条图标) - if (this.icon_node && fields.length > 0) { - const iconId = this.cardData.icon || FieldSkillSet[fields[0]]?.icon || ""; - this.updateIcon(this.icon_node, iconId); + // 图标(共享工具解析:cardData.icon → FieldSkillSet → SkillSet) + if (this.icon_node) { + const iconId = getCardIconId(this.cardData); + if (iconId) { + setSpriteFrame(this.icon_node, iconId); + } } // 购买按钮费用显示 @@ -201,6 +206,30 @@ export class EquipListComp extends CCComp { this.playEnterAnim(); } + /** + * 根据 card_lv 切换 bg_node 下的颜色子节点显示。 + * 等级与颜色对应关系: + * card_lv 1 → green + * card_lv 2 → blue + * card_lv 3 → purple + * card_lv 4 → red + * card_lv 5 → yellow + */ + private updateBgNode() { + if (!this.bg_node || !this.cardData) return; + const lvToColor: Record = { + 1: "green", + 2: "blue", + 3: "purple", + 4: "red", + 5: "yellow", + }; + const targetColor = lvToColor[this.cardData.card_lv ?? 1]; + this.bg_node.children.forEach(child => { + child.active = (child.name === targetColor); + }); + } + // ======================== 动画 ======================== /** 播放入场动画:卡牌由小变大(scale 0→1)+ 淡入 */ @@ -299,17 +328,6 @@ export class EquipListComp extends CCComp { .start(); } - /** 更新图标精灵 */ - 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; - } - } - // ======================== 长按信息框 ======================== /** 长按时长(秒) */ diff --git a/assets/script/game/map/ItemListComp.ts b/assets/script/game/map/ItemListComp.ts index f86c5ee2..960299a5 100644 --- a/assets/script/game/map/ItemListComp.ts +++ b/assets/script/game/map/ItemListComp.ts @@ -14,7 +14,7 @@ * 本组件同时服务于装备商店和商品商店,通过 cardData 的 trigger_type / kind 自动判断类型。 */ import { mLogger } from "../common/Logger"; -import { _decorator, Node, Sprite, Label, NodeEventType, Vec3, UIOpacity, tween, Tween } from "cc"; +import { _decorator, Node, Label, NodeEventType, Vec3, UIOpacity, tween, Tween } 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, CardTriggerType, CKind } from "../common/config/CardSet"; @@ -22,8 +22,8 @@ import { EquipPoolList } from "../common/config/EquipSet"; import { SkillCardList } from "../common/config/SCardSet"; import { FieldSkillSet, SkillSet } from "../common/config/SkillSet"; import { oops } from "db://oops-framework/core/Oops"; +import { getCardIconId, setSpriteFrame } from "../common/CardIconHelper"; import { GameEvent } from "../common/config/GameEvent"; -import { smc } from "../common/SingletonModuleComp"; import { UIID } from "../common/config/GameUIConfig"; import { MissionEconomy } from "./MissionEconomy"; @@ -46,7 +46,7 @@ export class ItemListComp extends CCComp { @property({ type: Node }) private icon_node: Node = null; - /** 背景节点 */ + /** 背景节点(含 green/blue/purple/red/yellow 颜色子节点,按 card_lv 激活) */ @property({ type: Node }) private bg_node: Node = null; @@ -175,6 +175,9 @@ export class ItemListComp extends CCComp { this.level_label.string = lv >= 2 ? "★".repeat(lv - 1) : ""; } + // 背景:按 card_lv 激活对应颜色子节点(1→green 2→blue 3→purple 4→red 5→yellow) + this.updateBgNode(); + // 描述/属性词条(最多显示 3 条) const fieldLabels = [this.fied1, this.fied2, this.fied3]; if (isEquip) { @@ -218,21 +221,11 @@ export class ItemListComp extends CCComp { } } - // 图标:优先用卡牌自定义 icon,装备取第一个 field 词条图标,商品取技能图标 + // 图标(共享工具解析:cardData.icon → FieldSkillSet → SkillSet) if (this.icon_node) { - let iconId = ""; - if (this.cardData.icon) { - iconId = this.cardData.icon; - } else if (isEquip) { - const fields = this.cardData.field || []; - if (fields.length > 0) { - iconId = FieldSkillSet[fields[0]]?.icon || ""; - } - } else if (isPotion && this.cardData.skill) { - iconId = SkillSet[this.cardData.skill]?.icon || ""; - } + const iconId = getCardIconId(this.cardData); if (iconId) { - this.updateIcon(this.icon_node, iconId); + setSpriteFrame(this.icon_node, iconId); } } @@ -250,6 +243,30 @@ export class ItemListComp extends CCComp { this.playEnterAnim(); } + /** + * 根据 card_lv 切换 bg_node 下的颜色子节点显示。 + * 等级与颜色对应关系: + * card_lv 1 → green + * card_lv 2 → blue + * card_lv 3 → purple + * card_lv 4 → red + * card_lv 5 → yellow + */ + private updateBgNode() { + if (!this.bg_node || !this.cardData) return; + const lvToColor: Record = { + 1: "green", + 2: "blue", + 3: "purple", + 4: "red", + 5: "yellow", + }; + const targetColor = lvToColor[this.cardData.card_lv ?? 1]; + this.bg_node.children.forEach(child => { + child.active = (child.name === targetColor); + }); + } + // ======================== 动画 ======================== /** 播放入场动画:卡牌由小变大(scale 0→1)+ 淡入 */ @@ -348,17 +365,6 @@ export class ItemListComp extends CCComp { .start(); } - /** 更新图标精灵 */ - 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; - } - } - // ======================== 长按信息框 ======================== /** 长按时长(秒) */ diff --git a/assets/script/game/map/SCardComp.ts b/assets/script/game/map/SCardComp.ts index 832256a9..358344af 100644 --- a/assets/script/game/map/SCardComp.ts +++ b/assets/script/game/map/SCardComp.ts @@ -18,6 +18,7 @@ import { CardConfig } from "../common/config/CardSet"; import { SkillCardList } from "../common/config/SCardSet"; import { FieldSkillSet, SkillSet, SkillConfig } from "../common/config/SkillSet"; import { oops } from "db://oops-framework/core/Oops"; +import { getCardIconId, setSpriteFrame } from "../common/CardIconHelper"; import { GameEvent } from "../common/config/GameEvent"; import { smc } from "../common/SingletonModuleComp"; import { UIID } from "../common/config/GameUIConfig"; @@ -42,7 +43,7 @@ export class SCardComp extends CCComp { @property({ type: Node }) private icon_node: Node = null; - /** 背景节点 */ + /** 背景节点(含 green/blue/purple/red/yellow 颜色子节点,按 card_lv 激活) */ @property({ type: Node }) private bg_node: Node = null; @@ -208,6 +209,9 @@ export class SCardComp extends CCComp { this.level_label.string = lv >= 2 ? "★".repeat(lv - 1) : ""; } + // 背景:按 card_lv 激活对应颜色子节点(1→green 2→blue 3→purple 4→red 5→yellow) + this.updateBgNode(); + // 描述词条(最多显示 3 条,对应 fied1/fied2/fied3) const fieldLabels = [this.fied1, this.fied2, this.fied3]; const desc = this.getDescription(skillCard, skill); @@ -217,11 +221,11 @@ export class SCardComp extends CCComp { fieldLabels[i].node.active = i === 0 && !!desc; } - // 图标 + // 图标(共享工具解析:cardData.icon → FieldSkillSet → SkillSet) if (this.icon_node) { - const iconId = this.getIconId(skill); + const iconId = getCardIconId(this.cardData); if (iconId) { - this.updateIcon(this.icon_node, iconId); + setSpriteFrame(this.icon_node, iconId); } } @@ -239,6 +243,30 @@ export class SCardComp extends CCComp { this.playEnterAnim(); } + /** + * 根据 card_lv 切换 bg_node 下的颜色子节点显示。 + * 等级与颜色对应关系: + * card_lv 1 → green + * card_lv 2 → blue + * card_lv 3 → purple + * card_lv 4 → red + * card_lv 5 → yellow + */ + private updateBgNode() { + if (!this.bg_node || !this.cardData) return; + const lvToColor: Record = { + 1: "green", + 2: "blue", + 3: "purple", + 4: "red", + 5: "yellow", + }; + const targetColor = lvToColor[this.cardData.card_lv ?? 1]; + this.bg_node.children.forEach(child => { + child.active = (child.name === targetColor); + }); + } + // ======================== 动画 ======================== /** 播放入场动画:卡牌由小变大(scale 0→1)+ 淡入 */ @@ -346,28 +374,6 @@ export class SCardComp extends CCComp { return skillCard?.info || skill?.info || this.cardData.info || ""; } - /** 获取技能图标 id:优先用卡牌自定义 icon,驻场技能取 FieldSkillSet.icon,其余取 SkillSet.icon */ - private getIconId(skill: SkillConfig | null): string { - if (!this.cardData) return ""; - if (this.cardData.icon) return this.cardData.icon; - if (!this.cardData.skill && this.cardData.field && this.cardData.field.length > 0) { - const fieldUuid = this.cardData.field[0]; - return FieldSkillSet[fieldUuid]?.icon || `${fieldUuid}`; - } - return skill?.icon || `${this.cardData.skill ?? this.cardData.uuid}`; - } - - /** 更新图标精灵 */ - 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; - } - } - // ======================== 长按信息框 ======================== /** 长按时长(秒) */ diff --git a/assets/script/game/map/SkillBoxComp.ts b/assets/script/game/map/SkillBoxComp.ts index c767df99..bdd7858c 100644 --- a/assets/script/game/map/SkillBoxComp.ts +++ b/assets/script/game/map/SkillBoxComp.ts @@ -30,16 +30,17 @@ * - smc.mission —— 游戏运行状态 */ import { mLogger } from "../common/Logger"; -import { _decorator, Node, Prefab, Sprite, Label, Vec3, resources, SpriteAtlas, tween, v3, Tween, NodeEventType } from "cc"; +import { _decorator, Node, Sprite, Label, Vec3, tween, v3, Tween, NodeEventType } 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 { CardTriggerType } from "../common/config/CardSet"; +import { CardTriggerType, CardConfig } from "../common/config/CardSet"; import { SkillCardList } from "../common/config/SCardSet"; -import { SkillSet, SkillOverrides, FieldSkillSet } from "../common/config/SkillSet"; +import { SkillOverrides } from "../common/config/SkillSet"; import { oops } from "db://oops-framework/core/Oops"; import { GameEvent } from "../common/config/GameEvent"; import { smc } from "../common/SingletonModuleComp"; import { UIID } from "../common/config/GameUIConfig"; +import { getCardIconId, setSpriteFrame } from "../common/CardIconHelper"; const { ccclass, property } = _decorator; /** @@ -370,27 +371,16 @@ export class SkillBoxComp extends CCComp { // 按技能等级切换背景颜色节点(与 getLvColor 等级配色一致) this.updateBgNode(); - // 加载技能图标 - if (this.icon_node) { - // 优先使用卡牌自定义 icon;未定义则按 trigger_type 自动取 - let iconId: string | undefined = this.card_icon; - if (!iconId) { - if (this.trigger_type === CardTriggerType.Interval) { - iconId = this.s_uuid ? SkillSet[this.s_uuid]?.icon : undefined; - } else if (this.trigger_type === CardTriggerType.Field) { - const fieldUuid = this.field?.[0]; - iconId = fieldUuid ? FieldSkillSet[fieldUuid]?.icon : undefined; - } - } - if (this.icon_node && this.icon_node.isValid) { - let sprite = this.icon_node.getComponent(Sprite) || this.icon_node.addComponent(Sprite); - if (smc.uiconsAtlas && iconId) { - const frame = smc.uiconsAtlas.getSpriteFrame(iconId); - sprite.spriteFrame = frame || null; // 取不到时清空,避免残留 - } else { - sprite.spriteFrame = null; - } - } + // 加载技能图标(共享工具解析:cardData.icon → FieldSkillSet → SkillSet) + if (this.icon_node && this.icon_node.isValid) { + const cardData = { + uuid: this.s_uuid, + skill: this.s_uuid, + icon: this.card_icon, + field: this.field, + } as CardConfig; + const iconId = getCardIconId(cardData); + setSpriteFrame(this.icon_node, iconId); } // 是否需要展示剩余次数