From 10e2c0d817b292905a1a071160b34c50456c6c15 Mon Sep 17 00:00:00 2001 From: panFD Date: Sat, 12 Sep 2026 22:41:51 +0800 Subject: [PATCH] =?UTF-8?q?refactor(mission):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=8A=82=E9=97=B4=E8=BF=87=E5=9C=BA=E8=A1=A8=E7=8E=B0=E4=B8=8E?= =?UTF-8?q?=E5=85=B3=E5=8D=A1=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增地图滚动速度重置与背景动画加速逻辑,强化过场推进感 2. 重构节间过场流程,同步启动英雄归位与地图滚动 3. 调整关卡文本显示格式与字体大小,优化视觉体验 4. 新增全局移动锁控制英雄过场动画状态,修复动画残留问题 --- assets/resources/gui/element/mission.prefab | 9 ++--- assets/script/game/hero/MoveComp.ts | 29 ++++++++++++-- assets/script/game/map/MissionComp.ts | 39 +++++++++++-------- assets/script/game/map/MissionHomeComp.ts | 2 + .../game/map/view/map/layer/MapLayer.ts | 23 ++++++++++- 5 files changed, 74 insertions(+), 28 deletions(-) diff --git a/assets/resources/gui/element/mission.prefab b/assets/resources/gui/element/mission.prefab index 5568977e..5ac5f74c 100644 --- a/assets/resources/gui/element/mission.prefab +++ b/assets/resources/gui/element/mission.prefab @@ -5746,10 +5746,10 @@ "b": 255, "a": 255 }, - "_string": "第 {{0}}章{{0}}{{2}}关", + "_string": "第 {{0}}-{{1}}关", "_horizontalAlign": 1, "_verticalAlign": 1, - "_actualFontSize": 20, + "_actualFontSize": 26, "_fontSize": 25, "_fontFamily": "Arial", "_lineHeight": 40, @@ -5809,8 +5809,7 @@ "labelType": "cc.Label", "watchPathArr": [ "data.stage_progress.chapter ", - "data.stage_progress.stage", - "data.stage_progress.section" + "data.stage_progress.stage" ], "_id": "" }, @@ -5827,8 +5826,6 @@ "__id__": 0 }, "fileId": "4685Eq90tNG7LQanmN1qTk", - "instance": null, - "targetOverrides": null, "nestedPrefabInstanceRoots": null }, { diff --git a/assets/script/game/hero/MoveComp.ts b/assets/script/game/hero/MoveComp.ts index 9f2d7ac4..757dad05 100644 --- a/assets/script/game/hero/MoveComp.ts +++ b/assets/script/game/hero/MoveComp.ts @@ -45,6 +45,13 @@ export class MoveComp extends ecs.Comp { @ecs.register('MoveSystem') export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { + /** + * 节间过场移动动画锁(全局): + * 置位期间,存活英雄即使已归位到编队槽位也保持"move"移动动画(配合地图滚动,表现行军推进)。 + * 由 MissionComp 在节间过场开始(背景滚动启动)时置 true、过场结束(背景滚动停止)时置 false。 + */ + public static transitionMoveLock = false; + /** 渲染层级重排节流,避免每帧排序 */ private readonly renderSortInterval = 0.05; private lastRenderSortAt = 0; @@ -65,6 +72,8 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate private static readonly QUEUE_LEFT_LIMIT = FightSet.HERO_ROW_BACK_X - 120; /** 编队槽位默认间隔(像素);后排人数过多时自动压缩间距 */ private static readonly QUEUE_SPACING = 30; + /** 编队归位目标时长(秒):任意距离动态提速,保证约 1s 到位 */ + private static readonly QUEUE_ARRIVE_TIME = 1; /** 编队槽位重算节流间隔(秒):召唤/死亡后最多延迟一次重算 */ private readonly queueRebuildInterval = 0.1; private lastQueueRebuildAt = 0; @@ -181,18 +190,30 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate view.node.setPosition(targetX, view.node.position.y, 0); } view.scale = 1; - view.status_change("idle"); + // 节间过场锁定期:即使已归位也保持移动动画(配合地图滚动表现行军推进),过场结束才回待机 + if (MoveSystem.transitionMoveLock) { + view.status_change("move"); + } else { + view.status_change("idle"); + } return; } - /** 未到位:向槽位推进(与战斗推进同速),朝移动方向转身 */ + /** 未到位:向槽位推进,朝移动方向转身 */ const dir = targetX > selfX ? 1 : -1; move.direction = dir; view.scale = dir; /** 编队排队允许突破战斗左边界(LETF_END=后排锚点),扩展至 QUEUE_LEFT_LIMIT */ const queueClamp = (x: number): number => Math.max(MoveSystem.QUEUE_LEFT_LIMIT, x); - const delta = (model.speed / 3) * this.dt * dir; - const newX = queueClamp(selfX + delta); + /** 归位速度:按剩余距离动态计算,保证任意距离约 1s 到位;不低于基础移速,避免近距离僵住 */ + const dist = Math.abs(targetX - selfX); + const speed = Math.max(model.speed / 3, dist / MoveSystem.QUEUE_ARRIVE_TIME); + const delta = speed * this.dt * dir; + let newX = queueClamp(selfX + delta); + /** 防过冲:本帧会越过目标则直接贴齐 */ + if ((dir > 0 && newX > targetX) || (dir < 0 && newX < targetX)) { + newX = targetX; + } if (Math.abs(newX - selfX) >= 0.01) { view.node.setPosition(newX, view.node.position.y, 0); view.status_change("move"); diff --git a/assets/script/game/map/MissionComp.ts b/assets/script/game/map/MissionComp.ts index 04fa8646..db02787c 100644 --- a/assets/script/game/map/MissionComp.ts +++ b/assets/script/game/map/MissionComp.ts @@ -23,6 +23,7 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu import { smc } from "../common/SingletonModuleComp"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { HeroAttrsComp } from "../hero/HeroAttrsComp"; +import { MoveSystem } from "../hero/MoveComp"; import { GameEvent } from "../common/config/GameEvent"; import { SkillTriggerType } from "../common/config/heroSet"; import { HeroViewComp } from "../hero/HeroViewComp"; @@ -223,6 +224,8 @@ export class MissionComp extends CCComp { } onDestroy() { + // 解除节间过场移动动画锁,避免泄漏到下一关 + MoveSystem.transitionMoveLock = false; // 退出关卡时恢复地图背景动画 smc.map.MapView.scene.mapLayer.playAnimations() super.onDestroy(); @@ -296,12 +299,10 @@ export class MissionComp extends CCComp { // ======================== 回合倒计时 ======================== - /** 进入节间过场:恢复背景滚动 + 英雄归位(in_fight=false 触发编队排队),倒计时结束自动开下一节 */ + /** 进入节间过场:停止倒计时状态(背景滚动与英雄归位已在节间过场启动时同步触发) */ private startWaveCountdown() { this.waveCountdown = MissionComp.COUNTDOWN_TRANSITION; this.isWaveCountdown = true; - // 节间过场:恢复地图背景滚动动画,表现玩家推进 - smc.map?.MapView?.scene?.mapLayer?.playAnimations(); this.updateCountdownUI(true); } @@ -319,6 +320,8 @@ export class MissionComp extends CCComp { this.lastTimeSecond = -1; // 过场结束,停掉背景滚动(战斗中保持静止) smc.map?.MapView?.scene?.mapLayer?.stopAnimations(); + // 解除英雄移动动画锁:归位后恢复待机 + MoveSystem.transitionMoveLock = false; } // ======================== 奖励与广告 ======================== @@ -1001,8 +1004,8 @@ export class MissionComp extends CCComp { // Boss 节(末节)清场 → 本关通关,延迟弹结算(等死亡动画播完) this.scheduleSettle(false, true); } else { - // 非末节清场 → 不切暂停,延迟切 BattleEnd,由 autoNextPhase 进入节间过场自动推进下一节 - this.scheduleSectionTransition(); + // 非末节清场 → 立即启动节间过场(背景滚动 + 英雄归位同帧启动),自动连战下一节 + this.beginSectionTransition(); } return; } @@ -1065,19 +1068,21 @@ export class MissionComp extends CCComp { } /** - * 预约节间过场(非末节清场):延迟 SETTLE_DELAY 秒等死亡动画播完,再切 BattleEnd。 - * 与 scheduleSettle 的区别:不置 pause、不弹结算,由 autoNextPhase 进入 PrepareStart 过场自动连战下一节。 + * 启动节间过场(非末节清场): + * 1. 立即切出战斗(in_fight=false)→ 英雄触发编队排队、自动归位到左侧站位。 + * 2. 同时恢复地图背景滚动,表现玩家推进(背景与英雄归位同帧启动,避免一先一后)。 + * 3. 切 BattleEnd,2s 后由 autoNextPhase 进入 PrepareStart 过场倒计时,倒计时结束自动连战下一节。 + * + * Why: 不等 SETTLE_DELAY 死亡动画,让"归位 + 背景滚动"在清场瞬间就启动,过渡更连贯。 */ - private scheduleSectionTransition() { - if (this._settleScheduled) return; - this._settleScheduled = true; - this.scheduleOnce(() => { - this._settleScheduled = false; - // 延迟期间局内状态可能已变化(如英雄刚好全灭、任务结束),兜底校验 - if (!smc.mission.play || smc.mission.pause) return; - if (this.currentPhase !== MissionPhase.Battle) return; - this.changePhase(MissionPhase.BattleEnd); - }, MissionComp.SETTLE_DELAY); + private beginSectionTransition() { + smc.mission.in_fight = false; + smc.vmdata.mission_data.in_fight = false; + // 恢复地图背景滚动(与英雄归位同步启动,2 倍速表现推进) + smc.map?.MapView?.scene?.mapLayer?.playAnimations(); + // 过场期间英雄保持移动动画:即使已归位也持续行军,地图停才回待机 + MoveSystem.transitionMoveLock = true; + this.changePhase(MissionPhase.BattleEnd); } /** diff --git a/assets/script/game/map/MissionHomeComp.ts b/assets/script/game/map/MissionHomeComp.ts index 67606778..1789a03b 100644 --- a/assets/script/game/map/MissionHomeComp.ts +++ b/assets/script/game/map/MissionHomeComp.ts @@ -290,6 +290,8 @@ export class MissionHomeComp extends CCComp { home_active() { // 任务结束回到主页:确保指引动画与气泡无残留 this.stopStartGuide(); + // 大厅地图恢复正常滚动速度(清除战斗节间过场的 4x 加速残留) + smc.map?.MapView?.scene?.mapLayer?.resetSpeed(); this.uodate_data() this.hideAllPages() this.refreshRedDots() diff --git a/assets/script/game/map/view/map/layer/MapLayer.ts b/assets/script/game/map/view/map/layer/MapLayer.ts index ab06b0d2..37d481d0 100644 --- a/assets/script/game/map/view/map/layer/MapLayer.ts +++ b/assets/script/game/map/view/map/layer/MapLayer.ts @@ -86,14 +86,35 @@ export default class MapLayer extends Component { return [this.bg_anm, this.bg_anm1, this.bg_anm2, this.bg_anm3, this.bg_anm4].filter((a): a is Animation => a !== null); } + /** 背景滚动播放速度倍率(节间过场加快推进感) */ + private static readonly SCROLL_SPEED = 4; + public playAnimations(name?: string): void { - this.animations.forEach(a => name ? a.play(name) : a.play()); + this.animations.forEach(a => { + a.play(name); + // 加快背景滚动速度,强化节间"向前推进"的观感(AnimationState.speed 属性) + const clip = a.defaultClip; + if (clip) { + const state = a.getState(clip.name); + if (state) state.speed = MapLayer.SCROLL_SPEED; + } + }); } public stopAnimations(): void { this.animations.forEach(a => a.stop()); } + /** 恢复背景滚动为正常速度(1x):大厅/非过场时调用,清除过场加速残留 */ + public resetSpeed(): void { + this.animations.forEach(a => { + const clip = a.defaultClip; + if (!clip) return; + const state = a.getState(clip.name); + if (state) state.speed = 1; + }); + } + public pauseAnimations(): void { this.animations.forEach(a => a.pause()); }