refactor(missionCard): 重构面板滑动逻辑为底部划出模式
重构了任务卡牌面板的布局与动画系统: 1. 移除原水平轮播逻辑,改为覆盖面板从底部划入/划出的交互 2. 统一使用场景读取的真实坐标作为显示/隐藏基准位置 3. 重命名相关变量与注释,匹配新的底部划出交互逻辑 4. 调整所有面板切换按钮的注释描述,贴合新交互行为
This commit is contained in:
@@ -179,14 +179,14 @@ export class MissionCardComp extends CCComp {
|
||||
/** 是否已召唤过英雄(用于控制其他面板按钮是否可点击) */
|
||||
private hasCalledHero: boolean = false;
|
||||
|
||||
// ======================== 面板轮播状态 ========================
|
||||
// ======================== 面板划出状态 ========================
|
||||
|
||||
/** 面板滑动间距(单面板宽度,运行时由 initPanelLayout 推导) */
|
||||
private panelSlideWidth: number = 720;
|
||||
/** 当前激活的面板索引:0=英雄, 1=装备, 2=技能, 3=药品 */
|
||||
private currentPanelIndex: number = 0;
|
||||
/** 各面板基准 Y 坐标缓存 */
|
||||
private panelBaseYs: number[] = [];
|
||||
/** 面板显示位置(英雄面板基准位置,运行时从场景读取) */
|
||||
private panelShowPos: Vec3 = new Vec3();
|
||||
/** 各覆盖面板(装备/技能/药品)的隐藏 Y 坐标(屏幕外底部,从场景初始位置读取) */
|
||||
private panelHideYs: number[] = [];
|
||||
/** 是否已初始化面板布局 */
|
||||
private hasInitPanelLayout: boolean = false;
|
||||
/** 是否已缓存卡牌面板基准缩放 */
|
||||
@@ -197,10 +197,8 @@ export class MissionCardComp extends CCComp {
|
||||
private cardsShowScale: Vec3 = new Vec3(1, 1, 1);
|
||||
/** 卡牌面板收起态缩放(scale=0 隐藏) */
|
||||
private cardsHideScale: Vec3 = new Vec3(0, 0, 1);
|
||||
/** 卡牌面板显示位置 Y 坐标 */
|
||||
private readonly cardPanelShowY: number = 0;
|
||||
/** 卡牌面板隐藏位置 Y 坐标(向下 700) */
|
||||
private readonly cardPanelHideY: number = -700;
|
||||
/** 卡牌面板隐藏 Y 坐标(场景初始位置,屏幕外底部,由 initPanelLayout 缓存) */
|
||||
private cardsPanHideY: number = 0;
|
||||
/** 卡牌面板显示/隐藏动画时长 */
|
||||
private readonly cardPanelAnimDuration: number = 0.25;
|
||||
|
||||
@@ -504,7 +502,7 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 技能面板按钮回调:滑动到技能面板(index=2)。
|
||||
* 技能面板按钮回调:技能面板从底部划出(index=2)。
|
||||
*/
|
||||
private onShowSkillsClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
@@ -514,7 +512,7 @@ export class MissionCardComp extends CCComp {
|
||||
// ======================== 商店面板 ========================
|
||||
|
||||
/**
|
||||
* 商店面板按钮回调:滑动到药品商店面板(index=3)。
|
||||
* 商店面板按钮回调:药品商店面板从底部划出(index=3)。
|
||||
*/
|
||||
private onShowShopClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
@@ -854,7 +852,7 @@ export class MissionCardComp extends CCComp {
|
||||
this.playButtonResetAnim(this.cards_chou);
|
||||
}
|
||||
|
||||
// ======================== 面板轮播系统 ========================
|
||||
// ======================== 面板底部划出系统 ========================
|
||||
|
||||
/** 面板节点数组(顺序:英雄→装备→技能→药品) */
|
||||
private getPanelNodes(): Node[] {
|
||||
@@ -867,46 +865,51 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化面板轮播布局:
|
||||
* 1. 缓存各面板基准 Y 坐标。
|
||||
* 2. 推导面板滑动间距(取场景设计分辨率宽度或默认 720)。
|
||||
* 3. 将所有面板排列为水平队列,初始显示英雄面板(index=0)。
|
||||
* 初始化面板布局:
|
||||
* 1. 以英雄面板位置为基准显示位置(英雄面板固定不动)。
|
||||
* 2. 缓存各覆盖面板(装备/技能/药品)的初始位置作为隐藏位置(屏幕外底部)。
|
||||
* 3. 将覆盖面板归位到隐藏位置,初始显示英雄面板(index=0)。
|
||||
*/
|
||||
private initPanelLayout() {
|
||||
if (this.hasInitPanelLayout) return;
|
||||
const panels = this.getPanelNodes();
|
||||
this.panelBaseYs = [];
|
||||
this.panelHideYs = [];
|
||||
|
||||
// 以第一个有效面板的位置为基准,推导滑动间距
|
||||
let refX = 0;
|
||||
for (const p of panels) {
|
||||
if (p && p.isValid) {
|
||||
refX = p.getPosition().x;
|
||||
break;
|
||||
}
|
||||
const heroNode = panels[0];
|
||||
if (heroNode && heroNode.isValid) {
|
||||
heroNode.active = true;
|
||||
this.panelShowPos.set(heroNode.getPosition());
|
||||
}
|
||||
this.panelSlideWidth = Math.abs(refX) * 2 || 720;
|
||||
if (this.panelSlideWidth < 400) this.panelSlideWidth = 720;
|
||||
|
||||
for (let i = 0; i < panels.length; i++) {
|
||||
// 覆盖面板:缓存场景初始位置(屏幕外底部)作为隐藏位置,X 对齐显示位置
|
||||
for (let i = 1; i < panels.length; i++) {
|
||||
const node = panels[i];
|
||||
if (!node || !node.isValid) {
|
||||
this.panelBaseYs.push(0);
|
||||
this.panelHideYs.push(this.panelShowPos.y);
|
||||
continue;
|
||||
}
|
||||
node.active = true;
|
||||
const pos = node.getPosition();
|
||||
this.panelBaseYs.push(pos.y);
|
||||
// 初始位置:当前面板(英雄)居中,其余按序排列
|
||||
const targetX = (i - this.currentPanelIndex) * this.panelSlideWidth;
|
||||
node.setPosition(targetX, pos.y, pos.z);
|
||||
this.panelHideYs.push(pos.y);
|
||||
// index 0 为英雄面板,隐藏列表从 index 1 起
|
||||
node.setPosition(this.panelShowPos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
// 卡牌面板与其他覆盖面板同级:缓存场景初始位置(屏幕外底部)作为隐藏位置,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.hasInitPanelLayout = true;
|
||||
this.updatePanelButtonStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* 滑动到指定面板。
|
||||
* 切换到指定面板:
|
||||
* 英雄面板(index=0)固定不动;
|
||||
* 选中的覆盖面板从底部划入到显示位置,其余覆盖面板划出回底部隐藏位置。
|
||||
* @param index 0=英雄, 1=装备, 2=技能, 3=药品
|
||||
*/
|
||||
private slideToPanel(index: number) {
|
||||
@@ -916,14 +919,14 @@ export class MissionCardComp extends CCComp {
|
||||
this.currentPanelIndex = index;
|
||||
const duration = 0.25;
|
||||
|
||||
for (let i = 0; i < panels.length; i++) {
|
||||
// 覆盖面板从 index 1 开始(index 0 英雄面板固定不动)
|
||||
for (let i = 1; i < panels.length; i++) {
|
||||
const node = panels[i];
|
||||
if (!node || !node.isValid) continue;
|
||||
const targetX = (i - index) * this.panelSlideWidth;
|
||||
const baseY = this.panelBaseYs[i] ?? node.getPosition().y;
|
||||
const targetY = i === index ? this.panelShowPos.y : (this.panelHideYs[i - 1] ?? node.getPosition().y);
|
||||
Tween.stopAllByTarget(node);
|
||||
tween(node)
|
||||
.to(duration, { position: new Vec3(targetX, baseY, 0) }, { easing: 'quadOut' })
|
||||
.to(duration, { position: new Vec3(this.panelShowPos.x, targetY, 0) }, { easing: 'quadOut' })
|
||||
.start();
|
||||
}
|
||||
|
||||
@@ -960,7 +963,7 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 英雄面板按钮回调:滑动到英雄面板(index=0)。
|
||||
* 英雄面板按钮回调:收起覆盖面板,回到英雄面板(index=0)。
|
||||
*/
|
||||
private onShowHerosClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
@@ -1020,7 +1023,7 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示卡牌面板:从底部向上滑入到 Y=0
|
||||
* 显示卡牌面板:从底部向上滑入到面板显示位置(panelShowPos)
|
||||
*/
|
||||
private onShowCardsClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
@@ -1028,24 +1031,24 @@ export class MissionCardComp extends CCComp {
|
||||
this.cardPanNode.active = true;
|
||||
Tween.stopAllByTarget(this.cardPanNode);
|
||||
tween(this.cardPanNode)
|
||||
.to(this.cardPanelAnimDuration, { position: new Vec3(0, this.cardPanelShowY, 0) }, { easing: 'quadOut' })
|
||||
.to(this.cardPanelAnimDuration, { position: new Vec3(this.panelShowPos.x, this.panelShowPos.y, 0) }, { easing: 'quadOut' })
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏卡牌面板:向下滑出到 Y=-700
|
||||
* 隐藏卡牌面板:向下滑出到其隐藏位置(cardsPanHideY,屏幕外底部)
|
||||
*/
|
||||
private onCloseCardsClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
this.hideCardsPanel();
|
||||
}
|
||||
|
||||
/** 隐藏卡牌面板:向下滑出到 Y=-700(不含按钮音效,供系统逻辑复用) */
|
||||
/** 隐藏卡牌面板:向下滑出到隐藏位置(不含按钮音效,供系统逻辑复用) */
|
||||
private hideCardsPanel() {
|
||||
if (!this.cardPanNode || !this.cardPanNode.isValid) return;
|
||||
Tween.stopAllByTarget(this.cardPanNode);
|
||||
tween(this.cardPanNode)
|
||||
.to(this.cardPanelAnimDuration, { position: new Vec3(0, this.cardPanelHideY, 0) }, { easing: 'quadIn' })
|
||||
.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;
|
||||
@@ -1057,7 +1060,7 @@ export class MissionCardComp extends CCComp {
|
||||
// ======================== 装备商店面板 ========================
|
||||
|
||||
/**
|
||||
* 装备面板按钮回调:滑动到装备商店面板(index=1)。
|
||||
* 装备面板按钮回调:装备商店面板从底部划出(index=1)。
|
||||
*/
|
||||
private onShowEquipsClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
@@ -1451,8 +1454,8 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
/**
|
||||
* 进入准备阶段:
|
||||
* - 初始化面板轮播布局(如尚未初始化)。
|
||||
* - 滑动到英雄面板(index=0),作为默认展示。
|
||||
* - 初始化面板布局(如尚未初始化)。
|
||||
* - 收起覆盖面板回到英雄面板(index=0),作为默认展示。
|
||||
* - 激活 showHeros 按钮可见性。
|
||||
* - 显示卡牌面板(从底部滑入)。
|
||||
*/
|
||||
@@ -1469,10 +1472,10 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
// 刷新所有刷新按钮的费用显示(含刷新石/金币切换与置灰状态)
|
||||
this.updateDrawCostUI();
|
||||
// 显示卡牌面板(默认进入准备阶段时展开)
|
||||
// 显示卡牌面板(默认进入准备阶段时展开,位置与其他面板保持一致)
|
||||
if (this.cardPanNode && this.cardPanNode.isValid) {
|
||||
this.cardPanNode.active = true;
|
||||
this.cardPanNode.setPosition(0, this.cardPanelShowY, 0);
|
||||
this.cardPanNode.setPosition(this.panelShowPos.x, this.panelShowPos.y, 0);
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "enterPreparePhase cardPanNode", {
|
||||
active: this.cardPanNode.active,
|
||||
position: this.cardPanNode.position,
|
||||
@@ -1485,7 +1488,7 @@ export class MissionCardComp extends CCComp {
|
||||
cards_nodeChildren: this.cards_node?.children.length
|
||||
});
|
||||
}
|
||||
// 滑动到英雄面板
|
||||
// 收起覆盖面板,回到英雄面板
|
||||
this.slideToPanel(0);
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "enterPreparePhase herosPanNode", {
|
||||
@@ -1493,7 +1496,7 @@ export class MissionCardComp extends CCComp {
|
||||
herosPanNodePos: this.herosPanNode?.position,
|
||||
herosPanNodeWorldPos: this.herosPanNode?.worldPosition,
|
||||
currentPanelIndex: this.currentPanelIndex,
|
||||
panelSlideWidth: this.panelSlideWidth
|
||||
panelShowPos: this.panelShowPos
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user