新增 GameFlow 运行时状态机:PIE 与独立运行统一生命周期
Stopped→Loading→Running↔Paused 单一状态源,编辑器 Play/Stop 与 main.cpp 独立运行共用同一套加载/恢复/暂停语义
This commit is contained in:
@@ -44,6 +44,17 @@ namespace Engine {
|
||||
// 统一输入门面装配:脚本/游戏逻辑经 Input 查询键鼠,不直接
|
||||
// 依赖 Window 后端(GLFW 解耦)
|
||||
Input::Init(*m_Window);
|
||||
|
||||
// GameFlow →Stopped:脚本终局(Unity 退出 Play 语义——实例与
|
||||
// 成员状态不跨局存活,下次 Play 由对账重建)。注册在组合根最
|
||||
// 早处:先于编辑器 Init 的监听执行,EndPlay 跑在场景回滚之前,
|
||||
// OnDestroy 见到的仍是上局实体。m_Scripts 在 Run 里才创建,而
|
||||
// →Stopped 转换最早发生于 PIE Stop(届时已存在),空指针兜底
|
||||
m_GameFlow.AddListener([this](GameFlowState, GameFlowState to) {
|
||||
if (to == GameFlowState::Stopped && m_Scripts) {
|
||||
m_Scripts->EndPlay();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
@@ -132,15 +143,29 @@ namespace Engine {
|
||||
const FrameTime& frame = Time::Current();
|
||||
m_LastFrameTime = now;
|
||||
|
||||
// 输入动作层采样(单点,整帧一致):仅 Running 激活——
|
||||
// Paused/Stopped(编辑器态)动作静默清零,编辑器相机继续走
|
||||
// 原始 IsKeyDown 通道(自带 UI 捕获门控,两套互不干扰)
|
||||
Input::Poll(m_GameFlow.IsIn(GameFlowState::Running));
|
||||
|
||||
// 物理步进(OnUpdate 之前,Unity FixedUpdate→Update 语义):
|
||||
// 动态体姿态写回 Transform 并标脏,UpdateTransforms 统一重算;
|
||||
// 吃 scaled dt → timeScale=0(编辑器暂停)时 accumulator 冻结,
|
||||
// SyncScene 对账仍跑(拖静态体照常同步)
|
||||
m_Physics->Step(frame.Dt());
|
||||
// 吃 scaled dt → timeScale=0(Paused/Stopped 冻结)时
|
||||
// accumulator 冻结,SyncScene 对账仍跑(拖静态体照常同步)。
|
||||
// Loading 态整段跳过(加载中不步进)
|
||||
if (!m_GameFlow.IsIn(GameFlowState::Loading)) {
|
||||
m_Physics->Step(frame.Dt());
|
||||
|
||||
// 脚本更新(物理之后、业务 OnUpdate 之前,Unity FixedUpdate→Update
|
||||
// 语义;吃 scaled dt——timeScale=0 暂停时 dt=0 脚本自然停摆)
|
||||
m_Scripts->Update(frame.Dt());
|
||||
// 脚本只在 Running 执行(Unity Play Mode / UE PIE 语义):
|
||||
// 编辑态/暂停态 gameplay 脚本一律不跑——脚本副作用不依赖
|
||||
// dt(读输入/画调试图元/翻转动画状态),"dt=0 自然停摆"
|
||||
// 不成立,曾致编辑态按 WASD 就画箭头+翻动画状态。实例
|
||||
// 生命周期 = 一次 PIE 局:→Stopped 由 GameFlow 监听
|
||||
// EndPlay 统一销毁
|
||||
if (m_GameFlow.IsIn(GameFlowState::Running)) {
|
||||
m_Scripts->Update(frame.Dt());
|
||||
}
|
||||
}
|
||||
|
||||
OnUpdate(frame);
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// GameFlow.cpp:游戏流状态机实现。
|
||||
//
|
||||
|
||||
#include "Engine/Core/GameFlow.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Time.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
namespace {
|
||||
const char* StateName(GameFlowState s)
|
||||
{
|
||||
switch (s) {
|
||||
case GameFlowState::Stopped: return "Stopped";
|
||||
case GameFlowState::Loading: return "Loading";
|
||||
case GameFlowState::Running: return "Running";
|
||||
case GameFlowState::Paused: return "Paused";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
GameFlow::GameFlow()
|
||||
{
|
||||
// Stopped 语义即冻结:替代各处散落的初始 SetTimeScale(0)
|
||||
Time::SetTimeScale(0.0f);
|
||||
}
|
||||
|
||||
bool GameFlow::RequestStart()
|
||||
{
|
||||
if (m_State != GameFlowState::Stopped) {
|
||||
ENGINE_WARN("GameFlow: RequestStart rejected in {}",
|
||||
StateName(m_State));
|
||||
return false;
|
||||
}
|
||||
Transition(GameFlowState::Loading);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameFlow::CompleteLoading()
|
||||
{
|
||||
if (m_State != GameFlowState::Loading) {
|
||||
ENGINE_WARN("GameFlow: CompleteLoading rejected in {}",
|
||||
StateName(m_State));
|
||||
return;
|
||||
}
|
||||
Transition(GameFlowState::Running);
|
||||
}
|
||||
|
||||
bool GameFlow::RequestPause()
|
||||
{
|
||||
if (m_State != GameFlowState::Running) {
|
||||
ENGINE_WARN("GameFlow: RequestPause rejected in {}",
|
||||
StateName(m_State));
|
||||
return false;
|
||||
}
|
||||
Transition(GameFlowState::Paused);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameFlow::RequestResume()
|
||||
{
|
||||
if (m_State != GameFlowState::Paused) {
|
||||
ENGINE_WARN("GameFlow: RequestResume rejected in {}",
|
||||
StateName(m_State));
|
||||
return false;
|
||||
}
|
||||
Transition(GameFlowState::Running);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameFlow::RequestStop()
|
||||
{
|
||||
if (m_State == GameFlowState::Stopped) {
|
||||
ENGINE_WARN("GameFlow: RequestStop rejected in Stopped");
|
||||
return false;
|
||||
}
|
||||
Transition(GameFlowState::Stopped);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameFlow::Transition(GameFlowState to)
|
||||
{
|
||||
const GameFlowState from = m_State;
|
||||
m_State = to;
|
||||
|
||||
// timeScale 单一权威:Running/Loading 解冻,Paused/Stopped 冻结
|
||||
Time::SetTimeScale(
|
||||
to == GameFlowState::Running || to == GameFlowState::Loading
|
||||
? 1.0f : 0.0f);
|
||||
|
||||
ENGINE_INFO("GameFlow: {} -> {}", StateName(from), StateName(to));
|
||||
for (const Listener& l : m_Listeners) l(from, to);
|
||||
}
|
||||
|
||||
} // namespace Engine
|
||||
Reference in New Issue
Block a user