refactor(mission): 优化节间过场表现与关卡流程
1. 新增地图滚动速度重置与背景动画加速逻辑,强化过场推进感 2. 重构节间过场流程,同步启动英雄归位与地图滚动 3. 调整关卡文本显示格式与字体大小,优化视觉体验 4. 新增全局移动锁控制英雄过场动画状态,修复动画残留问题
This commit is contained in:
@@ -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
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user