fix(map, mission): 修复槽位布局与重置位置异常问题

1. 为英雄组件clearBySystem增加位置重置判断,避免未初始化时将槽位重置到原点
2. 重构任务面板槽位布局逻辑,统一使用三等分水平布局
3. 新增布局辅助方法,处理预制体Widget组件冲突问题
4. 禁用多个冗余的任务预制体节点,清理无效引用
This commit is contained in:
panFD
2026-07-26 20:08:47 +08:00
parent 1593c7ffbf
commit c7afe5d73f
3 changed files with 84 additions and 10 deletions

View File

@@ -6029,7 +6029,7 @@
"node": {
"__id__": 348
},
"_enabled": true,
"_enabled": false,
"__prefab": {
"__id__": 354
},
@@ -7599,7 +7599,7 @@
"node": {
"__id__": 456
},
"_enabled": true,
"_enabled": false,
"__prefab": {
"__id__": 462
},
@@ -8571,7 +8571,7 @@
"node": {
"__id__": 522
},
"_enabled": true,
"_enabled": false,
"__prefab": {
"__id__": 528
},
@@ -9562,7 +9562,7 @@
"node": {
"__id__": 590
},
"_enabled": true,
"_enabled": false,
"__prefab": {
"__id__": 596
},
@@ -14349,7 +14349,7 @@
"__id__": 530
},
"equip_refresh": {
"__id__": 530
"__id__": 464
},
"shop_refresh": {
"__id__": 598

View File

@@ -578,11 +578,16 @@ export class CHeroComp extends CCComp {
/**
* 系统清槽:任务开始/结束时强制重置。
* 清空数据与 UI 并隐藏节点。
*
* 仅在 setSlotPosition 初始化过静止位置后才恢复位置,
* 否则 restPosition 为默认 (0,0,0),会把所有槽位重置到中心。
*/
clearBySystem() {
this.cardData = null;
this.isPurchased = false;
this.node.setPosition(this.restPosition);
if (this.hasFixedBasePosition) {
this.node.setPosition(this.restPosition);
}
this.applyEmptyUI();
this.node.active = false;
}

View File

@@ -35,7 +35,7 @@
* - smc.vmdata.mission_data —— 局内数据coin / hero_num / hero_max_num
*/
import { mLogger } from "../common/Logger";
import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, Tween, tween, Vec3, UITransform } from "cc";
import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, Tween, tween, Vec3, UITransform, Widget } 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";
@@ -155,10 +155,13 @@ export class MissionCardComp extends CCComp {
/** 英雄数量显示节点(含 icon + num 子节点) */
@property(Node)
hero_num_node: Node = null!
/**技能刷新按钮节点 */
/** 技能刷新按钮节点 */
@property(Node)
skill_refresh: Node = null!
/** 槽位水平坐标(屏幕 720 三等分,卡片 230列宽 240 */
private readonly slotPosX: number[] = [-240, 0, 240];
/** 装备刷新按钮节点 */
@property(Node)
equip_refresh: Node = null!;
@@ -221,7 +224,6 @@ export class MissionCardComp extends CCComp {
onLoad() {
this.bindEvents();
this.cacheCardComps();
this.layoutCardSlots();
this.initCardsPanelPos();
this.initPanelLayout();
mLogger.log(this.debugMode, "MissionCardComp", "onLoad init", {
@@ -542,6 +544,9 @@ export class MissionCardComp extends CCComp {
// 根据物品数量设置列表面板高度
this.resizeListBox(this.shopBoxNode, targetCount);
// 三等分水平布局
this.layoutSlotsHorizontal(this.itemCardComps);
// 默认显示商店面板
if (this.shopPanNode) {
this.shopPanNode.active = true;
@@ -556,6 +561,7 @@ export class MissionCardComp extends CCComp {
/**
* 确保药品槽位数量足够。
* 优先复用现有槽位,不足时从 itemPrefab 实例化补充。
* 实例化后立即按 slotPosX 定位,避免堆叠在 (0,0)。
*/
private ensureItemSlots(targetCount: number) {
if (!this.shopBoxNode || !this.itemPrefab) return;
@@ -570,6 +576,7 @@ export class MissionCardComp extends CCComp {
const comp = node.getComponent(ItemListComp) || node.addComponent(ItemListComp);
this.itemCardComps.push(comp);
}
this.layoutSlotsHorizontal(this.itemCardComps);
}
/** 清空商品列表(复用槽位,仅清空数据) */
@@ -1015,6 +1022,9 @@ export class MissionCardComp extends CCComp {
// 根据物品数量设置列表面板高度
this.resizeListBox(this.equipsBoxNode, targetCount);
// 三等分水平布局
this.layoutSlotsHorizontal(this.equipCardComps);
// 默认显示装备面板
if (this.equipsPanNode) {
this.equipsPanNode.active = true;
@@ -1029,6 +1039,7 @@ export class MissionCardComp extends CCComp {
/**
* 确保装备槽位数量足够。
* 优先复用现有槽位,不足时从 equipPrefab 实例化补充。
* 实例化后立即按 slotPosX 定位,避免堆叠在 (0,0)。
*/
private ensureEquipSlots(targetCount: number) {
if (!this.equipsBoxNode || !this.equipPrefab) return;
@@ -1043,6 +1054,7 @@ export class MissionCardComp extends CCComp {
const comp = node.getComponent(EquipListComp) || node.addComponent(EquipListComp);
this.equipCardComps.push(comp);
}
this.layoutSlotsHorizontal(this.equipCardComps);
}
/** 清空装备列表(复用槽位,仅清空数据) */
@@ -1088,6 +1100,9 @@ export class MissionCardComp extends CCComp {
// 根据技能数量设置列表面板高度
this.resizeListBox(this.skillBoxNode, targetCount);
// 三等分水平布局
this.layoutSlotsHorizontal(this.skillCardComps);
// 默认显示技能面板
if (this.skillPanNode) {
this.skillPanNode.active = true;
@@ -1102,6 +1117,7 @@ export class MissionCardComp extends CCComp {
/**
* 确保技能槽位数量足够。
* 优先复用现有槽位,不足时从 sCardPrefab 实例化补充。
* 实例化后立即按 slotPosX 定位,避免堆叠在 (0,0)。
*/
private ensureSkillSlots(targetCount: number) {
if (!this.skillBoxNode || !this.sCardPrefab) return;
@@ -1112,10 +1128,15 @@ export class MissionCardComp extends CCComp {
// 补充实例化不足的槽位
while (this.skillCardComps.length < targetCount) {
const node = instantiate(this.sCardPrefab);
// 先禁用根节点 Widget 再挂到父节点:
// 否则 addChild 激活当帧 lateUpdate 时 Widget 会对齐覆盖 position
const widget = node.getComponent(Widget);
if (widget) widget.enabled = false;
this.skillBoxNode.addChild(node);
const comp = node.getComponent(SCardComp) || node.addComponent(SCardComp);
this.skillCardComps.push(comp);
}
this.layoutSlotsHorizontal(this.skillCardComps);
}
/** 清空技能卡槽(复用槽位,仅清空数据) */
@@ -1232,6 +1253,10 @@ export class MissionCardComp extends CCComp {
if (this.cardPrefab && this.cards_node && this.cards_node.isValid) {
for (let i = 0; i < 3; i++) {
const node = instantiate(this.cardPrefab);
// 先禁用根节点 Widget 再挂到父节点:
// 否则 addChild 激活当帧 lateUpdate 时 Widget 会对齐覆盖 position
const widget = node.getComponent(Widget);
if (widget) widget.enabled = false;
this.cards_node.addChild(node);
const comp = node.getComponent(CHeroComp) || node.addComponent(CHeroComp);
this.cardComps.push(comp);
@@ -1246,6 +1271,8 @@ export class MissionCardComp extends CCComp {
cardPrefabValid: this.cardPrefab.isValid
});
}
// 实例化后立即按 slotPosX 定位,避免堆叠在 (0,0)
this.layoutCardSlots();
} else {
mLogger.log(this.debugMode, "MissionCardComp", "cacheCardComps failed", {
cardPrefabValid: this.cardPrefab?.isValid,
@@ -1503,7 +1530,49 @@ export class MissionCardComp extends CCComp {
}
private layoutCardSlots() {
// 布局由 cHero.prefab 根节点 Widget 约束控制,代码不再覆盖位置
// 走 CHeroComp.setSlotPosition同步更新其 restPosition
// 避免 clearBySystem 恢复位置时与布局结果不一致
for (let i = 0; i < this.cardComps.length && i < this.slotPosX.length; i++) {
const comp = this.cardComps[i];
if (comp && comp.node && comp.node.isValid) {
const widget = comp.node.getComponent(Widget);
if (widget) widget.enabled = false;
comp.setSlotPosition(this.slotPosX[i]);
}
}
}
/**
* 三等分水平布局:
* 按固定坐标数组 slotPosX 排列槽位,超出数量的槽位不处理。
*
* Why: 各卡池根节点的 Layout 组件已移除,槽位水平位置改由代码统一控制。
* 部分预制体根节点残留 Widget 组件(对齐父节点中心),
* 启用时会在 lateUpdate 覆盖代码设置的 position需先禁用。
*
* @param comps 槽位组件数组CHeroComp / EquipListComp / SCardComp / ItemListComp
*/
private layoutSlotsHorizontal(comps: Array<CHeroComp | EquipListComp | SCardComp | ItemListComp>) {
for (let i = 0; i < comps.length && i < this.slotPosX.length; i++) {
const node = comps[i]?.node;
if (!node || !node.isValid) continue;
// 禁用根节点 Widget防止其在对齐刷新时覆盖 position
const widget = node.getComponent(Widget);
if (widget) widget.enabled = false;
const pos = node.getPosition();
node.setPosition(this.slotPosX[i], pos.y, pos.z);
}
}
/**
* 延迟一帧执行布局:
* 首次实例化的嵌套预制体在首帧会先恢复到原始位置,
* 等下一帧预制体初始化完成后再定位,避免首帧堆叠在 (0,0)。
*/
private layoutSlotsHorizontalDeferred(comps: Array<CHeroComp | EquipListComp | SCardComp | ItemListComp>) {
this.scheduleOnce(() => {
this.layoutSlotsHorizontal(comps);
}, 0);
}
private playButtonPressAnim(node: Node | null) {