3 Commits

Author SHA1 Message Date
pan
1634f6a528 feat(talent-ui): 优化天赋界面展示逻辑与数据显示
1.  天赋总览页新增各层已投入点数展示
2.  天赋详情弹窗重构前置与层位校验UI:
    - 拆分层位需求与软前置展示模块
    - 优化进度显示逻辑,避免数值歧义
    - 修正前置等级展示为当前/需求格式
3.  更新相关注释与文档说明
2026-09-20 16:04:09 +08:00
pan
c183ae464a fix(HerosListComp): 补刷英雄升级红点在页面重新显示时
页面隐藏期间会错过HERO_GROWTH_UPDATE事件,重新显示时补刷所有卡片的可升级状态,首次onEnable早于start时遍历空数组无风险
2026-09-20 14:15:19 +08:00
pan
d140645aab refactor(talent): 重构天赋道具UI状态显示逻辑
1. 移除原MAX满级角标,替换为两种状态边框
2. 新增满级边框和已加点边框逻辑
3. 调整UI3布局参数优化显示
4. 更新预制件和注释文档
2026-09-20 10:55:27 +08:00
9 changed files with 9981 additions and 5399 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

File diff suppressed because it is too large Load Diff

View File

@@ -7476,7 +7476,7 @@
"height": 17,
"rawWidth": 4,
"rawHeight": 17,
"borderTop": 10,
"borderTop": 9,
"borderBottom": 7,
"borderLeft": 2,
"borderRight": 2,

View File

