feat(ui): 添加战场英雄信息面板并增强主角召唤事件
扩展主角召唤事件,传递更多实体信息供UI系统使用。新增HInfoComp组件作为英雄信息面板基础,并在MissionCardComp中动态生成和管理英雄信息面板,实时显示英雄属性。同时调整相关预制体引用和布局配置。
This commit is contained in:
@@ -35,10 +35,13 @@
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 74
|
||||
},
|
||||
{
|
||||
"__id__": 76
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 76
|
||||
"__id__": 78
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -1733,6 +1736,24 @@
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "9c8CWaboJEQ4tagNhw+LQr"
|
||||
},
|
||||
{
|
||||
"__type__": "a832fh9yR9LJK1kR+tZ1lin",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 77
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "11S1XoDG5Ndo+S7MbRH4us"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
|
||||
@@ -778,7 +778,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInstance",
|
||||
"fileId": "8el1xtaIhFn4eJMCp78t65",
|
||||
"fileId": "b1fQvsJE5Axp3W57Iu81HO",
|
||||
"prefabRootNode": {
|
||||
"__id__": 1
|
||||
},
|
||||
@@ -890,7 +890,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInstance",
|
||||
"fileId": "f2Vo21jDdPfbsPEDATg/6w",
|
||||
"fileId": "24ho6yMw5OyKqGnnlb1lZe",
|
||||
"prefabRootNode": {
|
||||
"__id__": 1
|
||||
},
|
||||
@@ -920,7 +920,7 @@
|
||||
"propertyPath": [
|
||||
"_name"
|
||||
],
|
||||
"value": "hnode-001"
|
||||
"value": "hnode"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.TargetInfo",
|
||||
@@ -1002,7 +1002,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInstance",
|
||||
"fileId": "95i/033cdI1ar3RE/LeAFh",
|
||||
"fileId": "bf9CO3VXpEapXp0MM4jeGj",
|
||||
"prefabRootNode": {
|
||||
"__id__": 1
|
||||
},
|
||||
@@ -1032,7 +1032,7 @@
|
||||
"propertyPath": [
|
||||
"_name"
|
||||
],
|
||||
"value": "hnode-002"
|
||||
"value": "hnode"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.TargetInfo",
|
||||
@@ -1114,7 +1114,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInstance",
|
||||
"fileId": "96aOqwbNZC2554Tb3MVHAi",
|
||||
"fileId": "cbhqcW/7BHOIfs5h+tuWvE",
|
||||
"prefabRootNode": {
|
||||
"__id__": 1
|
||||
},
|
||||
@@ -1144,7 +1144,7 @@
|
||||
"propertyPath": [
|
||||
"_name"
|
||||
],
|
||||
"value": "hnode-003"
|
||||
"value": "hnode"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.TargetInfo",
|
||||
@@ -1226,7 +1226,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInstance",
|
||||
"fileId": "82lTFZokVJ1YXiKvnc7dBe",
|
||||
"fileId": "f9xptjk/hA+IJ5/9k6XxMX",
|
||||
"prefabRootNode": {
|
||||
"__id__": 1
|
||||
},
|
||||
@@ -1256,7 +1256,7 @@
|
||||
"propertyPath": [
|
||||
"_name"
|
||||
],
|
||||
"value": "hnode-004"
|
||||
"value": "hnode"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.TargetInfo",
|
||||
@@ -1338,7 +1338,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInstance",
|
||||
"fileId": "27cEJ94ONNHKT7mJlaxtlG",
|
||||
"fileId": "b9NtsF98ZKrpqsdLUl8ouf",
|
||||
"prefabRootNode": {
|
||||
"__id__": 1
|
||||
},
|
||||
@@ -1368,7 +1368,7 @@
|
||||
"propertyPath": [
|
||||
"_name"
|
||||
],
|
||||
"value": "hnode-005"
|
||||
"value": "hnode"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.TargetInfo",
|
||||
@@ -1517,7 +1517,7 @@
|
||||
"_constraint": 0,
|
||||
"_constraintNum": 2,
|
||||
"_affectedByScale": false,
|
||||
"_isAlign": false,
|
||||
"_isAlign": true,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
@@ -11461,7 +11461,10 @@
|
||||
"hero_info_node": {
|
||||
"__id__": 24
|
||||
},
|
||||
"hero_info_prefab": null,
|
||||
"hero_info_prefab": {
|
||||
"__uuid__": "46f1e2cb-6fa7-4e9e-b419-e424ba47fe68",
|
||||
"__expectedType__": "cc.Prefab"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
@@ -11833,7 +11836,7 @@
|
||||
"node": {
|
||||
"__id__": 560
|
||||
},
|
||||
"_enabled": true,
|
||||
"_enabled": false,
|
||||
"__prefab": {
|
||||
"__id__": 572
|
||||
},
|
||||
@@ -11847,10 +11850,7 @@
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "deedea09-8f2b-400f-9803-4cfd38e45d1a@58cb7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_spriteFrame": null,
|
||||
"_type": 1,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 0,
|
||||
@@ -11863,10 +11863,7 @@
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "deedea09-8f2b-400f-9803-4cfd38e45d1a",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_atlas": null,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
|
||||
@@ -113,7 +113,12 @@ export class Hero extends ecs.Entity {
|
||||
this.add(hv);
|
||||
hv.init();
|
||||
// 广播主角召唤事件,触发外部系统监听逻辑
|
||||
oops.message.dispatchEvent(GameEvent.MasterCalled,{uuid:uuid})
|
||||
oops.message.dispatchEvent(GameEvent.MasterCalled, {
|
||||
eid: this.eid,
|
||||
uuid,
|
||||
hero_lv,
|
||||
model
|
||||
})
|
||||
|
||||
// 初始化移动组件:方向、目标 X、站位基准 Y
|
||||
const move = this.get(MoveComp);
|
||||
|
||||
20
assets/script/game/map/HInfoComp.ts
Normal file
20
assets/script/game/map/HInfoComp.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { _decorator, Animation, AnimationClip, EventTouch, Label, Node, NodeEventType, Sprite, SpriteAtlas, Tween, tween, UIOpacity, Vec3, resources } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** 视图层对象 */
|
||||
@ccclass('HInfoComp')
|
||||
@ecs.register('HInfoComp', false)
|
||||
export class HInfoComp extends CCComp {
|
||||
private debugMode: boolean = true;
|
||||
/** 锁定态图标节点(显示时表示本槽位锁定) */
|
||||
|
||||
|
||||
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
|
||||
reset() {
|
||||
this.node.destroy();
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/HInfoComp.ts.meta
Normal file
9
assets/script/game/map/HInfoComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a832f87d-c91f-4b24-ad64-47eb59d658a7",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { _decorator, Label, Node, NodeEventType, Prefab, SpriteAtlas, Tween, tween, Vec3 } from "cc";
|
||||
import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, SpriteAtlas, Tween, tween, Vec3 } 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 { GameEvent } from "../common/config/GameEvent";
|
||||
@@ -7,6 +7,7 @@ import { CARD_POOL_INIT_LEVEL, CARD_POOL_MAX_LEVEL, CardConfig, CardInitCoins, C
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { CardComp } from "./CardComp";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -38,9 +39,9 @@ export class MissionCardComp extends CCComp {
|
||||
@property(Node)
|
||||
pool_lv_node:Node = null!
|
||||
@property(Node)
|
||||
hero_info_node:Node = null!
|
||||
hero_info_node:Node = null! //场上英雄信息面板所在节点
|
||||
@property(Prefab)
|
||||
hero_info_prefab:Prefab=null!
|
||||
hero_info_prefab:Prefab=null! //场上英雄信息面板Prefab
|
||||
/** 预留图集缓存(后续接入按钮/卡面图标时复用) */
|
||||
private uiconsAtlas: SpriteAtlas | null = null;
|
||||
/** 四个槽位对应的单卡控制器缓存 */
|
||||
@@ -48,6 +49,14 @@ export class MissionCardComp extends CCComp {
|
||||
/** 当前卡池等级(仅影响抽卡来源,不直接改卡槽现有内容) */
|
||||
private poolLv: number = CARD_POOL_INIT_LEVEL;
|
||||
private coin: number = CardInitCoins;
|
||||
private readonly heroInfoItemGap: number = 86;
|
||||
private heroInfoSyncTimer: number = 0;
|
||||
private heroInfoItems: Map<number, {
|
||||
node: Node,
|
||||
model: HeroAttrsComp,
|
||||
apLabel: Label | null,
|
||||
hpLabel: Label | null
|
||||
}> = new Map();
|
||||
onLoad() {
|
||||
/** 绑定事件 -> 缓存子控制器 -> 初始化UI状态 */
|
||||
this.bindEvents();
|
||||
@@ -62,6 +71,7 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
onDestroy() {
|
||||
this.unbindEvents();
|
||||
this.clearHeroInfoPanels();
|
||||
}
|
||||
init(){
|
||||
this.onMissionStart();
|
||||
@@ -71,6 +81,7 @@ export class MissionCardComp extends CCComp {
|
||||
onMissionStart() {
|
||||
this.poolLv = CARD_POOL_INIT_LEVEL;
|
||||
this.coin = CardInitCoins
|
||||
this.clearHeroInfoPanels();
|
||||
this.layoutCardSlots();
|
||||
this.clearAllCards();
|
||||
if (this.cards_up) {
|
||||
@@ -91,12 +102,18 @@ export class MissionCardComp extends CCComp {
|
||||
/** 任务结束时:清空4槽并隐藏面板 */
|
||||
onMissionEnd() {
|
||||
this.clearAllCards();
|
||||
this.clearHeroInfoPanels();
|
||||
this.node.active = false;
|
||||
}
|
||||
start() {
|
||||
|
||||
}
|
||||
|
||||
update(dt: number) {
|
||||
this.heroInfoSyncTimer += dt;
|
||||
if (this.heroInfoSyncTimer < 0.15) return;
|
||||
this.heroInfoSyncTimer = 0;
|
||||
this.refreshHeroInfoPanels();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -111,6 +128,7 @@ export class MissionCardComp extends CCComp {
|
||||
this.on(GameEvent.MissionStart, this.onMissionStart, this);
|
||||
this.on(GameEvent.MissionEnd, this.onMissionEnd, this);
|
||||
this.on(GameEvent.CoinAdd, this.onCoinAdd, this);
|
||||
oops.message.on(GameEvent.MasterCalled, this.onMasterCalled, this);
|
||||
|
||||
/** 按钮事件:抽卡与卡池升级 */
|
||||
this.cards_chou?.on(NodeEventType.TOUCH_START, this.onDrawTouchStart, this);
|
||||
@@ -129,6 +147,7 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
/** 解除按钮监听,避免节点销毁后回调泄漏 */
|
||||
private unbindEvents() {
|
||||
oops.message.off(GameEvent.MasterCalled, this.onMasterCalled, this);
|
||||
this.cards_chou?.off(NodeEventType.TOUCH_START, this.onDrawTouchStart, this);
|
||||
this.cards_chou?.off(NodeEventType.TOUCH_END, this.onDrawTouchEnd, this);
|
||||
this.cards_chou?.off(NodeEventType.TOUCH_CANCEL, this.onDrawTouchCancel, this);
|
||||
@@ -137,6 +156,14 @@ export class MissionCardComp extends CCComp {
|
||||
this.cards_up?.off(NodeEventType.TOUCH_CANCEL, this.onUpgradeTouchCancel, this);
|
||||
}
|
||||
|
||||
private onMasterCalled(event: string, args: any) {
|
||||
const payload = args ?? event;
|
||||
const eid = Number(payload?.eid ?? 0);
|
||||
const model = payload?.model as HeroAttrsComp | undefined;
|
||||
if (!eid || !model) return;
|
||||
this.ensureHeroInfoPanel(eid, model);
|
||||
}
|
||||
|
||||
private onDrawTouchStart() {
|
||||
this.playButtonPressAnim(this.cards_chou);
|
||||
}
|
||||
@@ -339,8 +366,110 @@ export class MissionCardComp extends CCComp {
|
||||
return CardsUpSet[lv] ?? 0;
|
||||
}
|
||||
|
||||
private ensureHeroInfoPanel(eid: number, model: HeroAttrsComp) {
|
||||
if (!this.hero_info_node || !this.hero_info_prefab) return;
|
||||
this.hero_info_node.active = true;
|
||||
const current = this.heroInfoItems.get(eid);
|
||||
if (current) {
|
||||
current.model = model;
|
||||
this.updateHeroInfoPanel(current);
|
||||
return;
|
||||
}
|
||||
const node = instantiate(this.hero_info_prefab);
|
||||
node.parent = this.hero_info_node;
|
||||
node.active = true;
|
||||
const item = {
|
||||
node,
|
||||
model,
|
||||
apLabel: this.resolvePanelLabel(node, [["ap", "val"]]),
|
||||
hpLabel: this.resolvePanelLabel(node, [["hp", "val"]])
|
||||
};
|
||||
this.heroInfoItems.set(eid, item);
|
||||
this.relayoutHeroInfoPanels();
|
||||
this.updateHeroInfoPanel(item);
|
||||
}
|
||||
|
||||
private refreshHeroInfoPanels() {
|
||||
const removeKeys: number[] = [];
|
||||
this.heroInfoItems.forEach((item, eid) => {
|
||||
if (!item.node || !item.node.isValid) {
|
||||
removeKeys.push(eid);
|
||||
return;
|
||||
}
|
||||
const ent = (item.model as any)?.ent;
|
||||
if (!ent) {
|
||||
if (item.node.isValid) item.node.destroy();
|
||||
removeKeys.push(eid);
|
||||
return;
|
||||
}
|
||||
this.updateHeroInfoPanel(item);
|
||||
});
|
||||
for (let i = 0; i < removeKeys.length; i++) {
|
||||
this.heroInfoItems.delete(removeKeys[i]);
|
||||
}
|
||||
if (removeKeys.length > 0) {
|
||||
this.relayoutHeroInfoPanels();
|
||||
}
|
||||
}
|
||||
|
||||
private updateHeroInfoPanel(item: {
|
||||
node: Node,
|
||||
model: HeroAttrsComp,
|
||||
apLabel: Label | null,
|
||||
hpLabel: Label | null
|
||||
}) {
|
||||
if (item.apLabel) item.apLabel.string = `${Math.max(0, Math.floor(item.model.ap ?? 0))}`;
|
||||
if (item.hpLabel) item.hpLabel.string = `${Math.max(0, Math.floor(item.model.hp_max ?? 0))}`;
|
||||
}
|
||||
|
||||
private relayoutHeroInfoPanels() {
|
||||
let index = 0;
|
||||
this.heroInfoItems.forEach(item => {
|
||||
if (!item.node || !item.node.isValid) return;
|
||||
const pos = item.node.position;
|
||||
item.node.setPosition(pos.x, -index * this.heroInfoItemGap, pos.z);
|
||||
index += 1;
|
||||
});
|
||||
}
|
||||
|
||||
private clearHeroInfoPanels() {
|
||||
this.heroInfoItems.forEach(item => {
|
||||
if (item.node && item.node.isValid) {
|
||||
item.node.destroy();
|
||||
}
|
||||
});
|
||||
this.heroInfoItems.clear();
|
||||
if (this.hero_info_node && this.hero_info_node.isValid) {
|
||||
for (let i = this.hero_info_node.children.length - 1; i >= 0; i--) {
|
||||
const child = this.hero_info_node.children[i];
|
||||
if (child && child.isValid) child.destroy();
|
||||
}
|
||||
}
|
||||
this.heroInfoSyncTimer = 0;
|
||||
}
|
||||
|
||||
private resolvePanelLabel(root: Node, paths: string[][]): Label | null {
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const node = this.findNodeByPath(root, paths[i]);
|
||||
if (!node) continue;
|
||||
const label = node.getComponent(Label) || node.getComponentInChildren(Label);
|
||||
if (label) return label;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private findNodeByPath(root: Node, path: string[]): Node | null {
|
||||
let current: Node | null = root;
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
current = current?.getChildByName(path[i]) ?? null;
|
||||
if (!current) return null;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
|
||||
reset() {
|
||||
this.clearHeroInfoPanels();
|
||||
this.resetButtonScale(this.cards_chou);
|
||||
this.resetButtonScale(this.cards_up);
|
||||
this.node.destroy();
|
||||
|
||||
Reference in New Issue
Block a user