2 Commits

Author SHA1 Message Date
pan
0ccf8abcf4 feat(herolist&skillbox): 新增英雄图鉴技能图标展示功能
1. 为SSkillBoxComp新增英雄图鉴专用初始化方法,支持区分技能/驻场光环图标来源
2. 扩展技能背景色适配,支持5档英雄技能等级配色
3. 在HerosListComp中实现英雄技能图标列表刷新逻辑,支持大招和各类触发技能展示
4. 按英雄等级动态显示技能解锁状态和对应等级图标
2026-08-25 10:55:31 +08:00
pan
a8aa876a74 feat: 新增技能图标组件与UI优化
1. 新增SSkillBoxComp技能小图标组件,支持技能展示与状态控制
2. 优化CardLiteComp选中态动画,改为缩放高亮效果
3. 为英雄列表、任务主页添加顶部栏显示控制逻辑
4. 调整部分预制件布局与节点配置
2026-08-25 10:50:28 +08:00
10 changed files with 5455 additions and 791 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "c887ba55-bb80-4c4b-b0e1-0d265b3f58e0",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "sibox"
}
}

View File

@@ -22,10 +22,10 @@
"__id__": 2
},
{
"__id__": 268
"__id__": 285
},
{
"__id__": 277
"__id__": 267
}
],
"_active": true,
@@ -107,7 +107,7 @@
}
],
"_prefab": {
"__id__": 267
"__id__": 284
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -3802,6 +3802,9 @@
"tals_btn": {
"__id__": 212
},
"top_bar_node": {
"__id__": 267
},
"heros_page": {
"__id__": 88
},
@@ -3817,6 +3820,214 @@
"__type__": "cc.CompPrefabInfo",
"fileId": "96KsLaZWJF7YqZMqE+112D"
},
{
"__type__": "cc.Node",
"_objFlags": 0,
"_parent": {
"__id__": 1
},
"_prefab": {
"__id__": 268
},
"__editorExtras__": {}
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 267
},
"asset": {
"__uuid__": "56aee962-4a5e-45ae-a779-999444d06d18",
"__expectedType__": "cc.Prefab"
},
"fileId": "cboM54s0hM07XCtrpFp0/b",
"instance": {
"__id__": 269
},
"targetOverrides": null
},
{
"__type__": "cc.PrefabInstance",
"fileId": "2fMXacmbRNHo6Tx0XZe8OP",
"prefabRootNode": {
"__id__": 1
},
"mountedChildren": [],
"mountedComponents": [],
"propertyOverrides": [
{
"__id__": 270
},
{
"__id__": 272
},
{
"__id__": 273
},
{
"__id__": 274
},
{
"__id__": 275
},
{
"__id__": 277
},
{
"__id__": 278
},
{
"__id__": 279
},
{
"__id__": 280
},
{
"__id__": 282
}
],
"removedComponents": []
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 271
},
"propertyPath": [
"_name"
],
"value": "top"
},
{
"__type__": "cc.TargetInfo",
"localID": [
"cboM54s0hM07XCtrpFp0/b"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 271
},
"propertyPath": [
"_lpos"
],
"value": {
"__type__": "cc.Vec3",
"x": 0,
"y": 1175,
"z": 0
}
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 271
},
"propertyPath": [
"_lrot"
],
"value": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
}
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 271
},
"propertyPath": [
"_euler"
],
"value": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
}
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 276
},
"propertyPath": [
"_top"
],
"value": 70
},
{
"__type__": "cc.TargetInfo",
"localID": [
"52mhkKH89DVKECWS3HY4Qh"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 276
},
"propertyPath": [
"_alignFlags"
],
"value": 17
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 276
},
"propertyPath": [
"_bottom"
],
"value": 1193.667
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 271
},
"propertyPath": [
"_active"
],
"value": true
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 281
},
"propertyPath": [
"_active"
],
"value": false
},
{
"__type__": "cc.TargetInfo",
"localID": [
"71PjuREPhL54gIRBLnZ//L"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 283
},
"propertyPath": [
"_active"
],
"value": false
},
{
"__type__": "cc.TargetInfo",
"localID": [
"41v9yb+khAT5fF0JOnsr2d"
]
},
{
"__type__": "cc.PrefabInfo",
"root": {
@@ -3837,14 +4048,14 @@
"__id__": 1
},
"_prefab": {
"__id__": 269
"__id__": 286
},
"__editorExtras__": {}
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 268
"__id__": 285
},
"asset": {
"__uuid__": "26bff847-cd29-48a5-bbfa-c3e2dbda688d",
@@ -3852,7 +4063,7 @@
},
"fileId": "5a9CMsVQhKP5Y+UJfTKPbx",
"instance": {
"__id__": 270
"__id__": 287
},
"targetOverrides": null
},
@@ -3866,19 +4077,19 @@
"mountedComponents": [],
"propertyOverrides": [
{
"__id__": 271
"__id__": 288
},
{
"__id__": 273
"__id__": 290
},
{
"__id__": 274
"__id__": 291
},
{
"__id__": 275
"__id__": 292
},
{
"__id__": 276
"__id__": 293
}
],
"removedComponents": []
@@ -3886,7 +4097,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 272
"__id__": 289
},
"propertyPath": [
"_name"
@@ -3902,7 +4113,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 272
"__id__": 289
},
"propertyPath": [
"_lpos"
@@ -3917,7 +4128,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 272
"__id__": 289
},
"propertyPath": [
"_lrot"
@@ -3933,7 +4144,7 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 272
"__id__": 289
},
"propertyPath": [
"_euler"
@@ -3948,221 +4159,13 @@
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 272
"__id__": 289
},
"propertyPath": [
"_active"
],
"value": false
},
{
"__type__": "cc.Node",
"_objFlags": 0,
"_parent": {
"__id__": 1
},
"_prefab": {
"__id__": 278
},
"__editorExtras__": {}
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 277
},
"asset": {
"__uuid__": "56aee962-4a5e-45ae-a779-999444d06d18",
"__expectedType__": "cc.Prefab"
},
"fileId": "cboM54s0hM07XCtrpFp0/b",
"instance": {
"__id__": 279
},
"targetOverrides": null
},
{
"__type__": "cc.PrefabInstance",
"fileId": "2fMXacmbRNHo6Tx0XZe8OP",
"prefabRootNode": {
"__id__": 1
},
"mountedChildren": [],
"mountedComponents": [],
"propertyOverrides": [
{
"__id__": 280
},
{
"__id__": 282
},
{
"__id__": 283
},
{
"__id__": 284
},
{
"__id__": 285
},
{
"__id__": 287
},
{
"__id__": 288
},
{
"__id__": 289
},
{
"__id__": 290
},
{
"__id__": 292
}
],
"removedComponents": []
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 281
},
"propertyPath": [
"_name"
],
"value": "top"
},
{
"__type__": "cc.TargetInfo",
"localID": [
"cboM54s0hM07XCtrpFp0/b"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 281
},
"propertyPath": [
"_lpos"
],
"value": {
"__type__": "cc.Vec3",
"x": 0,
"y": 1175,
"z": 0
}
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 281
},
"propertyPath": [
"_lrot"
],
"value": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
}
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 281
},
"propertyPath": [
"_euler"
],
"value": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
}
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 286
},
"propertyPath": [
"_top"
],
"value": 70
},
{
"__type__": "cc.TargetInfo",
"localID": [
"52mhkKH89DVKECWS3HY4Qh"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 286
},
"propertyPath": [
"_alignFlags"
],
"value": 17
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 286
},
"propertyPath": [
"_bottom"
],
"value": 1193.667
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 281
},
"propertyPath": [
"_active"
],
"value": true
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 291
},
"propertyPath": [
"_active"
],
"value": false
},
{
"__type__": "cc.TargetInfo",
"localID": [
"71PjuREPhL54gIRBLnZ//L"
]
},
{
"__type__": "CCPropertyOverrideInfo",
"targetInfo": {
"__id__": 293
},
"propertyPath": [
"_active"
],
"value": false
},
{
"__type__": "cc.TargetInfo",
"localID": [
"41v9yb+khAT5fF0JOnsr2d"
]
},
{
"__type__": "cc.UITransform",
"_name": "GameUI<UITransform>",
@@ -4275,10 +4278,10 @@
],
"nestedPrefabInstanceRoots": [
{
"__id__": 277
"__id__": 267
},
{
"__id__": 268
"__id__": 285
},
{
"__id__": 212

View File

@@ -37,6 +37,9 @@ const { ccclass, property } = _decorator;
export class CardLiteComp extends CCComp {
private debugMode: boolean = false;
/** 选中态放大倍率 */
private static readonly SELECT_SCALE: number = 1.08;
// ======================== 编辑器绑定节点 ========================
@property(Node)
@@ -109,12 +112,15 @@ export class CardLiteComp extends CCComp {
}
/**
* 设置选中态:切换根节点下 `active` 子节点的显隐(不再通过缩放表达选中)。
* 设置选中态:选中卡微微放大,取消选中恢复原尺寸。
* @param selected 是否选中
*/
setSelected(selected: boolean) {
const mark = this.node.getChildByName("active");
if (mark) mark.active = selected;
Tween.stopAllByTarget(this.node);
const target = selected ? CardLiteComp.SELECT_SCALE : 1;
tween(this.node)
.to(0.12, { scale: new Vec3(target, target, 1) }, { easing: 'quadOut' })
.start();
}
// ======================== 事件绑定 ========================

View File

@@ -22,12 +22,13 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ec
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { oops } from "db://oops-framework/core/Oops";
import { mLogger } from "../common/Logger";
import { HeroInfo, HeroList } from "../common/config/heroSet";
import { HeroInfo, HeroList, SkillTriggerType, LvFieldEntry, LvReviveEntry, LvTriggerEntry, TriggerGrouped } from "../common/config/heroSet";
import { buildSkillDesc } from "../common/config/HeroSkillDesc";
import { GameEvent } from "../common/config/GameEvent";
import { HERO_UPGRADE_NEED_FRAGMENTS, CARD_LV_BG_COLORS } from "../common/config/GameSet";
import { CardLiteComp } from "./CardLiteComp";
import { ChipComp } from "./ChipComp";
import { SSkillBoxComp } from "./SSkillBoxComp";
import { smc } from "../common/SingletonModuleComp";
const { property, ccclass } = _decorator;
@@ -87,6 +88,17 @@ export class HerosListComp extends CCComp {
@property(Node)
chip_node = null!
/** 技能描述节点() */
@property(Node)
infos_node = null!
/** 技能图标容器节点(动态挂载 SSkillBoxComp 实例,展示当前英雄技能) */
@property(Node)
skills_node = null!
/** 技能小图标预制体(根节点挂 SSkillBoxComp) */
@property(Prefab)
skill_icon_prefab = null!
/** 升级特效动画节点(内含升级效果 Animation,点击升级时从头播放) */
@property(Node)
upgrade_anim_node = null!
@@ -405,6 +417,7 @@ export class HerosListComp extends CCComp {
this.updateTypeDisplay(hero)
this.updateHeroAnimation(uuid)
this.updateGrowthPanel(uuid)
this.updateSkillIcons(hero)
}
private updatePoolLvBg(card_lv: number) {
@@ -473,6 +486,112 @@ export class HerosListComp extends CCComp {
}
}
// ======================== 技能图标列表 ========================
/** 图鉴面板展示的触发技能类型(heroInfo 触发配置:死亡/战斗开始/光环/攻击后/受击/复活) */
private static readonly PANEL_TRIGGER_TYPES: SkillTriggerType[] = [
SkillTriggerType.Dead,
SkillTriggerType.FStart,
SkillTriggerType.Field,
SkillTriggerType.Atking,
SkillTriggerType.Atked,
SkillTriggerType.Revive,
]
/**
* 刷新当前英雄的技能图标列表:
* 1. 大招:skills[1] 及以后(首个为普攻,跳过)。
* 2. 触发技能:Dead/FStart/Field/Atking/Atked/Revive。
* 以局外等级(getHeroOuterLv)为当前等级,未达解锁档的显示 locked,
* 已激活的显示当前等级对应的技能等级。
*/
private updateSkillIcons(hero: typeof HeroInfo[number]) {
if (!this.skills_node || !this.skill_icon_prefab) return
// 清空旧图标(显式销毁,避免节点泄漏)
for (let i = this.skills_node.children.length - 1; i >= 0; i--) {
this.skills_node.children[i].destroy()
}
const lv = Math.max(1, smc.getHeroOuterLv(hero.uuid))
// ---- 大招:skills[1] 及以后 ----
Object.values(hero.skills).slice(1).forEach(hSkill => {
// 与 Hero.load 同式:技能等级 = 配置 lv + 英雄等级 - 2,> 0 才视为已解锁
const unlocked = hSkill.lv + lv - 2 > 0
// 技能等级展示与 buildSkillDesc 同规则:lv_entries 取 ≤lv 最大档的 s_lv
const sLv = hSkill.lv_entries?.length
? (hSkill.lv_entries.filter(e => e.lv <= lv).pop()?.s_lv ?? hSkill.lv)
: hSkill.lv
this.addSkillIcon(hSkill.uuid, sLv, !unlocked, false)
})
// ---- 触发技能:按 PANEL_TRIGGER_TYPES 顺序追加 ----
for (const type of HerosListComp.PANEL_TRIGGER_TYPES) {
if (type === SkillTriggerType.Field) {
this.addFieldIcons(hero[SkillTriggerType.Field], lv)
} else if (type === SkillTriggerType.Revive) {
this.addReviveIcon(hero[SkillTriggerType.Revive], lv)
} else {
this.addTriggerIcons(hero[type] as TriggerGrouped | undefined, lv)
}
}
}
/**
* 实例化技能小图标并初始化
* @param s_uuid 技能 UUID(SkillSet 或 FieldSkillSet)
* @param s_lv 当前等级对应的技能等级(locked 时为解锁预览档)
* @param locked 当前等级未激活 → 显示锁定遮罩
* @param isField true 表示驻场光环词条(图标取 FieldSkillSet)
*/
private addSkillIcon(s_uuid: number, s_lv: number, locked: boolean, isField: boolean) {
const node = instantiate(this.skill_icon_prefab)
node.name = `skill_${s_uuid}`
const comp = node.getComponent(SSkillBoxComp) || node.addComponent(SSkillBoxComp)
comp.initHeroSkill(s_uuid, s_lv, locked, isField)
this.skills_node.addChild(node)
}
/**
* 触发技能(dead/fstart/atking/atked):按 s_uuid 分组,取 ≤lv 最大档判定激活;
* 未激活时预览最低档(与 resolveTriggerByLv 同语义)
*/
private addTriggerIcons(grouped: TriggerGrouped | undefined, lv: number) {
if (!grouped) return
for (const key in grouped) {
const entries = grouped[key]
if (!entries?.length) continue
const sorted = [...entries].sort((a, b) => a.lv - b.lv)
let active: LvTriggerEntry | undefined
for (const e of sorted) { if (e.lv <= lv) active = e }
const pick = active ?? sorted[0]
this.addSkillIcon(Number(key), pick.s_lv ?? 1, !active, false)
}
}
/** 驻场光环:未激活取最低档预览,激活取 ≤lv 最大档的词条列表 */
private addFieldIcons(field: LvFieldEntry[] | undefined, lv: number) {
if (!field?.length) return
const sorted = [...field].sort((a, b) => a.lv - b.lv)
let active: LvFieldEntry | undefined
for (const e of sorted) { if (e.lv <= lv) active = e }
const pick = active ?? sorted[0]
for (const entry of pick.uuids) {
this.addSkillIcon(entry.uuid, pick.lv, !active, true)
}
}
/** 复活技能:未激活取最低档预览,激活取 ≤lv 最大档 */
private addReviveIcon(revive: LvReviveEntry[] | undefined, lv: number) {
if (!revive?.length) return
const sorted = [...revive].sort((a, b) => a.lv - b.lv)
let active: LvReviveEntry | undefined
for (const e of sorted) { if (e.lv <= lv) active = e }
const pick = active ?? sorted[0]
this.addSkillIcon(pick.s_uuid, pick.lv, !active, false)
}
// ======================== 英雄动画 ========================
private updateHeroAnimation(uuid: number) {

View File

@@ -54,6 +54,9 @@ export class MissionHomeComp extends CCComp {
/** 天赋按钮节点 */
@property(Node)
tals_btn = null!
/** 顶部栏节点(仅主页显示) */
@property(Node)
top_bar_node = null!
@property(Node)
@@ -197,6 +200,9 @@ export class MissionHomeComp extends CCComp {
}
private showPage(page: Node) {
// 进入分页时隐藏顶部栏,仅主页显示
this.top_bar_node.active = false
this.heros_page.active = (page === this.heros_page)
this.talents_page.active = (page === this.talents_page)
this.ranks_page.active = (page === this.ranks_page)
@@ -208,6 +214,9 @@ export class MissionHomeComp extends CCComp {
}
private hideAllPages() {
// 回到主页时恢复顶部栏显示
this.top_bar_node.active = true
this.heros_page.active = false
this.talents_page.active = false
this.ranks_page.active = false

View File

@@ -0,0 +1,173 @@
import { _decorator, Node, Sprite, Label } 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 { EquipPoolList } from "../common/config/EquipSet";
import { SkillSet, FieldSkillSet } from "../common/config/SkillSet";
import { FieldEntry } from "../common/config/heroSet";
import { smc } from "../common/SingletonModuleComp";
const { ccclass, property } = _decorator;
/**
* 技能小图标组件
*
* 由 MissSkillsComp.addSkill() 实例化并初始化。
* 在战场上以图标 + 剩余次数的形式呈现。
*/
@ccclass('SSkillBoxComp')
@ecs.register('SSkillBoxComp', false)
export class SSkillBoxComp extends CCComp {
/** 技能图标节点 */
@property({ type: Node })
private icon_node: Node = null;
/** 技能图标节点:下面有 green blue purple red yellow 5个不同颜色节点*/
@property({ type: Node })
private bg_node: Node = null;
/** 技能等级标签 */
@property(Label)
private lv_label: Label = null;
/** 锁定遮罩节点(active 时表示技能未激活) */
@property({ type: Node })
private locked: Node = null;
/** 技能 UUID */
private s_uuid: number = 0;
/** 卡牌等级 */
private card_lv: number = 1;
/** 卡牌自定义图标ID(优先级最高,未定义则按 trigger_type 自动取) */
private card_icon?: string;
/** 触发类型(默认即时,保持向后兼容) */
private trigger_type: CardTriggerType = CardTriggerType.Instant;
/** 驻场技能词条列表(uuid 指向 FieldSkillSet 底座,value 为生效数值) */
public field: FieldEntry[] = [];
/** 英雄图鉴模式下的图标来源:skill=SkillSet,field=FieldSkillSet;undefined 表示装备卡模式 */
private hero_icon_source?: "skill" | "field";
/**
* 初始化技能卡效果:
* 1. 从 EquipPoolList 查询装备卡的触发配置(含 trigger_type)。
* 2. 更新 UI 显示(图标 + 次数)。
*
* @param uuid 卡牌 UUID
* @param card_lv 技能卡等级
*/
init(uuid: number, card_lv: number) {
this.card_lv = card_lv;
// 装备卡配置已迁移至 EquipPoolList
const config = EquipPoolList.find(c => c.uuid === uuid);
if (config) {
this.s_uuid = config.skill ?? uuid; // 获取实际的技能 UUID
this.card_icon = config.icon; // 保存卡牌自定义图标(优先级最高)
this.trigger_type = config.trigger_type ?? CardTriggerType.Instant;
} else {
this.s_uuid = uuid;
}
this.updateUI();
}
/**
* 初始化英雄技能图标(英雄图鉴面板专用):
* 图标按 s_uuid 直接取 SkillSet / FieldSkillSet 配置,不走 EquipPoolList。
* @param s_uuid 技能 UUID(大招/触发技能指向 SkillSet,光环词条指向 FieldSkillSet)
* @param s_lv 当前英雄等级下生效的技能等级(显示在等级标签)
* @param locked 当前等级未激活时显示锁定遮罩
* @param isField true 时图标取 FieldSkillSet(驻场光环词条)
*/
initHeroSkill(s_uuid: number, s_lv: number, locked: boolean, isField: boolean = false) {
this.s_uuid = s_uuid;
this.card_lv = s_lv;
this.hero_icon_source = isField ? "field" : "skill";
this.updateUI();
this.setLocked(locked);
}
/**
* 设置技能激活状态:
* - locked = true → 显示锁定遮罩,技能未激活
* - locked = false → 隐藏锁定遮罩,技能已激活
*/
setLocked(locked: boolean) {
if (this.locked) {
this.locked.active = locked;
}
}
/**
* 根据技能等级切换 bg_node 下的颜色子节点显示。
* 等级与颜色对应关系:
* 技能卡档位:card_lv 1 (wave 1) → green,2 (wave 5) → blue,3 (wave 8) → purple
* 英雄技能等级:s_lv 1~5 → green / blue / purple / red / yellow
*/
private updateBgNode() {
if (!this.bg_node) return;
const lvToColor: Record<number, string> = {
1: "green",
2: "blue",
3: "purple",
4: "red",
5: "yellow",
};
const targetColor = lvToColor[this.card_lv];
this.bg_node.children.forEach(child => {
child.active = (child.name === targetColor);
});
}
/**
* 更新 UI:
* - 背景:根据技能等级切换对应颜色子节点显示
* - 图标:从 uicons 图集获取
* - 等级标签:显示技能卡等级
*/
updateUI() {
// 按技能等级切换背景颜色节点(与 getLvColor 等级配色一致)
this.updateBgNode();
// 加载技能图标
if (this.icon_node) {
// 优先使用卡牌自定义 icon;未定义则按图鉴模式 / 装备卡 trigger_type 自动取
let iconId: string | undefined = this.card_icon;
if (!iconId) {
if (this.hero_icon_source === "field") {
// 英雄图鉴:驻场光环词条图标取 FieldSkillSet
iconId = this.s_uuid ? FieldSkillSet[this.s_uuid]?.icon : undefined;
} else if (this.hero_icon_source === "skill") {
// 英雄图鉴:大招/触发技能图标取 SkillSet
iconId = this.s_uuid ? SkillSet[this.s_uuid]?.icon : undefined;
} else 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]?.uuid;
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;
}
}
}
// 显示技能等级
if (this.lv_label) {
this.lv_label.string = `${this.card_lv}`;
}
}
/** ECS 组件移除时销毁节点 */
reset() {
if (this.node && this.node.isValid) {
this.node.destroy();
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "36150383-6711-44b9-83d7-0d7af167584d",
"files": [],
"subMetas": {},
"userData": {}
}