Files
pixelheros/assets/script/game/map/HerosListComp.ts
pan c183ae464a fix(HerosListComp): 补刷英雄升级红点在页面重新显示时
页面隐藏期间会错过HERO_GROWTH_UPDATE事件,重新显示时补刷所有卡片的可升级状态,首次onEnable早于start时遍历空数组无风险
2026-09-20 14:15:19 +08:00

176 lines
5.7 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 HerosListComp.ts
* @description 英雄图鉴页面(UI 视图层)
*
* 职责:
* 1. 以卡片列表形式展示所有可用英雄(使用 CardLiteComp 预制体)。
* 2. 点击卡片打开英雄详情弹窗(UIID.HeroInfo),详情/升级在 HeroInfoComp 中处理。
* 3. 卡片排序:仅按品质降序 → uuid 升序(与养成进度解耦,保持展示稳定)。
*
* 关键设计:
* - cards_node 为卡片容器,通过 instantiate(card_lite_prefab) 动态生成卡片。
* - 升级/碎片变动后通过 HERO_GROWTH_UPDATE 事件统一刷新所有卡片的可升级标识。
* - 详情面板已从本组件剥离,由 HeroInfoComp 承载。
* - 排序不依赖 smc,仅读取静态配置(HeroInfo.rarity)。
*
* 依赖:
* - HeroInfo / HeroList(heroSet)—— 英雄静态配置与全量 UUID 列表
* - CardLiteComp —— 轻量卡片组件
* - UIID.HeroInfo(oops.gui)—— 英雄详情弹窗
*/
import { _decorator, Node, Prefab, UITransform, instantiate } 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 { oops } from "db://oops-framework/core/Oops";
import { mLogger } from "../common/Logger";
import { HeroInfo, HeroList } from "../common/config/heroSet";
import { GameEvent } from "../common/config/GameEvent";
import { UIID } from "../common/config/GameUIConfig";
import { CardLiteComp } from "./CardLiteComp";
const { property, ccclass } = _decorator;
@ccclass('HerosListComp')
@ecs.register('HerosListComp', false)
export class HerosListComp extends CCComp {
// ======================== 编辑器绑定节点 ========================
@property(Node)
cards_node = null!
@property(Prefab)
card_lite_prefab = null!
/** 卡片宽度 */
@property
card_w = 120
/** 卡片高度 */
@property
card_h = 200
/** 卡片横向间隔 */
@property
spacing_x = 10
/** 卡片纵向间隔 */
@property
spacing_y = 10
/** 每行卡片数(竖向滚动时按列排列) */
@property
cols = 4
/** 卡片左右边距 */
@property
margin_x = 15
/** 卡片上下边距 */
@property
margin_y = 50
// ======================== 运行时状态 ========================
debugMode: boolean = false
start() {
this.initCardList()
}
protected onEnable(): void {
oops.message.on(GameEvent.HERO_GROWTH_UPDATE, this.onGrowthUpdate, this)
// 页面隐藏期间会错过事件(战斗结算发碎片时本节点 active=false 已注销监听),
// 重新显示时必须补刷;首次 onEnable 早于 start,卡片未创建时 forEach 空数组安全
this.refreshAllCardsCanUp()
}
protected onDisable(): void {
oops.message.off(GameEvent.HERO_GROWTH_UPDATE, this.onGrowthUpdate, this)
}
private onGrowthUpdate() {
this.refreshAllCardsCanUp()
}
/** 升级/碎片变动后统一刷新所有卡片的可升级标识与等级数字 */
private refreshAllCardsCanUp() {
if (!this.cards_node) return
this.cards_node.children.forEach(cardNode => {
const comp = cardNode.getComponent(CardLiteComp)
comp?.refreshCanUp()
comp?.refreshLv()
})
}
closeHeros() {
this.node.active = false
}
// ======================== 卡片列表 ========================
private initCardList() {
if (!this.cards_node || !this.card_lite_prefab) return
this.cards_node.removeAllChildren()
// 图鉴排序:仅按品质降序 → uuid 升序(与养成进度解耦,保持展示稳定)
const sorted = [...HeroList].sort((a, b) => {
const ra = HeroInfo[a]?.rarity ?? 1;
const rb = HeroInfo[b]?.rarity ?? 1;
if (ra !== rb) return rb - ra;
return a - b;
})
for (const uuid of sorted) {
const hero = HeroInfo[uuid]
if (!hero) continue
const cardNode = instantiate(this.card_lite_prefab)
cardNode.name = `card_${uuid}`
const comp = cardNode.getComponent(CardLiteComp) || cardNode.addComponent(CardLiteComp)
comp.setHeroByUuid(uuid)
comp.onClickCallback = () => {
// 点击卡片:打开英雄详情弹窗(详情/升级/前后切换由 HeroInfoComp 承载)
oops.audio.playEffect("music/button");
oops.gui.open(UIID.HeroInfo, { uuid })
}
this.cards_node.addChild(cardNode)
}
mLogger.log(this.debugMode, "HerosListComp", "initCardList done", {
totalChildren: this.cards_node.children.length,
})
this.updateContentSize()
}
private updateContentSize() {
const total = this.cards_node.children.length
if (total === 0) return
// 竖向滚动:宽度固定(每行 cols 列),高度按行数累计
const rows = Math.ceil(total / this.cols)
// 宽度 = 每行卡片总宽 + 横向间隔 + 左右边距
const contentW = this.cols * this.card_w
+ (this.cols - 1) * this.spacing_x
+ this.margin_x * 2
// 高度 = 行数 × 卡片高 + 纵向间隔 + 上下边距
const contentH = rows * this.card_h
+ (rows - 1) * this.spacing_y
+ this.margin_y * 2
const uiTrans = this.cards_node.getComponent(UITransform)
if (uiTrans) {
uiTrans.setContentSize(contentW, contentH)
}
}
reset() {
this.node.destroy()
}
}