refactor(map): 优化英雄信息面板的缓存与复用逻辑

1. 新增缓存预先放置的HInfoComp组件,避免运行时实例化预制体
2. 移除动态创建面板逻辑,改为复用预先摆放的节点
3. 简化ensureHeroInfoPanel逻辑,通过node_index直接获取目标组件
4. 销毁时改为隐藏缓存节点而非直接销毁,保留复用基础
5. 移除冗余的relayoutHeroInfoPanels方法和相关逻辑
This commit is contained in:
panw
2026-05-13 17:13:41 +08:00
parent b578e17186
commit 626d27e676
2 changed files with 1803 additions and 1796 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -150,6 +150,8 @@ export class MissionCardComp extends CCComp {
model: HeroAttrsComp, model: HeroAttrsComp,
comp: HInfoComp comp: HInfoComp
}> = new Map(); }> = new Map();
/** 缓存预先放置的 6 个 HInfoComp */
private cachedHInfoComps: Map<number, HInfoComp> = new Map();
// ======================== 生命周期 ======================== // ======================== 生命周期 ========================
/** /**
@@ -161,6 +163,7 @@ export class MissionCardComp extends CCComp {
* 5. 触发首次任务开始流程。 * 5. 触发首次任务开始流程。
*/ */
onLoad() { onLoad() {
this.cacheHInfoComps();
this.bindEvents(); this.bindEvents();
this.cacheCardComps(); this.cacheCardComps();
this.layoutCardSlots(); this.layoutCardSlots();
@@ -171,6 +174,19 @@ export class MissionCardComp extends CCComp {
poolLv: this.poolLv poolLv: this.poolLv
}); });
} }
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() {
@@ -880,48 +896,44 @@ export class MissionCardComp extends CCComp {
} }
private ensureHeroInfoPanel(eid: number, model: HeroAttrsComp) { private ensureHeroInfoPanel(eid: number, model: HeroAttrsComp) {
if (!this.hero_info_node || !this.hero_info_prefab) { if (!this.hero_info_node) {
mLogger.error(this.debugMode, "MissionCardComp", "ensureHeroInfoPanel: missing hero_info_node or hero_info_prefab"); mLogger.error(this.debugMode, "MissionCardComp", "ensureHeroInfoPanel: missing hero_info_node");
return; return;
} }
this.hero_info_node.active = true;
// 计算目标 node_index: lane_index=0 对应 1排(1-3), lane_index=1 对应 2排(4-6)
// lane=0 上路, lane=1 中路, lane=2 下路
const expectedNodeIndex = model.lane_index * 3 + model.lane + 1;
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); const current = this.heroInfoItems.get(eid);
if (current) { if (current) {
current.model = model; current.model = model;
current.comp.bindData(eid, model); current.comp = comp;
current.node = comp.node;
comp.node.active = true;
comp.bindData(eid, model);
this.updateHeroInfoPanel(current); this.updateHeroInfoPanel(current);
return; return;
} }
mLogger.log(this.debugMode, "MissionCardComp", "ensureHeroInfoPanel: creating new panel for eid", eid); comp.node.active = true;
const node = instantiate(this.hero_info_prefab);
node.parent = this.hero_info_node;
node.active = true;
// 尝试两种方式获取组件,并输出日志
let comp = node.getComponent(HInfoComp) as any;
if (!comp) {
comp = node.getComponent("HInfoComp") as any;
}
if (!comp) {
mLogger.error(this.debugMode, "MissionCardComp", "ensureHeroInfoPanel: Failed to get HInfoComp from prefab!");
node.destroy();
return;
}
const item = { const item = {
node, node: comp.node,
model, model,
comp comp
}; };
comp.bindData(eid, model); comp.bindData(eid, model);
comp.setBattlePhase(this.isBattlePhase); comp.setBattlePhase(this.isBattlePhase);
this.heroInfoItems.set(eid, item); this.heroInfoItems.set(eid, item);
this.relayoutHeroInfoPanels();
this.updateHeroInfoPanel(item); this.updateHeroInfoPanel(item);
mLogger.log(this.debugMode, "MissionCardComp", `ensureHeroInfoPanel: new panel created for eid ${eid}, final position:`, item.node.position); mLogger.log(this.debugMode, "MissionCardComp", `ensureHeroInfoPanel: updated panel for eid ${eid} at node_index ${expectedNodeIndex}`);
} }
private refreshHeroInfoPanels() { private refreshHeroInfoPanels() {
@@ -932,7 +944,7 @@ export class MissionCardComp extends CCComp {
return; return;
} }
if (!item.comp.isModelAlive()) { if (!item.comp.isModelAlive()) {
if (item.node.isValid) item.node.destroy(); if (item.node.isValid) item.node.active = false;
removeKeys.push(eid); removeKeys.push(eid);
return; return;
} }
@@ -941,7 +953,6 @@ export class MissionCardComp extends CCComp {
for (let i = 0; i < removeKeys.length; i++) { for (let i = 0; i < removeKeys.length; i++) {
this.heroInfoItems.delete(removeKeys[i]); this.heroInfoItems.delete(removeKeys[i]);
} }
this.relayoutHeroInfoPanels();
this.updateHeroNumUI(false, false); this.updateHeroNumUI(false, false);
} }
@@ -954,54 +965,24 @@ export class MissionCardComp extends CCComp {
item.comp.setBattlePhase(this.isBattlePhase); item.comp.setBattlePhase(this.isBattlePhase);
} }
private relayoutHeroInfoPanels() {
const sortedItems = [...this.heroInfoItems.values()].sort((a, b) => {
const aEnt = (a.model as any)?.ent as ecs.Entity | undefined;
const bEnt = (b.model as any)?.ent as ecs.Entity | undefined;
const aView = aEnt?.get(HeroViewComp);
const bView = bEnt?.get(HeroViewComp);
const aMove = aEnt?.get(MoveComp);
const bMove = bEnt?.get(MoveComp);
// 排序逻辑反转:适应 cc.Layout 的节点渲染顺序(先渲染/index小的在左边
// 1. x 坐标越小越靠后排index 应该越小
const aFrontScore = aView?.node?.position?.x ?? -999999;
const bFrontScore = bView?.node?.position?.x ?? -999999;
if (aFrontScore !== bFrontScore) return aFrontScore - bFrontScore;
const aSpawnOrder = aMove?.spawnOrder ?? 0;
const bSpawnOrder = bMove?.spawnOrder ?? 0;
if (aSpawnOrder !== bSpawnOrder) return aSpawnOrder - bSpawnOrder;
const aEid = aEnt?.eid ?? 0;
const bEid = bEnt?.eid ?? 0;
return aEid - bEid;
});
for (let index = 0; index < sortedItems.length; index++) {
const item = sortedItems[index];
if (!item.node || !item.node.isValid) continue;
// 既然使用了 cc.Layout 进行自动排版,我们只需设置渲染顺序
// Layout 会自动根据 siblingIndex 对所有子节点重新排位
item.node.setSiblingIndex(index);
}
}
private clearHeroInfoPanels() { private clearHeroInfoPanels() {
if (this.heroInfoItems) { if (this.heroInfoItems) {
this.heroInfoItems.forEach(item => {
if (item && item.node && item.node.isValid) {
item.node.destroy();
}
});
this.heroInfoItems.clear(); this.heroInfoItems.clear();
} }
if (this.hero_info_node && this.hero_info_node.isValid) { if (this.cachedHInfoComps) {
for (let i = this.hero_info_node.children.length - 1; i >= 0; i--) { this.cachedHInfoComps.forEach(comp => {
const child = this.hero_info_node.children[i]; if (comp && comp.node && comp.node.isValid) {
if (child && child.isValid) child.destroy(); comp.node.active = false;
} }
});
} }
// 不再销毁子节点,因为它们是预先放置的
// 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.heroInfoSyncTimer = 0;
this.syncMissionHeroData(0); this.syncMissionHeroData(0);
this.updateHeroNumUI(false, false); this.updateHeroNumUI(false, false);