refactor(map/hero): 重构英雄位置管理逻辑,移除lane相关字段

重构了英雄分路排位的旧实现,改用硬编码的点位数组管理英雄站位,移除了HeroAttrsComp中的lane和lane_index字段,简化了英雄位置分配、UI面板绑定的逻辑,提升代码可维护性。
This commit is contained in:
walkpan
2026-05-13 23:48:58 +08:00
parent e3a9d447ba
commit 3f47df2682
5 changed files with 105 additions and 265 deletions

View File

@@ -141,15 +141,6 @@ export class MissionCardComp extends CCComp {
private cardsHideScale: Vec3 = new Vec3(0, 0, 1);
/** 卡牌原始定位点 */
private cardsPos = [-260,-75,108,260]
/**
* 英雄信息面板映射EID → { node, model, comp }
* 用于追踪每个出战英雄的面板实例和数据引用
*/
private heroInfoItems: Map<number, {
node: Node,
model: HeroAttrsComp,
comp: HInfoComp
}> = new Map();
/** 缓存预先放置的 6 个 HInfoComp */
private cachedHInfoComps: Map<number, HInfoComp> = new Map();
// ======================== 生命周期 ========================
@@ -226,11 +217,6 @@ export class MissionCardComp extends CCComp {
missionData.hero_extend_max_num = FightSet.HERO_MAX_NUM + 1;
}
// 确保 Map 被正确初始化
if (!this.heroInfoItems) {
this.heroInfoItems = new Map();
}
// 确保卡牌组件列表已被正确缓存
if (!this.cardComps || this.cardComps.length === 0) {
this.cacheCardComps();
@@ -276,7 +262,13 @@ export class MissionCardComp extends CCComp {
this.heroInfoSyncTimer += dt;
if (this.heroInfoSyncTimer < 0.15) return;
this.heroInfoSyncTimer = 0;
this.refreshHeroInfoPanels();
// 遍历所有预设的 HInfoComp让其根据 node_index 自己刷新
this.cachedHInfoComps.forEach(comp => {
if (comp && comp.isValid) {
comp.refreshByNodeIndex();
}
});
}
@@ -422,14 +414,12 @@ export class MissionCardComp extends CCComp {
if (!eid || !model) return;
const before = this.getAliveHeroCount();
this.ensureHeroInfoPanel(eid, model);
const after = this.getAliveHeroCount();
this.updateHeroNumUI(true, after > before);
}
/** 英雄死亡事件回调:刷新面板列表并更新英雄数量 UI */
private onHeroDead() {
this.refreshHeroInfoPanels();
this.updateHeroNumUI(true, false);
}
@@ -663,9 +653,9 @@ export class MissionCardComp extends CCComp {
Tween.stopAllByTarget(this.cards_node);
this.cards_node.setScale(this.cardsShowScale);
this.heroInfoItems.forEach(item => {
if (item.comp && item.comp.isValid) {
item.comp.setBattlePhase(false);
this.cachedHInfoComps.forEach(comp => {
if (comp && comp.isValid) {
comp.setBattlePhase(false);
}
});
}
@@ -683,9 +673,9 @@ export class MissionCardComp extends CCComp {
})
.start();
this.heroInfoItems.forEach(item => {
if (item.comp && item.comp.isValid) {
item.comp.setBattlePhase(true);
this.cachedHInfoComps.forEach(comp => {
if (comp && comp.isValid) {
comp.setBattlePhase(true);
}
});
}
@@ -895,132 +885,7 @@ export class MissionCardComp extends CCComp {
return Math.floor(cost);
}
private ensureHeroInfoPanel(eid: number, model: HeroAttrsComp) {
if (!this.hero_info_node) {
mLogger.error(this.debugMode, "MissionCardComp", "ensureHeroInfoPanel: missing hero_info_node");
return;
}
// MoveComp.ts 里的 assignment:
// lanePriority = [1, 0, 2]; // slotIndex 0->中路(1), 1->上路(0), 2->下路(2)
// laneIdx = lanePriority[slotIndex % 3]; // priority: 1(中), 0(上), 2(下)
// model.lane = laneIdx;
//
// 所以当:
// model.lane = 0 (上路), model.lane_index = 0 -> 对应 node_index = 1
// model.lane = 1 (中路), model.lane_index = 0 -> 对应 node_index = 2
// model.lane = 2 (下路), model.lane_index = 0 -> 对应 node_index = 3
const expectedNodeIndex = model.lane_index * 3 + model.lane + 1;
mLogger.log(this.debugMode, "MissionCardComp", `ensureHeroInfoPanel calculation: lane=${model.lane}, lane_index=${model.lane_index} -> expectedNodeIndex=${expectedNodeIndex}`);
const comp = this.cachedHInfoComps.get(expectedNodeIndex);
if (!comp) {
mLogger.error(this.debugMode, "MissionCardComp", `ensureHeroInfoPanel: missing pre-placed HInfoComp for index ${expectedNodeIndex}`);
return;
}
const current = this.heroInfoItems.get(eid);
if (current) {
current.model = model;
current.comp = comp;
current.node = comp.node;
comp.node.active = true;
comp.bindData(eid, model);
this.updateHeroInfoPanel(current);
return;
}
comp.node.active = true;
const item = {
node: comp.node,
model,
comp
};
comp.bindData(eid, model);
comp.setBattlePhase(this.isBattlePhase);
this.heroInfoItems.set(eid, item);
this.updateHeroInfoPanel(item);
mLogger.log(this.debugMode, "MissionCardComp", `ensureHeroInfoPanel: updated panel for eid ${eid} at node_index ${expectedNodeIndex}`);
}
private refreshHeroInfoPanels() {
const removeKeys: number[] = [];
// 1. 先将已死亡的英雄移除,释放占用的节点
this.heroInfoItems.forEach((item, eid) => {
if (!item.node || !item.node.isValid) {
removeKeys.push(eid);
return;
}
// 使用 model.is_dead 增加判断条件,更加准确
if (!item.comp.isModelAlive() || item.model.is_dead) {
if (item.node.isValid) item.node.active = false;
removeKeys.push(eid);
return;
}
});
for (let i = 0; i < removeKeys.length; i++) {
this.heroInfoItems.delete(removeKeys[i]);
}
// 2. 然后再处理所有存活英雄的位置转移和信息刷新
// 如果有多个英雄在同一帧发生位置变动,我们需要统一处理
const needTransfer: Array<{eid: number, expectedNodeIndex: number}> = [];
this.heroInfoItems.forEach((item, eid) => {
// 检查英雄是否改变了位置 (lane 或 lane_index 发生了变化)
const expectedNodeIndex = item.model.lane_index * 3 + item.model.lane + 1;
if (item.comp.node_index !== expectedNodeIndex) {
// 如果位置变了,需要转移到新的节点上
const newComp = this.cachedHInfoComps.get(expectedNodeIndex);
if (newComp) {
needTransfer.push({eid, expectedNodeIndex});
// 将原来的节点释放,以供其他可能换到这个位置的英雄使用
item.node.active = false;
}
} else {
this.updateHeroInfoPanel(item);
}
});
// 执行位置转移
for (const transfer of needTransfer) {
const item = this.heroInfoItems.get(transfer.eid);
if (!item) continue;
const newComp = this.cachedHInfoComps.get(transfer.expectedNodeIndex);
if (newComp) {
// 转移到新节点
item.comp = newComp;
item.node = newComp.node;
item.node.active = true;
item.comp.bindData(transfer.eid, item.model);
item.comp.setBattlePhase(this.isBattlePhase);
this.updateHeroInfoPanel(item);
}
}
this.updateHeroNumUI(false, false);
}
private updateHeroInfoPanel(item: {
node: Node,
model: HeroAttrsComp,
comp: HInfoComp
}) {
item.comp.refresh();
item.comp.setBattlePhase(this.isBattlePhase);
}
private clearHeroInfoPanels() {
if (this.heroInfoItems) {
this.heroInfoItems.clear();
}
if (this.cachedHInfoComps) {
this.cachedHInfoComps.forEach(comp => {
if (comp && comp.node && comp.node.isValid) {
@@ -1028,13 +893,6 @@ export class MissionCardComp extends CCComp {
}
});
}
// 不再销毁子节点,因为它们是预先放置的
// 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;
this.syncMissionHeroData(0);
this.updateHeroNumUI(false, false);
@@ -1066,11 +924,11 @@ export class MissionCardComp extends CCComp {
private getAliveHeroCount(): number {
let count = 0;
this.heroInfoItems.forEach(item => {
if (!item?.node || !item.node.isValid) return;
if (!item.comp?.isModelAlive()) return;
if (item.model?.is_dead) return;
count += 1;
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
const model = entity.get(HeroAttrsComp);
if (model && model.fac === FacSet.HERO && !model.is_dead) {
count++;
}
});
return count;
}
@@ -1218,7 +1076,6 @@ export class MissionCardComp extends CCComp {
// this.resetButtonScale(this.cards_up);
// 关键:在 reset/销毁 时将 Map 置空,彻底切断引用
this.heroInfoItems = null as any;
this.cardComps = [] as any;
if (this.node && this.node.isValid) {