From f678d52ffb39ede0b9d8cd79d710a195a627376f Mon Sep 17 00:00:00 2001 From: panw Date: Tue, 14 Apr 2026 09:43:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(map):=20=E5=BC=95=E5=85=A5=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E9=98=B6=E6=AE=B5=E7=8A=B6=E6=80=81=E6=9C=BA=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E6=88=98=E6=96=97=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 MissionPhase 枚举定义任务生命周期各阶段 - 实现 changePhase 方法统一管理阶段切换逻辑与事件触发 - 重构 to_fight、enterPreparePhase 等方法使用状态机驱动 - 调整战斗开始/结束逻辑以适配新的阶段状态 - 启用调试模式便于开发阶段问题排查 --- assets/script/game/map/MissionComp.ts | 153 +++++++++++++++++++++----- 1 file changed, 123 insertions(+), 30 deletions(-) diff --git a/assets/script/game/map/MissionComp.ts b/assets/script/game/map/MissionComp.ts index e7f336e6..151c7d50 100644 --- a/assets/script/game/map/MissionComp.ts +++ b/assets/script/game/map/MissionComp.ts @@ -46,6 +46,17 @@ import { Tooltip } from "../skill/Tooltip"; import { CardInitCoins } from "../common/config/CardSet"; const { ccclass, property } = _decorator; +/** 任务(关卡)生命周期阶段 */ +export enum MissionPhase { + None = 0, // 未初始化 + PrepareStart = 1, // 准备开始阶段 (2s) + Prepare = 2, // 准备阶段 (等待玩家点击开始) + PrepareEnd = 3, // 准备结束阶段 (2s) + BattleStart = 4, // 战斗开始阶段 (2s) + Battle = 5, // 战斗阶段 (刷怪、战斗中) + BattleEnd = 6, // 战斗结束阶段 (2s) + Settle = 7 // 结算阶段 +} //@todo 需要关注 当boss死亡的时候的动画播放完成后,需要触发事件,通知 MissionComp 进行奖励处理 @@ -59,7 +70,7 @@ const { ccclass, property } = _decorator; @ecs.register('MissionComp', false) export class MissionComp extends CCComp { @property({ tooltip: "是否启用调试日志" }) - private debugMode: boolean = false; + private debugMode: boolean = true; @property({ tooltip: "是否显示战斗内存观测面板" }) private showMemoryPanel: boolean = false; @@ -133,6 +144,8 @@ export class MissionComp extends CCComp { private currentWave: number = 0; /** 上一次发放金币奖励的波数(防止重复发放) */ private lastPrepareCoinWave: number = 0; + /** 当前任务阶段 */ + public currentPhase: MissionPhase = MissionPhase.None; // ======================== ECS 查询匹配器(预缓存) ======================== @@ -168,7 +181,7 @@ export class MissionComp extends CCComp { protected update(dt: number): void { if(!smc.mission.play) return if(smc.mission.pause) return - if(smc.mission.in_fight){ + if(this.currentPhase === MissionPhase.Battle){ this.syncMonsterSpawnState(dt) if(smc.mission.stop_mon_action) return smc.vmdata.mission_data.fight_time+=dt @@ -235,7 +248,7 @@ export class MissionComp extends CCComp { this.node.active=true this.data_init() oops.message.dispatchEvent(GameEvent.FightReady) - this.enterPreparePhase() + this.changePhase(MissionPhase.Prepare) let loading=this.node.parent.getChildByName("loading") loading.active=true this.scheduleOnce(()=>{ @@ -243,6 +256,89 @@ export class MissionComp extends CCComp { },0.5) } + /** + * 阶段切换核心方法(状态机) + * 处理状态流转时所需的事件触发和全局标志位修改。 + * @param targetPhase 目标阶段 + */ + private changePhase(targetPhase: MissionPhase) { + if (this.currentPhase === targetPhase) return; + const oldPhase = this.currentPhase; + this.currentPhase = targetPhase; + + // 取消状态机内部产生的定时流转任务,防止状态错乱 + this.unschedule(this.autoNextPhase); + + switch (targetPhase) { + case MissionPhase.PrepareStart: + smc.mission.in_fight = false; + smc.vmdata.mission_data.in_fight = false; + smc.mission.stop_spawn_mon = true; + if (this.start_btn && this.start_btn.isValid) this.start_btn.active = false; + this.scheduleOnce(this.autoNextPhase, 2); + break; + + case MissionPhase.Prepare: + if (this.start_btn && this.start_btn.isValid) this.start_btn.active = true; + break; + + case MissionPhase.PrepareEnd: + if (this.start_btn && this.start_btn.isValid) this.start_btn.active = false; + this.scheduleOnce(this.autoNextPhase, 2); + break; + + case MissionPhase.BattleStart: + // 触发战斗开始技能(fstart) + this.triggerHeroBattleSkills(true); + this.scheduleOnce(this.autoNextPhase, 2); + break; + + case MissionPhase.Battle: + smc.mission.stop_spawn_mon = false; + smc.mission.in_fight = true; + smc.vmdata.mission_data.in_fight = true; + oops.message.dispatchEvent(GameEvent.FightStart); + break; + + case MissionPhase.BattleEnd: + smc.mission.in_fight = false; + smc.vmdata.mission_data.in_fight = false; + smc.mission.stop_spawn_mon = true; + // 触发战斗结束技能(fend) + this.triggerHeroBattleSkills(false); + break; + + case MissionPhase.Settle: + smc.mission.in_fight = false; + smc.vmdata.mission_data.in_fight = false; + smc.mission.stop_spawn_mon = true; + if (this.start_btn && this.start_btn.isValid) this.start_btn.active = false; + break; + + case MissionPhase.None: + smc.mission.in_fight = false; + smc.vmdata.mission_data.in_fight = false; + smc.mission.stop_spawn_mon = false; + if (this.start_btn && this.start_btn.isValid) this.start_btn.active = false; + break; + } + } + + /** 自动流转到下一阶段(过渡状态结束时调用) */ + private autoNextPhase() { + switch (this.currentPhase) { + case MissionPhase.PrepareStart: + this.changePhase(MissionPhase.Prepare); + break; + case MissionPhase.PrepareEnd: + this.changePhase(MissionPhase.BattleStart); + break; + case MissionPhase.BattleStart: + this.changePhase(MissionPhase.Battle); + break; + } + } + /** * 进入战斗: * - 恢复刷怪 @@ -252,12 +348,7 @@ export class MissionComp extends CCComp { * - 触发英雄战斗开始技能 */ to_fight(){ - smc.mission.stop_spawn_mon = false; - smc.mission.in_fight=true - smc.vmdata.mission_data.in_fight = true - if (this.start_btn && this.start_btn.isValid) this.start_btn.active = false; - oops.message.dispatchEvent(GameEvent.FightStart) - this.triggerHeroBattleSkills(true); + this.changePhase(MissionPhase.PrepareEnd); } /** @@ -268,11 +359,7 @@ export class MissionComp extends CCComp { * - 触发英雄战斗结束技能 */ private enterPreparePhase() { - this.triggerHeroBattleSkills(false); - smc.mission.in_fight = false; - smc.vmdata.mission_data.in_fight = false - smc.mission.stop_spawn_mon = true; - if (this.start_btn && this.start_btn.isValid) this.start_btn.active = true; + this.changePhase(MissionPhase.PrepareStart); } /** @@ -302,7 +389,7 @@ export class MissionComp extends CCComp { private onStartFightBtnClick() { if (!smc.mission.play) return; if (smc.mission.pause) return; - if (smc.mission.in_fight) return; + if (this.currentPhase !== MissionPhase.Prepare) return; this.to_fight(); } @@ -316,24 +403,33 @@ export class MissionComp extends CCComp { * @param is_hero_dead 是否因英雄全灭触发 */ open_Victory(e:any,is_hero_dead: boolean = false){ - smc.mission.pause = true; - mLogger.log(this.debugMode, 'MissionComp', " open_Victory",is_hero_dead,this.revive_times) - oops.gui.open(UIID.Victory,{ - victory:false, - rewards:this.rewards, - game_data:this.game_data, - can_revive: is_hero_dead && this.revive_times > 0 - }) + this.changePhase(MissionPhase.BattleEnd); + + // 延迟 2s 后进入结算,让 BattleEnd 阶段的表现能够播完 + this.scheduleOnce(() => { + this.changePhase(MissionPhase.Settle); + smc.mission.pause = true; + mLogger.log(this.debugMode, 'MissionComp', " open_Victory",is_hero_dead,this.revive_times) + oops.gui.open(UIID.Victory,{ + victory:false, + rewards:this.rewards, + game_data:this.game_data, + can_revive: is_hero_dead && this.revive_times > 0 + }) + }, 2); } /** 战斗结束:延迟清理组件和对象池 */ fight_end(){ + this.changePhase(MissionPhase.BattleEnd); + this.scheduleOnce(() => { + this.changePhase(MissionPhase.Settle); smc.mission.play=false this.cleanComponents() this.clearBattlePools() - }, 0.5) + }, 2) } /** @@ -347,9 +443,7 @@ export class MissionComp extends CCComp { this.unscheduleAllCallbacks(); smc.mission.play=false smc.mission.pause = false; - smc.mission.in_fight = false; - smc.vmdata.mission_data.in_fight = false - if (this.start_btn && this.start_btn.isValid) this.start_btn.active = false; + this.changePhase(MissionPhase.None); this.cleanComponents() this.clearBattlePools() this.node.active=false @@ -372,6 +466,7 @@ export class MissionComp extends CCComp { smc.vmdata.mission_data.mon_num=0 smc.vmdata.mission_data.level = 1 smc.vmdata.mission_data.mon_max = Math.max(1, Math.floor(this.maxMonsterCount)) + this.currentPhase = MissionPhase.None; this.currentWave = 1; this.FightTime=FightSet.FiIGHT_TIME this.rewards=[] @@ -513,9 +608,7 @@ export class MissionComp extends CCComp { private handleHeroWipe(heroCount: number) { if (heroCount > 0) return; if (!smc.mission.play || smc.mission.pause) return; - if (!smc.mission.in_fight) return; - smc.mission.in_fight = false; - smc.vmdata.mission_data.in_fight = false; + if (this.currentPhase !== MissionPhase.Battle) return; this.open_Victory(null, true); }