@@ -79,6 +79,9 @@ export class HerosListComp extends CCComp {
protected onEnable(): void {
oops.message.on(GameEvent.HERO_GROWTH_UPDATE, this.onGrowthUpdate, this)
// 页面隐藏期间会错过事件(战斗结算发碎片时本节点 active=false 已注销监听),
// 重新显示时必须补刷;首次 onEnable 早于 start,卡片未创建时 forEach 空数组安全
this.refreshAllCardsCanUp()
}
protected onDisable(): void {

View File

@@ -4,10 +4,11 @@
*
* 职责:
* 1. 展示天赋图标 / 名称 / 当前等级 / 条幅说明(getTalentDisplayText)。
* 2. 展示软前置行:前置天赋名称、需求等级、前置已达成勾图标(达成亮 On,未达成亮 Off)。
* 3. 加点按钮:仅在可加点(层位已解锁 + 前置满足 + 未满级 + 点数足够)时显示;
* 2. 展示层位需求行:前置层位名称(如:1 级天赋)、投入进度(如:5/5)、解锁勾图标(解锁亮 On,未解锁亮 Off)。
* 3. 展示软前置行:前置天赋名称、等级进度(当前/需求,如:0/5)、前置已达成勾图标(达成亮 On,未达成亮 Off)。
* 4. 加点按钮:仅在可加点(层位已解锁 + 前置满足 + 未满级 + 点数足够)时显示;
* 不满足条件或已满级时隐藏,弹窗仍可正常打开查看。
* 4. 加点成功后通过 TALENT_UPDATE 事件驱动 TalentsComp 全量刷新,并同步刷新本弹窗。
* 5. 加点成功后通过 TALENT_UPDATE 事件驱动 TalentsComp 全量刷新,并同步刷新本弹窗。
*
* 使用(oops.gui 模块):
* oops.gui.open(UIID.TIBox, { uuid: 9001 });
@@ -20,7 +21,7 @@
import { _decorator, EventTouch, Label, Node, NodeEventType, RichText, Sprite, 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 { TalentSet, TalentConfig, getTalentUpgradeCost, isTalentPrereqMet, isTalentTierUnlocked } from "../common/config/TalentSet";
import { TalentSet, TalentConfig, TalentTier, TALENT_TIER_UNLOCK_THRESHOLD, getTalentTierInvestedPoints, getTalentUpgradeCost, isTalentPrereqMet, isTalentTierUnlocked } from "../common/config/TalentSet";
import { GameEvent } from "../common/config/GameEvent";
import { HRoleName } from "../common/config/heroSet";
import { outlineRich } from "../common/RichOutline";
@@ -52,7 +53,7 @@ export class TIBoxComp extends CCComp {
@property({ type: Label, tooltip: "前置天赋名称(无前置时隐藏前置行)" })
lbl_prereq_name: Label = null!;
@property({ type: Label, tooltip: "前置天赋需求等级" })
@property({ type: Label, tooltip: "前置天赋等级进度(当前/需求,如:0/5)" })
lbl_prereq_lv: Label = null!;
@property({ type: Node, tooltip: "前置已达成勾图标(On)" })
@@ -64,6 +65,21 @@ export class TIBoxComp extends CCComp {
@property({ type: Node, tooltip: "前置行容器(无前置天赋时整体隐藏)" })
node_prereq: Node = null!;
@property({ type: Node, tooltip: "层位需求行容器(第 1 层天赋无层位需求,整体隐藏)" })
node_tier: Node = null!;
@property({ type: Label, tooltip: "前置层位名称(如:1 级天赋)" })
lbl_tier_name: Label = null!;
@property({ type: Label, tooltip: "前一层投入进度(已投入/门槛,如:5/5)" })
lbl_tier_progress: Label = null!;
@property({ type: Node, tooltip: "层位已解锁勾图标(On)" })
node_tier_check_on: Node = null!;
@property({ type: Node, tooltip: "层位未解锁勾图标(Off)" })
node_tier_check_off: Node = null!;
@property({ type: RichText, tooltip: "条幅说明(当前已生效数值 / 每级引导)" })
richtext_info: RichText = null!;
@@ -122,6 +138,21 @@ export class TIBoxComp extends CCComp {
// 条幅说明:已加点显示当前生效数值,未加点显示每级引导
if (this.richtext_info) this.richtext_info.string = outlineRich(this.buildDetailText(cfg, lv));
// 层位需求行:第 1 层恒解锁无需求,整体隐藏;其余层展示前一层名称与投入进度(如:1 级天赋 5/5)
const hasTierNeed = cfg.tier > TalentTier.L1;
if (this.node_tier) this.node_tier.active = hasTierNeed;
if (hasTierNeed) {
const prevTier = (cfg.tier - 1) as TalentTier;
const invested = getTalentTierInvestedPoints(levels, prevTier);
const tierMet = isTalentTierUnlocked(levels, cfg.tier);
if (this.lbl_tier_name) this.lbl_tier_name.string = `${prevTier} 级天赋`;
// 进度超门槛时截断显示(如已投入 7 点仍显示 5/5,避免 7/5 的歧义观感)
const shown = Math.min(invested, TALENT_TIER_UNLOCK_THRESHOLD);
if (this.lbl_tier_progress) this.lbl_tier_progress.string = `${shown}/${TALENT_TIER_UNLOCK_THRESHOLD}`;
if (this.node_tier_check_on) this.node_tier_check_on.active = tierMet;
if (this.node_tier_check_off) this.node_tier_check_off.active = !tierMet;
}
// 软前置行:无前置整体隐藏;有前置展示名称/需求等级/达成勾
const hasPrereq = !!(cfg.prereq_uuid && cfg.prereq_lv);
if (this.node_prereq) this.node_prereq.active = hasPrereq;
@@ -129,7 +160,9 @@ export class TIBoxComp extends CCComp {
const preCfg = TalentSet[cfg.prereq_uuid!];
const preMet = isTalentPrereqMet(levels, cfg);
if (this.lbl_prereq_name) this.lbl_prereq_name.string = preCfg ? preCfg.name : `天赋${cfg.prereq_uuid}`;
if (this.lbl_prereq_lv) this.lbl_prereq_lv.string = `${cfg.prereq_lv}`;
// 前置等级进度(当前/需求,如:0/5);超需求时截断显示,避免 7/5 的歧义观感
const preLv = Math.min(levels?.[cfg.prereq_uuid!] ?? 0, cfg.prereq_lv!);
if (this.lbl_prereq_lv) this.lbl_prereq_lv.string = `${preLv}/${cfg.prereq_lv}`;
if (this.node_prereq_check_on) this.node_prereq_check_on.active = preMet;
if (this.node_prereq_check_off) this.node_prereq_check_off.active = !preMet;
}

View File

@@ -6,7 +6,7 @@
* 1. 通过编辑器配置的 uuid 自查询天赋配置 + 当前等级 + 层位解锁态,渲染迷你点选卡。
* 2. 卡面自包含:图标 / 名称 / 当前已生效数值(Lv0 显示每级数值引导)/ 右下「当前/满级」角标。
* 3. 层位背景:普通背景节点下挂 blue / red / yellow 三个颜色子节点,按层位映射激活其一(L1蓝 / L2红 / L3金,L4/L5 复用金)。
* 4. 状态机:满级(MAX 金角标)/ 锁定(锁遮罩:层位未解锁或前置未满足;可加点时不显示,不做灰阶处理)。
* 4. 状态机:锁定(锁遮罩:层位未解锁或前置未满足;可加点时不显示,不做灰阶处理)/ 状态边框(满级 max 边框、已加点未满 active 边框,未加点均隐藏)。
* 5. 点击回调上抛卡体点击,加点确认弹窗与升级校验统一由 TalentsComp 处理(单一决策点)。
*
* 依赖:
@@ -55,12 +55,15 @@ export class TalentItemComp extends CCComp {
@property({ type: Node, tooltip: "卡牌背景节点(下挂 blue / red / yellow 三颜色子节点,按层位激活其一)" })
bg: Node = null!;
@property({ type: Node, tooltip: "MAX 满级角标" })
node_max: Node = null!;
@property({ type: Node, tooltip: "锁遮罩/锁图标(层位未解锁或前置未满足时显示;可加点时隐藏)" })
node_lock: Node = null!;
@property({ type: Node, tooltip: "满级边框(lv 达到 max_lv 时显示)" })
node_border_max: Node = null!;
@property({ type: Node, tooltip: "已加点边框(0 < lv < max_lv 时显示)" })
node_border_active: Node = null!;
/** 点击回调(卡体点击上抛),由 TalentsComp 统一校验升级 */
public onTalentTap: ((uuid: number) => void) | null = null;
@@ -105,9 +108,12 @@ export class TalentItemComp extends CCComp {
if (frame) this.icon.spriteFrame = frame;
}
// 状态角标:满级 MAX / 锁遮罩(层位未解锁或前置未满足)
if (this.node_max) this.node_max.active = lv >= cfg.max_lv;
// 锁遮罩:层位未解锁或前置未满足时显示
if (this.node_lock) this.node_lock.active = locked;
// 状态边框:满级显示 max 边框;已加点未满显示 active 边框;未加点均隐藏
if (this.node_border_max) this.node_border_max.active = lv >= cfg.max_lv;
if (this.node_border_active) this.node_border_active.active = lv > 0 && lv < cfg.max_lv;
}
/** 按层位激活 bg 下对应颜色子节点,其余隐藏 */

View File

@@ -3,7 +3,7 @@
* @description 天赋加点页组件(UI 视图层,最小容器)
*
* 职责:
* 1. 页头展示:可用天赋点 / 天赋等级(Lv.x)/ 当前等级经验进度。
* 1. 页头展示:可用天赋点 / 天赋等级(Lv.x)/ 当前等级经验进度 / 各层已投入点数。
* 2. 收集子树内全部 TalentItemComp(编辑器静态摆放),TALENT_UPDATE 事件驱动全量刷新。
* 3. 卡片点击统一打开 TIBox 天赋详情弹窗;加点校验(层位/前置/满级/点数)由 TIBoxComp 内聚,
* 不满足条件时弹窗隐藏加点按钮,仅作详情查看。
@@ -46,6 +46,9 @@ export class TalentsComp extends CCComp {
@property({ type: Label, tooltip: "当前等级经验数值(当前经验/升级所需经验)" })
lbl_exp: Label = null!;
@property({ type: [Label], tooltip: "各层已投入点数 Label(数组索引对应第 1~5 层)" })
lbl_tier_points: Label[] = [];
@property({ type: Node, tooltip: "看广告重置按钮" })
btn_reset: Node = null!;
@@ -88,6 +91,14 @@ export class TalentsComp extends CCComp {
this.lbl_points.string = `${remaining}/${remaining + invested}`;
}
// 各层已投入点数(数组索引对应层 1~5)
for (let tier = 1; tier <= this.lbl_tier_points.length; tier++) {
const lbl = this.lbl_tier_points[tier - 1];
if (lbl) {
lbl.string = `${getTalentTierInvestedPoints(levels, tier)}`;
}
}
// 天赋等级(Lv.x)与当前等级经验进度
const talentLv = smc.getTalentLevel();
if (this.lbl_lv) this.lbl_lv.string = `Lv.${talentLv}`;