refactor(hero-ui): 重构英雄信息面板为点击弹窗形式
本次修改完成以下核心调整: 1. 在GameUIConfig中注册HInfo弹窗的UIID与预制体路径 2. 为场上英雄节点添加点击交互,点击时打开对应英雄的信息弹窗 3. 清理MissionCardComp中常驻英雄信息面板的旧逻辑代码 4. 重构HInfoComp适配弹窗模式,支持按实体ID绑定英雄数据并实时刷新显示 5. 调整CardComp中英雄图标缩放,优化界面显示效果
This commit is contained in:
55
.trae/documents/plan_hero_ui_refactor.md
Normal file
55
.trae/documents/plan_hero_ui_refactor.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# 重构场上英雄UI表现及交互计划
|
||||||
|
|
||||||
|
## 1. 目标与现状分析
|
||||||
|
|
||||||
|
**现状**:
|
||||||
|
目前游戏中 `HInfoComp.ts` 负责在界面下方显示场上英雄的信息(生命、攻击、出售),由 `MissionCardComp` 管理 6 个固定槽位。
|
||||||
|
`HeroViewComp.ts` 负责战斗场景中英雄实体的动画表现。
|
||||||
|
|
||||||
|
**目标**:
|
||||||
|
1. 保留 `HInfoComp.ts` 组件及预制体,但**取消其在底部的常驻显示**,将其改造为**弹窗形式**(类似 `IBoxComp`)。
|
||||||
|
2. 在战斗或准备阶段,玩家**直接点击场上的英雄模型**(`HeroViewComp`)时,弹出 `HInfoComp` 面板。
|
||||||
|
3. 清理 `MissionCardComp.ts` 中管理底层 `HInfoComp` 的旧逻辑。
|
||||||
|
|
||||||
|
## 2. 具体修改步骤
|
||||||
|
|
||||||
|
### 2.1 注册 HInfo 为独立弹窗
|
||||||
|
* 修改 `assets/script/game/common/config/GameUIConfig.ts`:
|
||||||
|
* 在 `UIID` 枚举中添加 `HInfo`。
|
||||||
|
* 在 `UIConfigData` 中注册:`[UIID.HInfo]: { layer: LayerType.UI, prefab: "gui/element/hnode" }`。
|
||||||
|
|
||||||
|
### 2.2 改造 HInfoComp.ts
|
||||||
|
* **数据传入**:添加 `onAdded(args: { eid: number })`,根据 `eid` 查询 `HeroAttrsComp` 实体进行数据绑定。
|
||||||
|
* **自驱动刷新**:原先由外部驱动刷新,现在添加 `update(dt: number)` 生命周期,在内部调用 `this.refresh()` 以保持血量等信息实时更新。
|
||||||
|
* **移除旧逻辑**:删除 `node_index`、`refreshByNodeIndex` 等固定槽位相关的代码。
|
||||||
|
* **交互恢复**:取消注释 `bindEvents` 和 `unbindEvents`,恢复出售按钮的点击事件。出售完成后调用 `oops.gui.remove(UIID.HInfo)`。打开 `IBox` 的点击逻辑可保持不变(或者作为详情按钮)。
|
||||||
|
* **添加关闭机制**:考虑到它是弹窗,可以添加一个点击非按钮区域关闭自身的功能,或者点击英雄之外的区域关闭。为简单起见,可以暂时复用点击面板打开 IBox(同时关闭 HInfo),并在 HInfo 添加额外的关闭按钮,或由 UI 框架自动处理(如果注册为 PopUp 并带有背景)。如果它是纯 UI,可以点击其他地方关闭。这里我们让它在打开 `IBox` 后关闭自己:`oops.gui.remove(UIID.HInfo)`。
|
||||||
|
|
||||||
|
### 2.3 清理 MissionCardComp.ts
|
||||||
|
* **移除属性**:删除 `@property(Node) hero_info_node` 和 `@property(Prefab) hero_info_prefab` 及其编辑器绑定。
|
||||||
|
* **移除内部状态**:删除 `cachedHInfoComps` 和 `heroInfoSyncTimer`。
|
||||||
|
* **移除生命周期调用**:在 `onLoad`、`update`、`onMissionStart`、`onMissionEnd`、`onDestroy`、`reset`、`enterPreparePhase`、`enterBattlePhase` 中,删除所有涉及 `HInfoComp` 实例创建、刷新、显隐控制、销毁的代码。
|
||||||
|
|
||||||
|
### 2.4 修改 HeroViewComp.ts 添加点击交互
|
||||||
|
* **绑定事件**:在 `onLoad` 中为英雄模型节点绑定点击事件 `this.node.on(NodeEventType.TOUCH_END, this.onHeroClicked, this);`,并在 `reset` 等清理处解绑。
|
||||||
|
* **点击回调逻辑**:
|
||||||
|
```typescript
|
||||||
|
private onHeroClicked(event: EventTouch) {
|
||||||
|
if (!this.model) return;
|
||||||
|
if (this.model.fac !== FacSet.HERO) return; // 仅对玩家英雄生效
|
||||||
|
|
||||||
|
const eid = this.ent?.eid;
|
||||||
|
if (!eid) return;
|
||||||
|
|
||||||
|
// 呼出英雄信息弹窗
|
||||||
|
oops.gui.remove(UIID.HInfo);
|
||||||
|
oops.gui.open(UIID.HInfo, { eid: eid });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 验证步骤
|
||||||
|
1. 进入战斗,确认下方不再有常驻的英雄信息面板。
|
||||||
|
2. 点击场上的英雄模型,确认能弹出该英雄的 `HInfoComp` 弹窗。
|
||||||
|
3. 观察弹窗内的血量和攻击力是否能随战斗实时刷新。
|
||||||
|
4. 点击弹窗上的出售按钮,确认英雄消失、金币增加且弹窗关闭。
|
||||||
|
5. 点击弹窗上的信息区域,确认能弹出 `IBoxComp` 详情面板。
|
||||||
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
@@ -21,6 +21,7 @@ export enum UIID {
|
|||||||
Heros,
|
Heros,
|
||||||
Talents,
|
Talents,
|
||||||
Mission,
|
Mission,
|
||||||
|
HInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 打开界面方式的配置数据 */
|
/** 打开界面方式的配置数据 */
|
||||||
@@ -35,4 +36,5 @@ export var UIConfigData: { [key: number]: UIConfig } = {
|
|||||||
[UIID.Heros]: { layer: LayerType.UI, prefab: "gui/element/heros" },
|
[UIID.Heros]: { layer: LayerType.UI, prefab: "gui/element/heros" },
|
||||||
[UIID.Talents]: { layer: LayerType.UI, prefab: "gui/element/talents" },
|
[UIID.Talents]: { layer: LayerType.UI, prefab: "gui/element/talents" },
|
||||||
[UIID.Mission]: { layer: LayerType.UI, prefab: "gui/element/mission" },
|
[UIID.Mission]: { layer: LayerType.UI, prefab: "gui/element/mission" },
|
||||||
|
[UIID.HInfo]: { layer: LayerType.UI, prefab: "gui/element/hnode" },
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ export class Hero extends ecs.Entity {
|
|||||||
model.critical = HeroAttrsComp.getTalentValue(TalentType.Critical); // 暴击强化
|
model.critical = HeroAttrsComp.getTalentValue(TalentType.Critical); // 暴击强化
|
||||||
model.wfuny = HeroAttrsComp.getTalentValue(TalentType.WindFury); // 风怒强化
|
model.wfuny = HeroAttrsComp.getTalentValue(TalentType.WindFury); // 风怒强化
|
||||||
model.freeze_chance = HeroAttrsComp.getTalentValue(TalentType.Freeze); // 冰冻强化
|
model.freeze_chance = HeroAttrsComp.getTalentValue(TalentType.Freeze); // 冰冻强化
|
||||||
model.puncture = HeroAttrsComp.getTalentValue(TalentType.Puncture); // 穿刺强化
|
model.puncture_chance = HeroAttrsComp.getTalentValue(TalentType.Puncture); // 穿刺强化
|
||||||
// 护盾强化 和 亡语强化 在对应逻辑中应用
|
// 护盾强化 和 亡语强化 在对应逻辑中应用
|
||||||
} else {
|
} else {
|
||||||
model.ap = base_ap;
|
model.ap = base_ap;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Vec3, _decorator , v3,Collider2D,Contact2DType,Label ,Node,Prefab,instantiate,ProgressBar, Component, Material, Sprite, math, clamp, Game, tween, Tween, Color, BoxCollider2D, UITransform, UIOpacity} from "cc";
|
import { Vec3, _decorator , v3,Collider2D,Contact2DType,Label ,Node,Prefab,instantiate,ProgressBar, Component, Material, Sprite, math, clamp, Game, tween, Tween, Color, BoxCollider2D, UITransform, UIOpacity, NodeEventType} from "cc";
|
||||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||||
import { mLogger } from "../common/Logger";
|
import { mLogger } from "../common/Logger";
|
||||||
@@ -8,6 +8,7 @@ import { smc } from "../common/SingletonModuleComp";
|
|||||||
import { SkillSet,} from "../common/config/SkillSet";
|
import { SkillSet,} from "../common/config/SkillSet";
|
||||||
import { HeroInfo } from "../common/config/heroSet";
|
import { HeroInfo } from "../common/config/heroSet";
|
||||||
import { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
|
import { UIID } from "../common/config/GameUIConfig";
|
||||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||||
import { Tooltip } from "../skill/Tooltip";
|
import { Tooltip } from "../skill/Tooltip";
|
||||||
import { timedCom } from "../skill/timedCom";
|
import { timedCom } from "../skill/timedCom";
|
||||||
@@ -80,7 +81,22 @@ export class HeroViewComp extends CCComp {
|
|||||||
},0.1)
|
},0.1)
|
||||||
// let anm = this.node.getChildByName("anm")
|
// let anm = this.node.getChildByName("anm")
|
||||||
// anm.setScale(anm.scale.x*0.8,anm.scale.y*0.8);
|
// anm.setScale(anm.scale.x*0.8,anm.scale.y*0.8);
|
||||||
|
|
||||||
|
// 绑定点击事件,点击打开英雄信息面板弹窗
|
||||||
|
this.node.on(NodeEventType.TOUCH_END, this.onHeroClicked, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onHeroClicked() {
|
||||||
|
if (!this.model) return;
|
||||||
|
if (this.model.fac !== FacSet.HERO) return;
|
||||||
|
|
||||||
|
const eid = this.ent?.eid;
|
||||||
|
if (!eid) return;
|
||||||
|
|
||||||
|
oops.gui.remove(UIID.HInfo);
|
||||||
|
oops.gui.open(UIID.HInfo, { eid: eid });
|
||||||
|
}
|
||||||
|
|
||||||
/** 视图层逻辑代码分离演示 */
|
/** 视图层逻辑代码分离演示 */
|
||||||
start () {
|
start () {
|
||||||
this.init();
|
this.init();
|
||||||
@@ -621,6 +637,9 @@ export class HeroViewComp extends CCComp {
|
|||||||
this.damageQueue.length = 0;
|
this.damageQueue.length = 0;
|
||||||
this.isProcessingDamage = false;
|
this.isProcessingDamage = false;
|
||||||
|
|
||||||
|
// 解绑点击事件
|
||||||
|
this.node.off(NodeEventType.TOUCH_END, this.onHeroClicked, this);
|
||||||
|
|
||||||
// 节点生命周期由 Monster 对象池管理,此处不再销毁
|
// 节点生命周期由 Monster 对象池管理,此处不再销毁
|
||||||
// if (this.node && this.node.isValid) {
|
// if (this.node && this.node.isValid) {
|
||||||
// this.node.destroy();
|
// this.node.destroy();
|
||||||
|
|||||||
@@ -689,16 +689,16 @@ export class CardComp extends CCComp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.HF_node) {
|
// if (this.HF_node) {
|
||||||
this.HF_node.active = true;
|
// this.HF_node.active = true;
|
||||||
this.HF_node.children.forEach(child => {
|
// this.HF_node.children.forEach(child => {
|
||||||
child.active = (child.name === kindName);
|
// child.active = (child.name === kindName);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (this.NF_node) {
|
// if (this.NF_node) {
|
||||||
this.NF_node.active = false;
|
// this.NF_node.active = false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
const hbNodeUI = this.node.getChildByName("HB");
|
const hbNodeUI = this.node.getChildByName("HB");
|
||||||
if (hbNodeUI) hbNodeUI.active = false;
|
if (hbNodeUI) hbNodeUI.active = false;
|
||||||
@@ -753,39 +753,39 @@ export class CardComp extends CCComp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.HF_node) {
|
// if (this.HF_node) {
|
||||||
const hfTrans = this.HF_node.getComponent(UITransform);
|
// const hfTrans = this.HF_node.getComponent(UITransform);
|
||||||
if (hfTrans) {
|
// if (hfTrans) {
|
||||||
hfTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230);
|
// hfTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230);
|
||||||
const widget = this.HF_node.getComponent(Widget);
|
// const widget = this.HF_node.getComponent(Widget);
|
||||||
if (widget) widget.updateAlignment();
|
// if (widget) widget.updateAlignment();
|
||||||
}
|
// }
|
||||||
this.HF_node.children.forEach(child => {
|
// this.HF_node.children.forEach(child => {
|
||||||
const childTrans = child.getComponent(UITransform);
|
// const childTrans = child.getComponent(UITransform);
|
||||||
if (childTrans) {
|
// if (childTrans) {
|
||||||
childTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230);
|
// childTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230);
|
||||||
const widget = child.getComponent(Widget);
|
// const widget = child.getComponent(Widget);
|
||||||
if (widget) widget.updateAlignment();
|
// if (widget) widget.updateAlignment();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (this.NF_node) {
|
// if (this.NF_node) {
|
||||||
const nfTrans = this.NF_node.getComponent(UITransform);
|
// const nfTrans = this.NF_node.getComponent(UITransform);
|
||||||
if (nfTrans) {
|
// if (nfTrans) {
|
||||||
nfTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230);
|
// nfTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230);
|
||||||
const widget = this.NF_node.getComponent(Widget);
|
// const widget = this.NF_node.getComponent(Widget);
|
||||||
if (widget) widget.updateAlignment();
|
// if (widget) widget.updateAlignment();
|
||||||
}
|
// }
|
||||||
this.NF_node.children.forEach(child => {
|
// this.NF_node.children.forEach(child => {
|
||||||
const childTrans = child.getComponent(UITransform);
|
// const childTrans = child.getComponent(UITransform);
|
||||||
if (childTrans) {
|
// if (childTrans) {
|
||||||
childTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230);
|
// childTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230);
|
||||||
const widget = child.getComponent(Widget);
|
// const widget = child.getComponent(Widget);
|
||||||
if (widget) widget.updateAlignment();
|
// if (widget) widget.updateAlignment();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
const hbNode = this.node.getChildByName("HB");
|
const hbNode = this.node.getChildByName("HB");
|
||||||
if (hbNode) {
|
if (hbNode) {
|
||||||
@@ -892,10 +892,12 @@ export class CardComp extends CCComp {
|
|||||||
// ---- 图标 ----
|
// ---- 图标 ----
|
||||||
const iconNode = this.icon_node as Node;
|
const iconNode = this.icon_node as Node;
|
||||||
if (this.card_type === CardType.Hero) {
|
if (this.card_type === CardType.Hero) {
|
||||||
|
iconNode.setScale(new Vec3(-1.5, 1.5, 1));
|
||||||
// 英雄卡使用 AnimationClip,加载 idle 动画
|
// 英雄卡使用 AnimationClip,加载 idle 动画
|
||||||
this.updateHeroAnimation(iconNode, this.card_uuid, this.iconVisualToken);
|
this.updateHeroAnimation(iconNode, this.card_uuid, this.iconVisualToken);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
iconNode.setScale(new Vec3(1, 1, 1));
|
||||||
// 非英雄卡使用静态图标
|
// 非英雄卡使用静态图标
|
||||||
this.clearIconAnimation(iconNode);
|
this.clearIconAnimation(iconNode);
|
||||||
const iconId = this.resolveCardIconId(this.card_type, this.card_uuid);
|
const iconId = this.resolveCardIconId(this.card_type, this.card_uuid);
|
||||||
@@ -1005,38 +1007,38 @@ export class CardComp extends CCComp {
|
|||||||
if (childWidget) childWidget.updateAlignment();
|
if (childWidget) childWidget.updateAlignment();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.HF_node) {
|
// if (this.HF_node) {
|
||||||
const hfTrans = this.HF_node.getComponent(UITransform);
|
// const hfTrans = this.HF_node.getComponent(UITransform);
|
||||||
if (hfTrans) {
|
// if (hfTrans) {
|
||||||
hfTrans.setContentSize(170, 230);
|
// hfTrans.setContentSize(170, 230);
|
||||||
const widget = this.HF_node.getComponent(Widget);
|
// const widget = this.HF_node.getComponent(Widget);
|
||||||
if (widget) widget.updateAlignment();
|
// if (widget) widget.updateAlignment();
|
||||||
}
|
// }
|
||||||
this.HF_node.children.forEach(child => {
|
// this.HF_node.children.forEach(child => {
|
||||||
const childTrans = child.getComponent(UITransform);
|
// const childTrans = child.getComponent(UITransform);
|
||||||
if (childTrans) {
|
// if (childTrans) {
|
||||||
childTrans.setContentSize(170, 230);
|
// childTrans.setContentSize(170, 230);
|
||||||
const widget = child.getComponent(Widget);
|
// const widget = child.getComponent(Widget);
|
||||||
if (widget) widget.updateAlignment();
|
// if (widget) widget.updateAlignment();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
if (this.NF_node) {
|
// if (this.NF_node) {
|
||||||
const nfTrans = this.NF_node.getComponent(UITransform);
|
// const nfTrans = this.NF_node.getComponent(UITransform);
|
||||||
if (nfTrans) {
|
// if (nfTrans) {
|
||||||
nfTrans.setContentSize(170, 230);
|
// nfTrans.setContentSize(170, 230);
|
||||||
const widget = this.NF_node.getComponent(Widget);
|
// const widget = this.NF_node.getComponent(Widget);
|
||||||
if (widget) widget.updateAlignment();
|
// if (widget) widget.updateAlignment();
|
||||||
}
|
// }
|
||||||
this.NF_node.children.forEach(child => {
|
// this.NF_node.children.forEach(child => {
|
||||||
const childTrans = child.getComponent(UITransform);
|
// const childTrans = child.getComponent(UITransform);
|
||||||
if (childTrans) {
|
// if (childTrans) {
|
||||||
childTrans.setContentSize(170, 230);
|
// childTrans.setContentSize(170, 230);
|
||||||
const widget = child.getComponent(Widget);
|
// const widget = child.getComponent(Widget);
|
||||||
if (widget) widget.updateAlignment();
|
// if (widget) widget.updateAlignment();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
const hbNode = this.node.getChildByName("HB");
|
const hbNode = this.node.getChildByName("HB");
|
||||||
if (hbNode) {
|
if (hbNode) {
|
||||||
const hbTrans = hbNode.getComponent(UITransform);
|
const hbTrans = hbNode.getComponent(UITransform);
|
||||||
@@ -1079,15 +1081,16 @@ export class CardComp extends CCComp {
|
|||||||
if (this.BG_node) {
|
if (this.BG_node) {
|
||||||
this.BG_node.children.forEach(child => child.active = false);
|
this.BG_node.children.forEach(child => child.active = false);
|
||||||
}
|
}
|
||||||
if (this.HF_node) {
|
// if (this.HF_node) {
|
||||||
this.HF_node.active = false;
|
// this.HF_node.active = false;
|
||||||
this.HF_node.children.forEach(child => child.active = false);
|
// this.HF_node.children.forEach(child => child.active = false);
|
||||||
}
|
// }
|
||||||
if (this.NF_node) this.NF_node.active = false;
|
// if (this.NF_node) this.NF_node.active = false;
|
||||||
if (this.lv_node) {
|
// if (this.lv_node) {
|
||||||
this.lv_node.children.forEach(child => child.active = false);
|
// this.lv_node.children.forEach(child => child.active = false);
|
||||||
}
|
// }
|
||||||
if (this.cost_node) this.cost_node.active = false;
|
if (this.cost_node) this.cost_node.active = false;
|
||||||
|
if (this.icon_node) (this.icon_node as Node).setScale(new Vec3(1, 1, 1));
|
||||||
this.clearIconAnimation(this.icon_node as Node);
|
this.clearIconAnimation(this.icon_node as Node);
|
||||||
const sprite = this.icon_node?.getComponent(Sprite) || this.icon_node?.getComponentInChildren(Sprite);
|
const sprite = this.icon_node?.getComponent(Sprite) || this.icon_node?.getComponentInChildren(Sprite);
|
||||||
if (sprite) sprite.spriteFrame = null;
|
if (sprite) sprite.spriteFrame = null;
|
||||||
|
|||||||
@@ -64,9 +64,6 @@ export class HInfoComp extends CCComp {
|
|||||||
@property(Node)
|
@property(Node)
|
||||||
lv_node=null!
|
lv_node=null!
|
||||||
|
|
||||||
@property(CCInteger)
|
|
||||||
node_index=0
|
|
||||||
|
|
||||||
/** 绑定的英雄 ECS 实体 ID */
|
/** 绑定的英雄 ECS 实体 ID */
|
||||||
private eid: number = 0;
|
private eid: number = 0;
|
||||||
/** 绑定的英雄属性数据模型引用 */
|
/** 绑定的英雄属性数据模型引用 */
|
||||||
@@ -84,10 +81,44 @@ export class HInfoComp extends CCComp {
|
|||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.cacheLabels();
|
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() {
|
onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
this.unbindEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -102,54 +133,6 @@ export class HInfoComp extends CCComp {
|
|||||||
this.refresh();
|
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. 根据英雄等级切换高级 / 普通边框。
|
* 1. 根据英雄等级切换高级 / 普通边框。
|
||||||
@@ -268,34 +251,42 @@ export class HInfoComp extends CCComp {
|
|||||||
[...clips].forEach(clip => anim.removeClip(clip, true));
|
[...clips].forEach(clip => anim.removeClip(clip, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 交互(当前已注释) ========================
|
// ======================== 交互 ========================
|
||||||
|
|
||||||
// private bindEvents() {
|
private bindEvents() {
|
||||||
// this.sell_node?.on(Button.EventType.CLICK, this.onSellHero, this);
|
this.sell_node?.on(Button.EventType.CLICK, this.onSellHero, this);
|
||||||
// this.node.on(NodeEventType.TOUCH_END, this.onOpenIBox, this);
|
this.node.on(NodeEventType.TOUCH_END, this.onOpenIBox, this);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// private unbindEvents() {
|
private unbindEvents() {
|
||||||
// this.sell_node?.off(Button.EventType.CLICK, this.onSellHero, this);
|
if (this.sell_node && this.sell_node.isValid) {
|
||||||
// this.node.off(NodeEventType.TOUCH_END, this.onOpenIBox, this);
|
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)。
|
* 点击面板时打开英雄详情弹窗(IBox)。
|
||||||
* 传入英雄 UUID、等级和技能列表。
|
* 传入英雄 UUID、等级和技能列表。
|
||||||
*/
|
*/
|
||||||
private onOpenIBox() {
|
private onOpenIBox() {
|
||||||
if (!this.model) return;
|
// if (this.isClosing) return;
|
||||||
if (!this.isModelAlive()) return;
|
// if (!this.model) return;
|
||||||
const heroUuid = this.model.hero_uuid ?? 0;
|
// if (!this.isModelAlive()) return;
|
||||||
if (!heroUuid || !HeroInfo[heroUuid]) return;
|
// const heroUuid = this.model.hero_uuid ?? 0;
|
||||||
const heroLv = Math.max(1, Math.floor(this.model.lv ?? 1));
|
// if (!heroUuid || !HeroInfo[heroUuid]) return;
|
||||||
oops.gui.remove(UIID.IBox);
|
// const heroLv = Math.max(1, Math.floor(this.model.lv ?? 1));
|
||||||
oops.gui.open(UIID.IBox, {
|
|
||||||
heroUuid,
|
// this.isClosing = true;
|
||||||
heroLv,
|
// oops.gui.remove(UIID.HInfo); // 打开 IBox 前关闭自身
|
||||||
skills: this.model.skills
|
// 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) {
|
private onSellHero(event?: Event) {
|
||||||
|
if (this.isClosing) return;
|
||||||
if (!this.eid) return;
|
if (!this.eid) return;
|
||||||
const heroLv = Math.max(1, Math.floor(this.model?.lv ?? 1));
|
const heroLv = Math.max(1, Math.floor(this.model?.lv ?? 1));
|
||||||
const removed = Hero.removeByEid(this.eid);
|
const removed = Hero.removeByEid(this.eid);
|
||||||
@@ -317,7 +309,8 @@ export class HInfoComp extends CCComp {
|
|||||||
// 使用统一经济管理入口出售英雄(按等级计算卖价)
|
// 使用统一经济管理入口出售英雄(按等级计算卖价)
|
||||||
MissionEconomy.executeSellHero(heroLv);
|
MissionEconomy.executeSellHero(heroLv);
|
||||||
|
|
||||||
oops.gui.remove(UIID.IBox);
|
this.isClosing = true;
|
||||||
|
oops.gui.remove(UIID.HInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ECS 组件移除时的释放钩子:清理动画资源并销毁节点 */
|
/** ECS 组件移除时的释放钩子:清理动画资源并销毁节点 */
|
||||||
@@ -327,6 +320,9 @@ export class HInfoComp extends CCComp {
|
|||||||
this.iconHeroUuid = 0;
|
this.iconHeroUuid = 0;
|
||||||
this.model = null;
|
this.model = null;
|
||||||
this.eid = 0;
|
this.eid = 0;
|
||||||
this.node.destroy();
|
// 弹窗节点的生命周期由 oops.gui 统一管理,此处不再主动销毁节点
|
||||||
|
// if (this.node && this.node.isValid) {
|
||||||
|
// this.node.destroy();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import { CARD_POOL_INIT_LEVEL, CARD_POOL_MAX_LEVEL, CARD_POOL_UPGRADE_DISCOUNT_P
|
|||||||
import { CardComp } from "./CardComp";
|
import { CardComp } from "./CardComp";
|
||||||
import { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||||
import { HInfoComp } from "./HInfoComp";
|
|
||||||
import { smc } from "../common/SingletonModuleComp";
|
import { smc } from "../common/SingletonModuleComp";
|
||||||
import { HeroInfo, HType } from "../common/config/heroSet";
|
import { HeroInfo, HType } from "../common/config/heroSet";
|
||||||
import { HeroViewComp } from "../hero/HeroViewComp";
|
import { HeroViewComp } from "../hero/HeroViewComp";
|
||||||
@@ -107,12 +106,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
/** 卡池等级显示节点 */
|
/** 卡池等级显示节点 */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
pool_lv_node:Node = null!
|
pool_lv_node:Node = null!
|
||||||
/** 场上英雄信息面板容器节点(HInfoComp 实例的父节点) */
|
|
||||||
@property(Node)
|
|
||||||
hero_info_node:Node = null!
|
|
||||||
/** 英雄信息面板预制体(每个英雄上场时实例化一份) */
|
|
||||||
@property(Prefab)
|
|
||||||
hero_info_prefab:Prefab=null!
|
|
||||||
/** 英雄数量显示节点(含 icon + num 子节点) */
|
/** 英雄数量显示节点(含 icon + num 子节点) */
|
||||||
@property(Node)
|
@property(Node)
|
||||||
hero_num_node:Node=null!
|
hero_num_node:Node=null!
|
||||||
@@ -126,12 +119,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
private cardComps: CardComp[] = [];
|
private cardComps: CardComp[] = [];
|
||||||
/** 当前卡池等级(仅影响抽卡来源,不直接改卡槽现有内容) */
|
/** 当前卡池等级(仅影响抽卡来源,不直接改卡槽现有内容) */
|
||||||
private poolLv: number = CARD_POOL_INIT_LEVEL;
|
private poolLv: number = CARD_POOL_INIT_LEVEL;
|
||||||
/** 英雄信息面板项间距(像素) */
|
|
||||||
private readonly heroInfoItemGap: number = 135;
|
|
||||||
/** 英雄信息面板项间额外间距(像素) */
|
|
||||||
private readonly heroInfoItemSpacing: number = 5;
|
|
||||||
/** 英雄信息面板同步计时器(降频刷新用) */
|
|
||||||
private heroInfoSyncTimer: number = 0;
|
|
||||||
/** 是否已缓存卡牌面板基准缩放 */
|
/** 是否已缓存卡牌面板基准缩放 */
|
||||||
private hasCachedCardsBaseScale: boolean = false;
|
private hasCachedCardsBaseScale: boolean = false;
|
||||||
/** 卡牌面板基准缩放(从场景读取) */
|
/** 卡牌面板基准缩放(从场景读取) */
|
||||||
@@ -142,8 +129,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
private cardsHideScale: Vec3 = new Vec3(0, 0, 1);
|
private cardsHideScale: Vec3 = new Vec3(0, 0, 1);
|
||||||
/** 卡牌原始定位点 */
|
/** 卡牌原始定位点 */
|
||||||
private cardsPos = [-260,-75,108,260]
|
private cardsPos = [-260,-75,108,260]
|
||||||
/** 缓存预先放置的 6 个 HInfoComp */
|
|
||||||
private cachedHInfoComps: Map<number, HInfoComp> = new Map();
|
|
||||||
// ======================== 生命周期 ========================
|
// ======================== 生命周期 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,7 +141,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
* 5. 触发首次任务开始流程。
|
* 5. 触发首次任务开始流程。
|
||||||
*/
|
*/
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.cacheHInfoComps();
|
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
this.cacheCardComps();
|
this.cacheCardComps();
|
||||||
this.layoutCardSlots();
|
this.layoutCardSlots();
|
||||||
@@ -167,19 +152,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private cacheHInfoComps() {
|
|
||||||
this.cachedHInfoComps.clear();
|
|
||||||
if (!this.hero_info_node) return;
|
|
||||||
for (let i = 0; i < this.hero_info_node.children.length; i++) {
|
|
||||||
const child = this.hero_info_node.children[i];
|
|
||||||
const comp = (child.getComponent(HInfoComp) || child.getComponent("HInfoComp")) as HInfoComp;
|
|
||||||
if (comp && comp.node_index > 0) {
|
|
||||||
this.cachedHInfoComps.set(comp.node_index, comp);
|
|
||||||
child.active = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 组件销毁时解绑所有事件并清理英雄信息面板 */
|
/** 组件销毁时解绑所有事件并清理英雄信息面板 */
|
||||||
onDestroy() {
|
onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
@@ -189,7 +161,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.cards_chou.off(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, this);
|
this.cards_chou.off(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, this);
|
||||||
}
|
}
|
||||||
this.unbindEvents();
|
this.unbindEvents();
|
||||||
this.clearHeroInfoPanels();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 外部初始化入口(由 CardController 调用) */
|
/** 外部初始化入口(由 CardController 调用) */
|
||||||
@@ -223,7 +194,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.cacheCardComps();
|
this.cacheCardComps();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clearHeroInfoPanels();
|
|
||||||
this.layoutCardSlots();
|
this.layoutCardSlots();
|
||||||
this.clearAllCards();
|
this.clearAllCards();
|
||||||
// if (this.cards_up) {
|
// if (this.cards_up) {
|
||||||
@@ -246,7 +216,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
/** 任务结束:清空 4 槽 + 英雄面板并隐藏整个节点 */
|
/** 任务结束:清空 4 槽 + 英雄面板并隐藏整个节点 */
|
||||||
onMissionEnd() {
|
onMissionEnd() {
|
||||||
this.clearAllCards();
|
this.clearAllCards();
|
||||||
this.clearHeroInfoPanels();
|
|
||||||
if (this.node && this.node.isValid) {
|
if (this.node && this.node.isValid) {
|
||||||
this.node.active = false;
|
this.node.active = false;
|
||||||
}
|
}
|
||||||
@@ -260,20 +229,8 @@ export class MissionCardComp extends CCComp {
|
|||||||
* 检测已死亡 / 已失效的面板并移除,刷新存活面板属性。
|
* 检测已死亡 / 已失效的面板并移除,刷新存活面板属性。
|
||||||
*/
|
*/
|
||||||
update(dt: number) {
|
update(dt: number) {
|
||||||
this.heroInfoSyncTimer += dt;
|
|
||||||
if (this.heroInfoSyncTimer < 0.15) return;
|
|
||||||
this.heroInfoSyncTimer = 0;
|
|
||||||
|
|
||||||
// 遍历所有预设的 HInfoComp,让其根据 node_index 自己刷新
|
|
||||||
this.cachedHInfoComps.forEach(comp => {
|
|
||||||
if (comp && comp.isValid) {
|
|
||||||
comp.refreshByNodeIndex();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** 关闭面板(不销毁数据模型,仅隐藏) */
|
/** 关闭面板(不销毁数据模型,仅隐藏) */
|
||||||
close() {
|
close() {
|
||||||
if (this.node && this.node.isValid) {
|
if (this.node && this.node.isValid) {
|
||||||
@@ -647,12 +604,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.cards_node.active = true;
|
this.cards_node.active = true;
|
||||||
Tween.stopAllByTarget(this.cards_node);
|
Tween.stopAllByTarget(this.cards_node);
|
||||||
this.cards_node.setScale(this.cardsShowScale);
|
this.cards_node.setScale(this.cardsShowScale);
|
||||||
|
|
||||||
this.cachedHInfoComps.forEach(comp => {
|
|
||||||
if (comp && comp.isValid) {
|
|
||||||
comp.setBattlePhase(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enterBattlePhase() {
|
private enterBattlePhase() {
|
||||||
@@ -668,12 +619,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
// .start();
|
// .start();
|
||||||
|
|
||||||
this.cachedHInfoComps.forEach(comp => {
|
|
||||||
if (comp && comp.isValid) {
|
|
||||||
comp.setBattlePhase(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 构建本次抽卡结果,保证最终可分发3条数据 */
|
/** 构建本次抽卡结果,保证最终可分发3条数据 */
|
||||||
@@ -874,21 +819,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
return Math.max(0, baseCost - discount);
|
return Math.max(0, baseCost - discount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private clearHeroInfoPanels() {
|
|
||||||
if (this.cachedHInfoComps) {
|
|
||||||
this.cachedHInfoComps.forEach(comp => {
|
|
||||||
if (comp && comp.node && comp.node.isValid) {
|
|
||||||
comp.node.active = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.heroInfoSyncTimer = 0;
|
|
||||||
this.syncMissionHeroData(0);
|
|
||||||
this.updateHeroNumUI(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setHeroMaxCount(max: number) {
|
public setHeroMaxCount(max: number) {
|
||||||
const missionData = this.getMissionData();
|
const missionData = this.getMissionData();
|
||||||
if (!missionData) return;
|
if (!missionData) return;
|
||||||
@@ -1055,7 +985,6 @@ export class MissionCardComp extends CCComp {
|
|||||||
|
|
||||||
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
|
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
|
||||||
reset() {
|
reset() {
|
||||||
this.clearHeroInfoPanels();
|
|
||||||
this.resetButtonScale(this.cards_chou);
|
this.resetButtonScale(this.cards_chou);
|
||||||
// this.resetButtonScale(this.cards_up);
|
// this.resetButtonScale(this.cards_up);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user