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

View File

@@ -150,6 +150,8 @@ export class MissionCardComp extends CCComp {
model: HeroAttrsComp,
comp: HInfoComp
}> = new Map();
/** 缓存预先放置的 6 个 HInfoComp */
private cachedHInfoComps: Map<number, HInfoComp> = new Map();
// ======================== 生命周期 ========================
/**
@@ -161,6 +163,7 @@ export class MissionCardComp extends CCComp {
* 5. 触发首次任务开始流程。
*/
onLoad() {
this.cacheHInfoComps();
this.bindEvents();
this.cacheCardComps();
this.layoutCardSlots();
@@ -171,6 +174,19 @@ export class MissionCardComp extends CCComp {
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() {
@@ -880,48 +896,44 @@ export class MissionCardComp extends CCComp {
}
private ensureHeroInfoPanel(eid: number, model: HeroAttrsComp) {
if (!this.hero_info_node || !this.hero_info_prefab) {
mLogger.error(this.debugMode, "MissionCardComp", "ensureHeroInfoPanel: missing hero_info_node or hero_info_prefab");
if (!this.hero_info_node) {
mLogger.error(this.debugMode, "MissionCardComp", "ensureHeroInfoPanel: missing hero_info_node");
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);
if (current) {
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);
return;
}
mLogger.log(this.debugMode, "MissionCardComp", "ensureHeroInfoPanel: creating new panel for eid", eid);
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;
}
comp.node.active = true;
const item = {
node,
node: comp.node,
model,
comp
};
comp.bindData(eid, model);
comp.setBattlePhase(this.isBattlePhase);
this.heroInfoItems.set(eid, item);
this.relayoutHeroInfoPanels();
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() {
@@ -932,7 +944,7 @@ export class MissionCardComp extends CCComp {
return;
}
if (!item.comp.isModelAlive()) {
if (item.node.isValid) item.node.destroy();
if (item.node.isValid) item.node.active = false;
removeKeys.push(eid);
return;
}
@@ -941,7 +953,6 @@ export class MissionCardComp extends CCComp {
for (let i = 0; i < removeKeys.length; i++) {
this.heroInfoItems.delete(removeKeys[i]);
}
this.relayoutHeroInfoPanels();
this.updateHeroNumUI(false, false);
}
@@ -954,54 +965,24 @@ export class MissionCardComp extends CCComp {
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() {
if (this.heroInfoItems) {
this.heroInfoItems.forEach(item => {
if (item && 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();
}
if (this.cachedHInfoComps) {
this.cachedHInfoComps.forEach(comp => {
if (comp && comp.node && comp.node.isValid) {
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.syncMissionHeroData(0);
this.updateHeroNumUI(false, false);