diff --git a/assets/script/game/map/MissionCardComp.ts b/assets/script/game/map/MissionCardComp.ts index 79147556..ae58d589 100644 --- a/assets/script/game/map/MissionCardComp.ts +++ b/assets/script/game/map/MissionCardComp.ts @@ -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 }); }