refactor(hero-ui): 重构英雄信息面板为点击弹窗形式

本次修改完成以下核心调整:
1.  在GameUIConfig中注册HInfo弹窗的UIID与预制体路径
2.  为场上英雄节点添加点击交互,点击时打开对应英雄的信息弹窗
3.  清理MissionCardComp中常驻英雄信息面板的旧逻辑代码
4.  重构HInfoComp适配弹窗模式,支持按实体ID绑定英雄数据并实时刷新显示
5.  调整CardComp中英雄图标缩放,优化界面显示效果
This commit is contained in:
walkpan
2026-05-24 16:16:40 +08:00
parent 981f3a43b9
commit 1b26a9079d
10 changed files with 8775 additions and 5475 deletions

View File

@@ -64,9 +64,6 @@ export class HInfoComp extends CCComp {
@property(Node)
lv_node=null!
@property(CCInteger)
node_index=0
/** 绑定的英雄 ECS 实体 ID */
private eid: number = 0;
/** 绑定的英雄属性数据模型引用 */
@@ -84,10 +81,44 @@ export class HInfoComp extends CCComp {
onLoad() {
this.cacheLabels();
this.bindEvents();
}
onAdded(args: { eid: number }) {
const eid = args?.eid ?? 0;
if (!eid) return;
let foundModel: HeroAttrsComp | null = null;
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
if (entity.eid === eid) {
foundModel = entity.get(HeroAttrsComp);
}
});
if (foundModel) {
this.bindData(eid, foundModel);
} else {
this.isClosing = true;
oops.gui.remove(UIID.HInfo);
}
}
/** 是否正在关闭中,防止重复调用 remove */
private isClosing: boolean = false;
update(dt: number) {
if (this.isClosing) return;
if (!this.isModelAlive()) {
this.isClosing = true;
oops.gui.remove(UIID.HInfo);
return;
}
this.refresh();
}
onDestroy() {
super.onDestroy();
this.unbindEvents();
}
/**
@@ -102,54 +133,6 @@ export class HInfoComp extends CCComp {
this.refresh();
}
/**
* 根据 node_index 获取对应的硬编码位置,并查找该位置的英雄
*/
refreshByNodeIndex() {
if (this.node_index < 1 || this.node_index > 6) return;
const targetPos = MissionHeroComp.HERO_POSITIONS[this.node_index - 1];
let foundModel: HeroAttrsComp | null = null;
let foundEid: number = 0;
// 遍历所有英雄,查找 targetX 和 targetY 匹配该位置的英雄
ecs.query(ecs.allOf(HeroAttrsComp, MoveComp)).forEach((entity: ecs.Entity) => {
const model = entity.get(HeroAttrsComp);
const move = entity.get(MoveComp);
if (model && move && !model.is_dead && model.fac === FacSet.HERO) {
if (Math.abs(move.targetX - targetPos.x) < 2 && Math.abs(move.baseY - targetPos.y) < 2) {
foundModel = model;
foundEid = entity.eid;
}
}
});
if (foundModel) {
if (this.eid !== foundEid) {
this.bindData(foundEid, foundModel);
if (!this.node.active) this.node.active = true;
} else {
this.refresh();
}
} else {
if (this.eid !== 0) {
this.eid = 0;
this.model = null;
if (this.node.active) this.node.active = false;
}
}
}
/**
* 设置当前是否处于战斗阶段,控制出售按钮显示/隐藏
* @param isBattlePhase 是否处于战斗阶段
*/
setBattlePhase(isBattlePhase: boolean) {
if (this.sell_node && this.sell_node.isValid) {
this.sell_node.active = !isBattlePhase;
}
}
/**
* 刷新显示:
* 1. 根据英雄等级切换高级 / 普通边框。
@@ -268,34 +251,42 @@ export class HInfoComp extends CCComp {
[...clips].forEach(clip => anim.removeClip(clip, true));
}
// ======================== 交互(当前已注释) ========================
// ======================== 交互 ========================
// private bindEvents() {
// this.sell_node?.on(Button.EventType.CLICK, this.onSellHero, this);
// this.node.on(NodeEventType.TOUCH_END, this.onOpenIBox, this);
// }
private bindEvents() {
this.sell_node?.on(Button.EventType.CLICK, this.onSellHero, this);
this.node.on(NodeEventType.TOUCH_END, this.onOpenIBox, this);
}
// private unbindEvents() {
// this.sell_node?.off(Button.EventType.CLICK, this.onSellHero, this);
// this.node.off(NodeEventType.TOUCH_END, this.onOpenIBox, this);
// }
private unbindEvents() {
if (this.sell_node && this.sell_node.isValid) {
this.sell_node.off(Button.EventType.CLICK, this.onSellHero, this);
}
if (this.node && this.node.isValid) {
this.node.off(NodeEventType.TOUCH_END, this.onOpenIBox, this);
}
}
/**
* 点击面板时打开英雄详情弹窗IBox
* 传入英雄 UUID、等级和技能列表。
*/
private onOpenIBox() {
if (!this.model) return;
if (!this.isModelAlive()) return;
const heroUuid = this.model.hero_uuid ?? 0;
if (!heroUuid || !HeroInfo[heroUuid]) return;
const heroLv = Math.max(1, Math.floor(this.model.lv ?? 1));
oops.gui.remove(UIID.IBox);
oops.gui.open(UIID.IBox, {
heroUuid,
heroLv,
skills: this.model.skills
});
// if (this.isClosing) return;
// if (!this.model) return;
// if (!this.isModelAlive()) return;
// const heroUuid = this.model.hero_uuid ?? 0;
// if (!heroUuid || !HeroInfo[heroUuid]) return;
// const heroLv = Math.max(1, Math.floor(this.model.lv ?? 1));
// this.isClosing = true;
// oops.gui.remove(UIID.HInfo); // 打开 IBox 前关闭自身
// oops.gui.remove(UIID.IBox);
// oops.gui.open(UIID.IBox, {
// heroUuid,
// heroLv,
// skills: this.model.skills
// });
}
/**
@@ -303,6 +294,7 @@ export class HInfoComp extends CCComp {
* 并关闭详情弹窗。
*/
private onSellHero(event?: Event) {
if (this.isClosing) return;
if (!this.eid) return;
const heroLv = Math.max(1, Math.floor(this.model?.lv ?? 1));
const removed = Hero.removeByEid(this.eid);
@@ -317,7 +309,8 @@ export class HInfoComp extends CCComp {
// 使用统一经济管理入口出售英雄(按等级计算卖价)
MissionEconomy.executeSellHero(heroLv);
oops.gui.remove(UIID.IBox);
this.isClosing = true;
oops.gui.remove(UIID.HInfo);
}
/** ECS 组件移除时的释放钩子:清理动画资源并销毁节点 */
@@ -327,6 +320,9 @@ export class HInfoComp extends CCComp {
this.iconHeroUuid = 0;
this.model = null;
this.eid = 0;
this.node.destroy();
// 弹窗节点的生命周期由 oops.gui 统一管理,此处不再主动销毁节点
// if (this.node && this.node.isValid) {
// this.node.destroy();
// }
}
}