refactor(MissionCardComp): 重构面板显示逻辑,改用缩放控制显隐
1. 重构英雄卡牌等覆盖面板的显示逻辑,从位置滑动改为缩放动画 2. 移除默认激活面板的代码,改为按需显示 3. 优化英雄槽位的显示逻辑,默认隐藏空槽位 4. 调整UI预制体的布局参数与文本内容
This commit is contained in:
@@ -183,10 +183,12 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
/** 当前激活的面板索引:0=英雄, 1=装备, 2=技能, 3=药品 */
|
||||
private currentPanelIndex: number = 0;
|
||||
/** 面板显示位置(英雄面板基准位置,运行时从场景读取) */
|
||||
private panelShowPos: Vec3 = new Vec3();
|
||||
/** 各覆盖面板(装备/技能/药品)的隐藏 Y 坐标(屏幕外底部,从场景初始位置读取) */
|
||||
private panelHideYs: number[] = [];
|
||||
/** 覆盖面板(卡牌/装备/技能/药品)显示位置:面板铺满屏幕,显示时固定位于 (0,0) */
|
||||
private readonly panelShowPos: Vec3 = new Vec3(0, 0, 0);
|
||||
/** 覆盖面板显示态缩放 (1,1) */
|
||||
private readonly panelShowScale: Vec3 = new Vec3(1, 1, 1);
|
||||
/** 覆盖面板隐藏态缩放 (0,0) */
|
||||
private readonly panelHideScale: Vec3 = new Vec3(0, 0, 1);
|
||||
/** 是否已初始化面板布局 */
|
||||
private hasInitPanelLayout: boolean = false;
|
||||
/** 是否已缓存卡牌面板基准缩放 */
|
||||
@@ -197,9 +199,7 @@ export class MissionCardComp extends CCComp {
|
||||
private cardsShowScale: Vec3 = new Vec3(1, 1, 1);
|
||||
/** 卡牌面板收起态缩放(scale=0 隐藏) */
|
||||
private cardsHideScale: Vec3 = new Vec3(0, 0, 1);
|
||||
/** 卡牌面板隐藏 Y 坐标(场景初始位置,屏幕外底部,由 initPanelLayout 缓存) */
|
||||
private cardsPanHideY: number = 0;
|
||||
/** 卡牌面板显示/隐藏动画时长 */
|
||||
/** 覆盖面板显示/隐藏动画时长 */
|
||||
private readonly cardPanelAnimDuration: number = 0.25;
|
||||
|
||||
// ======================== 生命周期 ========================
|
||||
@@ -215,6 +215,7 @@ export class MissionCardComp extends CCComp {
|
||||
onLoad() {
|
||||
this.bindEvents();
|
||||
this.cacheCardComps();
|
||||
this.hideAllHeroBoxSlots();
|
||||
this.initCardsPanelPos();
|
||||
this.initPanelLayout();
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "onLoad init", {
|
||||
@@ -557,11 +558,6 @@ export class MissionCardComp extends CCComp {
|
||||
// 三等分水平布局
|
||||
this.layoutSlotsHorizontal(this.itemCardComps);
|
||||
|
||||
// 默认显示商店面板
|
||||
if (this.shopPanNode) {
|
||||
this.shopPanNode.active = true;
|
||||
}
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "populate shop items", {
|
||||
count: items.length,
|
||||
slotCount: this.itemCardComps.length
|
||||
@@ -800,7 +796,7 @@ export class MissionCardComp extends CCComp {
|
||||
if (heroIdx >= 0) {
|
||||
const cards = this.buildDrawCards();
|
||||
this.dispatchCardsToSlots(cards);
|
||||
// 召唤成功后收起英雄卡池面板
|
||||
// 抽 1 张后关闭英雄卡池面板
|
||||
this.hideCardsPanel();
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "refresh hero pool after buy", {
|
||||
triggerSlot: heroIdx
|
||||
@@ -811,6 +807,8 @@ export class MissionCardComp extends CCComp {
|
||||
const equipIdx = this.equipCardComps.findIndex(c => c === source);
|
||||
if (equipIdx >= 0) {
|
||||
this.populateEquipments();
|
||||
// 抽 1 张后关闭装备面板
|
||||
this.hideOverlayPanel(1);
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "refresh equip pool after buy", {
|
||||
triggerSlot: equipIdx
|
||||
});
|
||||
@@ -820,6 +818,8 @@ export class MissionCardComp extends CCComp {
|
||||
const skillIdx = this.skillCardComps.findIndex(c => c === source);
|
||||
if (skillIdx >= 0) {
|
||||
this.populateSkillCards();
|
||||
// 抽 1 张后关闭技能面板
|
||||
this.hideOverlayPanel(2);
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "refresh skill pool after buy", {
|
||||
triggerSlot: skillIdx
|
||||
});
|
||||
@@ -829,6 +829,8 @@ export class MissionCardComp extends CCComp {
|
||||
const itemIdx = this.itemCardComps.findIndex(c => c === source);
|
||||
if (itemIdx >= 0) {
|
||||
this.populateShopItems();
|
||||
// 抽 1 张后关闭药品面板
|
||||
this.hideOverlayPanel(3);
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "refresh item pool after buy", {
|
||||
triggerSlot: itemIdx
|
||||
});
|
||||
@@ -866,50 +868,75 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
/**
|
||||
* 初始化面板布局:
|
||||
* 1. 以英雄面板位置为基准显示位置(英雄面板固定不动)。
|
||||
* 2. 缓存各覆盖面板(装备/技能/药品)的初始位置作为隐藏位置(屏幕外底部)。
|
||||
* 3. 将覆盖面板归位到隐藏位置,初始显示英雄面板(index=0)。
|
||||
* 1. 英雄面板(index=0)为常驻基准面板,始终显示。
|
||||
* 2. 覆盖面板(卡牌/装备/技能/药品)铺满屏幕、位置固定 (0,0),
|
||||
* 通过缩放 0↔1 控制显示/隐藏,初始缩放置 0 并停用。
|
||||
*/
|
||||
private initPanelLayout() {
|
||||
if (this.hasInitPanelLayout) return;
|
||||
const panels = this.getPanelNodes();
|
||||
this.panelHideYs = [];
|
||||
|
||||
const heroNode = panels[0];
|
||||
if (heroNode && heroNode.isValid) {
|
||||
heroNode.active = true;
|
||||
this.panelShowPos.set(heroNode.getPosition());
|
||||
}
|
||||
|
||||
// 覆盖面板:缓存场景初始位置(屏幕外底部)作为隐藏位置,X 对齐显示位置
|
||||
// 覆盖面板从 index 1 开始:位置由代码接管,禁用 Widget 避免其在 lateUpdate 对齐覆盖 position
|
||||
for (let i = 1; i < panels.length; i++) {
|
||||
const node = panels[i];
|
||||
if (!node || !node.isValid) {
|
||||
this.panelHideYs.push(this.panelShowPos.y);
|
||||
continue;
|
||||
}
|
||||
node.active = true;
|
||||
const pos = node.getPosition();
|
||||
this.panelHideYs.push(pos.y);
|
||||
// index 0 为英雄面板,隐藏列表从 index 1 起
|
||||
node.setPosition(this.panelShowPos.x, pos.y, pos.z);
|
||||
this.initOverlayPanel(panels[i]);
|
||||
}
|
||||
|
||||
// 卡牌面板与其他覆盖面板同级:缓存场景初始位置(屏幕外底部)作为隐藏位置,X 对齐显示位置
|
||||
if (this.cardPanNode && this.cardPanNode.isValid) {
|
||||
const pos = this.cardPanNode.getPosition();
|
||||
this.cardsPanHideY = pos.y;
|
||||
this.cardPanNode.setPosition(this.panelShowPos.x, pos.y, pos.z);
|
||||
}
|
||||
// 卡牌面板与其他覆盖面板同级,同上处理
|
||||
this.initOverlayPanel(this.cardPanNode);
|
||||
|
||||
this.hasInitPanelLayout = true;
|
||||
this.updatePanelButtonStates();
|
||||
}
|
||||
|
||||
/** 单个覆盖面板初始化:禁用 Widget、定位到显示位置、缩放置 0 并隐藏 */
|
||||
private initOverlayPanel(node: Node | null) {
|
||||
if (!node || !node.isValid) return;
|
||||
const widget = node.getComponent(Widget);
|
||||
if (widget) widget.enabled = false;
|
||||
node.setPosition(this.panelShowPos);
|
||||
node.setScale(this.panelHideScale);
|
||||
node.active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示覆盖面板:缩放从 0 动画放大到 1。
|
||||
* @param node 覆盖面板节点(卡牌/装备/技能/药品)
|
||||
*/
|
||||
private showOverlayPanel(node: Node | null) {
|
||||
if (!node || !node.isValid) return;
|
||||
node.setPosition(this.panelShowPos);
|
||||
node.setScale(this.panelHideScale);
|
||||
node.active = true;
|
||||
Tween.stopAllByTarget(node);
|
||||
tween(node)
|
||||
.to(this.cardPanelAnimDuration, { scale: this.panelShowScale }, { easing: 'quadOut' })
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏覆盖面板:缩放从当前值动画缩小到 0,结束后停用节点。
|
||||
* @param node 覆盖面板节点(卡牌/装备/技能/药品)
|
||||
*/
|
||||
private hideOverlayPanelNode(node: Node | null) {
|
||||
if (!node || !node.isValid) return;
|
||||
Tween.stopAllByTarget(node);
|
||||
tween(node)
|
||||
.to(this.cardPanelAnimDuration, { scale: this.panelHideScale }, { easing: 'quadIn' })
|
||||
.call(() => {
|
||||
if (node.isValid) node.active = false;
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换到指定面板:
|
||||
* 英雄面板(index=0)固定不动;
|
||||
* 选中的覆盖面板从底部划入到显示位置,其余覆盖面板划出回底部隐藏位置。
|
||||
* 英雄面板(index=0)为常驻基准面板;
|
||||
* 选中的覆盖面板缩放放大到 1 显示,其余覆盖面板缩放缩小到 0 隐藏。
|
||||
* @param index 0=英雄, 1=装备, 2=技能, 3=药品
|
||||
*/
|
||||
private slideToPanel(index: number) {
|
||||
@@ -917,17 +944,16 @@ export class MissionCardComp extends CCComp {
|
||||
if (index < 0 || index >= panels.length) return;
|
||||
|
||||
this.currentPanelIndex = index;
|
||||
const duration = 0.25;
|
||||
|
||||
// 覆盖面板从 index 1 开始(index 0 英雄面板固定不动)
|
||||
// 覆盖面板从 index 1 开始(index 0 英雄面板常驻不动)
|
||||
for (let i = 1; i < panels.length; i++) {
|
||||
const node = panels[i];
|
||||
if (!node || !node.isValid) continue;
|
||||
const targetY = i === index ? this.panelShowPos.y : (this.panelHideYs[i - 1] ?? node.getPosition().y);
|
||||
Tween.stopAllByTarget(node);
|
||||
tween(node)
|
||||
.to(duration, { position: new Vec3(this.panelShowPos.x, targetY, 0) }, { easing: 'quadOut' })
|
||||
.start();
|
||||
if (i === index) {
|
||||
this.showOverlayPanel(node);
|
||||
} else {
|
||||
this.hideOverlayPanelNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
this.updatePanelButtonStates();
|
||||
@@ -975,8 +1001,8 @@ export class MissionCardComp extends CCComp {
|
||||
/**
|
||||
* 同步英雄信息盒槽位与场上英雄:
|
||||
* 1. 刷新 herosBoxNode 下 HeroBoxComp 槽位缓存(编辑器已预放 3 个)。
|
||||
* 2. 查询当前存活英雄列表,按序绑定到槽位。
|
||||
* 3. 多余槽位显示空英雄信息。
|
||||
* 2. 查询当前存活英雄列表,按序绑定到槽位(bindHero 内部激活节点)。
|
||||
* 3. 多余槽位清空数据并隐藏(默认全部隐藏,召唤一个激活一个)。
|
||||
*
|
||||
* Why: 英雄召唤 / 死亡 / 卖出 / 升级后调用,保证面板与场上一一对应。
|
||||
*/
|
||||
@@ -1003,11 +1029,14 @@ export class MissionCardComp extends CCComp {
|
||||
if (actor) {
|
||||
comp.bindHero(actor.eid, actor.model);
|
||||
} else {
|
||||
// 空槽位默认隐藏:召唤一个英雄才激活一个槽位
|
||||
comp.applyEmpty();
|
||||
comp.node.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.resizeListBox(this.herosBoxNode, Math.max(actors.length, 1));
|
||||
// 列表高度只按可见槽位(存活英雄数)计算,空槽位已隐藏不再占位
|
||||
this.resizeListBox(this.herosBoxNode, actors.length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1022,39 +1051,51 @@ export class MissionCardComp extends CCComp {
|
||||
.filter((comp): comp is HeroBoxComp => !!comp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示卡牌面板:从底部向上滑入到面板显示位置(panelShowPos)
|
||||
*/
|
||||
private onShowCardsClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
if (!this.cardPanNode || !this.cardPanNode.isValid) return;
|
||||
this.cardPanNode.active = true;
|
||||
Tween.stopAllByTarget(this.cardPanNode);
|
||||
tween(this.cardPanNode)
|
||||
.to(this.cardPanelAnimDuration, { position: new Vec3(this.panelShowPos.x, this.panelShowPos.y, 0) }, { easing: 'quadOut' })
|
||||
.start();
|
||||
/** 英雄信息盒槽位默认全部隐藏,召唤一个英雄再激活一个 */
|
||||
private hideAllHeroBoxSlots() {
|
||||
this.ensureHeroBoxSlots();
|
||||
for (const comp of this.heroBoxComps) {
|
||||
if (comp && comp.node && comp.node.isValid) {
|
||||
comp.node.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏卡牌面板:向下滑出到其隐藏位置(cardsPanHideY,屏幕外底部)
|
||||
* 显示卡牌面板:缩放从 0 动画放大到 1
|
||||
*/
|
||||
private onShowCardsClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
this.showOverlayPanel(this.cardPanNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏卡牌面板:缩放动画缩小到 0
|
||||
*/
|
||||
private onCloseCardsClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
this.hideCardsPanel();
|
||||
}
|
||||
|
||||
/** 隐藏卡牌面板:向下滑出到隐藏位置(不含按钮音效,供系统逻辑复用) */
|
||||
/** 隐藏卡牌面板:缩放缩小到 0(不含按钮音效,供系统逻辑复用) */
|
||||
private hideCardsPanel() {
|
||||
if (!this.cardPanNode || !this.cardPanNode.isValid) return;
|
||||
Tween.stopAllByTarget(this.cardPanNode);
|
||||
tween(this.cardPanNode)
|
||||
.to(this.cardPanelAnimDuration, { position: new Vec3(this.panelShowPos.x, this.cardsPanHideY, 0) }, { easing: 'quadIn' })
|
||||
.call(() => {
|
||||
if (this.cardPanNode && this.cardPanNode.isValid) {
|
||||
this.cardPanNode.active = false;
|
||||
}
|
||||
})
|
||||
.start();
|
||||
this.hideOverlayPanelNode(this.cardPanNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏指定覆盖面板(缩放缩小到 0 并停用):
|
||||
* 用于抽 1 张卡成功后自动收起对应卡池面板,露出下层英雄面板。
|
||||
* @param index 面板索引:1=装备, 2=技能, 3=药品(0 为英雄基准面板,不可隐藏)
|
||||
*/
|
||||
private hideOverlayPanel(index: number) {
|
||||
const panels = this.getPanelNodes();
|
||||
if (index < 1 || index >= panels.length) return;
|
||||
const node = panels[index];
|
||||
if (!node || !node.isValid) return;
|
||||
this.hideOverlayPanelNode(node);
|
||||
// 面板收起后 tab 高亮回归英雄面板
|
||||
this.currentPanelIndex = 0;
|
||||
this.updatePanelButtonStates();
|
||||
}
|
||||
|
||||
// ======================== 装备商店面板 ========================
|
||||
@@ -1118,11 +1159,6 @@ export class MissionCardComp extends CCComp {
|
||||
// 三等分水平布局
|
||||
this.layoutSlotsHorizontal(this.equipCardComps);
|
||||
|
||||
// 默认显示装备面板
|
||||
if (this.equipsPanNode) {
|
||||
this.equipsPanNode.active = true;
|
||||
}
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "populate equipments", {
|
||||
count: equips.length,
|
||||
slotCount: this.equipCardComps.length
|
||||
@@ -1196,11 +1232,6 @@ export class MissionCardComp extends CCComp {
|
||||
// 三等分水平布局
|
||||
this.layoutSlotsHorizontal(this.skillCardComps);
|
||||
|
||||
// 默认显示技能面板
|
||||
if (this.skillPanNode) {
|
||||
this.skillPanNode.active = true;
|
||||
}
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "populate skill cards", {
|
||||
count: skills.length,
|
||||
slotCount: this.skillCardComps.length
|
||||
@@ -1472,10 +1503,9 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
// 刷新所有刷新按钮的费用显示(含刷新石/金币切换与置灰状态)
|
||||
this.updateDrawCostUI();
|
||||
// 显示卡牌面板(默认进入准备阶段时展开,位置与其他面板保持一致)
|
||||
// 显示卡牌面板(默认进入准备阶段时展开,缩放放大到 1)
|
||||
if (this.cardPanNode && this.cardPanNode.isValid) {
|
||||
this.cardPanNode.active = true;
|
||||
this.cardPanNode.setPosition(this.panelShowPos.x, this.panelShowPos.y, 0);
|
||||
this.showOverlayPanel(this.cardPanNode);
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "enterPreparePhase cardPanNode", {
|
||||
active: this.cardPanNode.active,
|
||||
position: this.cardPanNode.position,
|
||||
|
||||
Reference in New Issue
Block a user