refactor(map): 重构英雄信息弹窗逻辑,移除oops.gui依赖

1.  将英雄信息弹窗从框架GUI系统改为同步实例化管理
2.  由HeroBoxComp接管弹窗的创建和销毁时机
3.  删除冗余的UIID配置和弹窗参数接口
4.  调整弹窗关闭逻辑,避免缓存节点带来的竞态问题
This commit is contained in:
pan
2026-08-05 18:02:35 +08:00
parent 1d4769b080
commit e2f5b05bc3
5 changed files with 2350 additions and 2653 deletions

View File

@@ -4,7 +4,7 @@
*
* 职责:
* 1. 同步显示一个已登场英雄的基础信息(图标 / 名称 / 等级 / AP / HP
* 2. 长按英雄盒弹出信息面板UIID.HeroMore → more.prefab展示次级属性与技能描述。
* 2. 长按英雄盒同步实例化信息面板(more.prefab展示次级属性与技能描述;放开手指销毁
* 3. 由 MissionCardComp 实例化到 herosBoxNode 下,最多 3 个,与场上英雄一一对应。
* 4. 英雄卖出 / 死亡后槽位保留,通过 applyEmpty() 显示为空英雄信息。
*
@@ -13,7 +13,7 @@
* - HeroInfoheroSet—— 英雄静态配置
*/
import { mLogger } from "../common/Logger";
import { _decorator, Node, Sprite, Label, resources, AnimationClip, SpriteFrame, NodeEventType, EventTouch, Tween, tween, Vec3, Color } from "cc";
import { _decorator, Node, Sprite, Label, resources, AnimationClip, SpriteFrame, NodeEventType, EventTouch, Tween, tween, Vec3, Color, Prefab, instantiate, director, find } 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";
@@ -25,6 +25,7 @@ import { FacSet, FightSet, getLvColor } from "../common/config/GameSet";
import { buildSkillDesc, ISkillDescSource } from "../common/config/HeroSkillDesc";
import { MissionEconomy } from "./MissionEconomy";
import { UIID } from "../common/config/GameUIConfig";
import { HeroMoreCom } from "./HeroMoreCom";
const { ccclass, property } = _decorator;
@@ -91,6 +92,10 @@ export class HeroBoxComp extends CCComp {
@property(Label)
private hp_plus_label: Label = null;
/** 英雄信息弹窗预制体(长按英雄盒时同步实例化展示,绕开 oops.gui 异步加载) */
@property(Prefab)
private morePrefab: Prefab = null;
// ======================== 运行时状态 ========================
/** 绑定的英雄实体 ID0 表示空槽位) */
@@ -102,6 +107,9 @@ export class HeroBoxComp extends CCComp {
/** 当前显示的英雄 UUID避免相同 UUID 重复加载动画) */
private iconHeroUuid: number = 0;
/** 当前已弹出的英雄信息面板节点null 表示未弹出) */
private moreNode: Node | null = null;
/** 长按判定:当前按下触摸 ID-1 表示无) */
private longPressTouchId: number = -1;
/** 长按判定:按下起点 UI 坐标 */
@@ -115,7 +123,7 @@ export class HeroBoxComp extends CCComp {
// ======================== 生命周期 ========================
onLoad() {
// 长按英雄盒任意区域弹出信息面板(UIID.HeroMore → more.prefab出售/升级按钮上的触摸不触发
// 长按英雄盒任意区域实例化信息面板more.prefab出售/升级按钮上的触摸不触发
this.node.on(NodeEventType.TOUCH_START, this.onBoxTouchStart, this);
this.node.on(NodeEventType.TOUCH_MOVE, this.onBoxTouchMove, this);
this.node.on(NodeEventType.TOUCH_END, this.onBoxTouchEnd, this);
@@ -144,6 +152,8 @@ export class HeroBoxComp extends CCComp {
if (this.upgrade_btn && this.upgrade_btn.isValid) {
this.upgrade_btn.off(NodeEventType.TOUCH_END, this.onUpgradeHeroClick, this);
}
// 销毁时一并清理已弹出的信息面板
this.closeMoreNode();
}
/**
@@ -350,14 +360,16 @@ export class HeroBoxComp extends CCComp {
}
}
/** 英雄盒抬起:取消未触发的长按判定 */
/** 英雄盒抬起:取消未触发的长按判定;若长按已弹出信息面板则立即关闭 */
private onBoxTouchEnd(event: EventTouch) {
if (this.longPressTouchId < 0) return;
if ((event.touch?.getID() ?? -1) !== this.longPressTouchId) return;
this.cancelLongPress();
// 直接销毁已弹出的信息面板(同步 destroy无竞态
this.closeMoreNode();
}
/** 英雄盒触摸被系统打断:取消长按判定 */
/** 英雄盒触摸被系统打断:取消长按判定;若已弹窗则一并关闭 */
private onBoxTouchCancel(event: EventTouch) {
this.onBoxTouchEnd(event);
}
@@ -368,12 +380,32 @@ export class HeroBoxComp extends CCComp {
this.unschedule(this.onLongPressFired);
}
/** 长按触发:弹出英雄信息面板more.prefab展示次级属性与技能描述 */
/** 长按触发:同步实例化英雄信息面板more.prefab展示次级属性与技能描述 */
private onLongPressFired() {
this.longPressTouchId = -1;
// 不重置 longPressTouchId:保留以便 onBoxTouchEnd 能识别同一触摸并销毁弹窗
if (!this.hasHero || !(this.model as any)?.ent) return;
if (!this.morePrefab) {
mLogger.warn(true, "HeroBoxComp", "onLongPressFired morePrefab 未绑定,请在编辑器拖入 more.prefab");
return;
}
oops.audio.playEffect("music/button");
oops.gui.open(UIID.HeroMore, { eid: this.eid });
// 同步实例化:避开 oops.gui 首次异步加载窗口期,确保放开时能立即 destroy
this.closeMoreNode();
this.moreNode = instantiate(this.morePrefab);
// more.prefab 是全屏遮罩弹窗,必须挂到 Canvas 根节点,避免被英雄盒父节点的 Mask 裁剪或坐标系错位
const canvas = find("Canvas") || director.getScene()?.getChildByName("Canvas");
(canvas ?? this.node.parent)?.addChild(this.moreNode);
// 整体上移 100 像素(弹窗相对英雄盒偏上展示)
this.moreNode.setPosition(this.moreNode.position.x, this.moreNode.position.y + 100, this.moreNode.position.z);
this.moreNode.getComponent(HeroMoreCom)?.init(this.eid);
}
/** 关闭并清理已弹出的英雄信息面板(同步 destroy */
private closeMoreNode() {
if (this.moreNode && this.moreNode.isValid) {
this.moreNode.destroy();
}
this.moreNode = null;
}
/**