feat(map): 引入任务阶段状态机重构战斗流程

- 新增 MissionPhase 枚举定义任务生命周期各阶段
- 实现 changePhase 方法统一管理阶段切换逻辑与事件触发
- 重构 to_fight、enterPreparePhase 等方法使用状态机驱动
- 调整战斗开始/结束逻辑以适配新的阶段状态
- 启用调试模式便于开发阶段问题排查
This commit is contained in:
panw
2026-04-14 09:43:48 +08:00
parent 05ce0a0b8c
commit f678d52ffb

View File

@@ -46,6 +46,17 @@ import { Tooltip } from "../skill/Tooltip";
import { CardInitCoins } from "../common/config/CardSet"; import { CardInitCoins } from "../common/config/CardSet";
const { ccclass, property } = _decorator; 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 进行奖励处理 //@todo 需要关注 当boss死亡的时候的动画播放完成后需要触发事件通知 MissionComp 进行奖励处理
@@ -59,7 +70,7 @@ const { ccclass, property } = _decorator;
@ecs.register('MissionComp', false) @ecs.register('MissionComp', false)
export class MissionComp extends CCComp { export class MissionComp extends CCComp {
@property({ tooltip: "是否启用调试日志" }) @property({ tooltip: "是否启用调试日志" })
private debugMode: boolean = false; private debugMode: boolean = true;
@property({ tooltip: "是否显示战斗内存观测面板" }) @property({ tooltip: "是否显示战斗内存观测面板" })
private showMemoryPanel: boolean = false; private showMemoryPanel: boolean = false;
@@ -133,6 +144,8 @@ export class MissionComp extends CCComp {
private currentWave: number = 0; private currentWave: number = 0;
/** 上一次发放金币奖励的波数(防止重复发放) */ /** 上一次发放金币奖励的波数(防止重复发放) */
private lastPrepareCoinWave: number = 0; private lastPrepareCoinWave: number = 0;
/** 当前任务阶段 */
public currentPhase: MissionPhase = MissionPhase.None;
// ======================== ECS 查询匹配器(预缓存) ======================== // ======================== ECS 查询匹配器(预缓存) ========================
@@ -168,7 +181,7 @@ export class MissionComp extends CCComp {
protected update(dt: number): void { protected update(dt: number): void {
if(!smc.mission.play) return if(!smc.mission.play) return
if(smc.mission.pause) return if(smc.mission.pause) return
if(smc.mission.in_fight){ if(this.currentPhase === MissionPhase.Battle){
this.syncMonsterSpawnState(dt) this.syncMonsterSpawnState(dt)
if(smc.mission.stop_mon_action) return if(smc.mission.stop_mon_action) return
smc.vmdata.mission_data.fight_time+=dt smc.vmdata.mission_data.fight_time+=dt
@@ -235,7 +248,7 @@ export class MissionComp extends CCComp {
this.node.active=true this.node.active=true
this.data_init() this.data_init()
oops.message.dispatchEvent(GameEvent.FightReady) oops.message.dispatchEvent(GameEvent.FightReady)
this.enterPreparePhase() this.changePhase(MissionPhase.Prepare)
let loading=this.node.parent.getChildByName("loading") let loading=this.node.parent.getChildByName("loading")
loading.active=true loading.active=true
this.scheduleOnce(()=>{ this.scheduleOnce(()=>{
@@ -243,6 +256,89 @@ export class MissionComp extends CCComp {
},0.5) },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(){ to_fight(){
smc.mission.stop_spawn_mon = false; this.changePhase(MissionPhase.PrepareEnd);
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);
} }
/** /**
@@ -268,11 +359,7 @@ export class MissionComp extends CCComp {
* - 触发英雄战斗结束技能 * - 触发英雄战斗结束技能
*/ */
private enterPreparePhase() { private enterPreparePhase() {
this.triggerHeroBattleSkills(false); this.changePhase(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 = true;
} }
/** /**
@@ -302,7 +389,7 @@ export class MissionComp extends CCComp {
private onStartFightBtnClick() { private onStartFightBtnClick() {
if (!smc.mission.play) return; if (!smc.mission.play) return;
if (smc.mission.pause) return; if (smc.mission.pause) return;
if (smc.mission.in_fight) return; if (this.currentPhase !== MissionPhase.Prepare) return;
this.to_fight(); this.to_fight();
} }
@@ -316,24 +403,33 @@ export class MissionComp extends CCComp {
* @param is_hero_dead 是否因英雄全灭触发 * @param is_hero_dead 是否因英雄全灭触发
*/ */
open_Victory(e:any,is_hero_dead: boolean = false){ open_Victory(e:any,is_hero_dead: boolean = false){
smc.mission.pause = true; this.changePhase(MissionPhase.BattleEnd);
mLogger.log(this.debugMode, 'MissionComp', " open_Victory",is_hero_dead,this.revive_times)
oops.gui.open(UIID.Victory,{ // 延迟 2s 后进入结算,让 BattleEnd 阶段的表现能够播完
victory:false, this.scheduleOnce(() => {
rewards:this.rewards, this.changePhase(MissionPhase.Settle);
game_data:this.game_data, smc.mission.pause = true;
can_revive: is_hero_dead && this.revive_times > 0 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(){ fight_end(){
this.changePhase(MissionPhase.BattleEnd);
this.scheduleOnce(() => { this.scheduleOnce(() => {
this.changePhase(MissionPhase.Settle);
smc.mission.play=false smc.mission.play=false
this.cleanComponents() this.cleanComponents()
this.clearBattlePools() this.clearBattlePools()
}, 0.5) }, 2)
} }
/** /**
@@ -347,9 +443,7 @@ export class MissionComp extends CCComp {
this.unscheduleAllCallbacks(); this.unscheduleAllCallbacks();
smc.mission.play=false smc.mission.play=false
smc.mission.pause = false; smc.mission.pause = false;
smc.mission.in_fight = false; this.changePhase(MissionPhase.None);
smc.vmdata.mission_data.in_fight = false
if (this.start_btn && this.start_btn.isValid) this.start_btn.active = false;
this.cleanComponents() this.cleanComponents()
this.clearBattlePools() this.clearBattlePools()
this.node.active=false this.node.active=false
@@ -372,6 +466,7 @@ export class MissionComp extends CCComp {
smc.vmdata.mission_data.mon_num=0 smc.vmdata.mission_data.mon_num=0
smc.vmdata.mission_data.level = 1 smc.vmdata.mission_data.level = 1
smc.vmdata.mission_data.mon_max = Math.max(1, Math.floor(this.maxMonsterCount)) smc.vmdata.mission_data.mon_max = Math.max(1, Math.floor(this.maxMonsterCount))
this.currentPhase = MissionPhase.None;
this.currentWave = 1; this.currentWave = 1;
this.FightTime=FightSet.FiIGHT_TIME this.FightTime=FightSet.FiIGHT_TIME
this.rewards=[] this.rewards=[]
@@ -513,9 +608,7 @@ export class MissionComp extends CCComp {
private handleHeroWipe(heroCount: number) { private handleHeroWipe(heroCount: number) {
if (heroCount > 0) return; if (heroCount > 0) return;
if (!smc.mission.play || smc.mission.pause) return; if (!smc.mission.play || smc.mission.pause) return;
if (!smc.mission.in_fight) return; if (this.currentPhase !== MissionPhase.Battle) return;
smc.mission.in_fight = false;
smc.vmdata.mission_data.in_fight = false;
this.open_Victory(null, true); this.open_Victory(null, true);
} }