feat: add runtime play tick and play-mode scene editing semantics

This commit is contained in:
2026-04-02 19:37:35 +08:00
parent e30f5d5ffa
commit fb15d60be9
28 changed files with 2016 additions and 45 deletions

View File

@@ -377,9 +377,11 @@ add_library(XCEngine STATIC
# Scene
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/Scene.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneRuntime.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/RuntimeLoop.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneManager.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/Scene.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneRuntime.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/RuntimeLoop.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneManager.cpp
# Platform

View File

@@ -0,0 +1,45 @@
#pragma once
#include <XCEngine/Scene/SceneRuntime.h>
#include <cstdint>
namespace XCEngine {
namespace Components {
class RuntimeLoop {
public:
struct Settings {
float fixedDeltaTime = 1.0f / 50.0f;
float maxFrameDeltaTime = 0.1f;
uint32_t maxFixedStepsPerFrame = 4;
};
explicit RuntimeLoop(Settings settings = {});
void SetSettings(const Settings& settings);
const Settings& GetSettings() const { return m_settings; }
void Start(Scene* scene);
void Stop();
void Tick(float deltaTime);
void Pause();
void Resume();
void StepFrame();
bool IsRunning() const { return m_sceneRuntime.IsRunning(); }
bool IsPaused() const { return m_paused; }
Scene* GetScene() const { return m_sceneRuntime.GetScene(); }
float GetFixedAccumulator() const { return m_fixedAccumulator; }
private:
SceneRuntime m_sceneRuntime;
Settings m_settings = {};
float m_fixedAccumulator = 0.0f;
bool m_paused = false;
bool m_stepRequested = false;
};
} // namespace Components
} // namespace XCEngine

View File

@@ -0,0 +1,100 @@
#include "Scene/RuntimeLoop.h"
#include <algorithm>
namespace XCEngine {
namespace Components {
namespace {
RuntimeLoop::Settings SanitizeSettings(RuntimeLoop::Settings settings) {
if (settings.fixedDeltaTime <= 0.0f) {
settings.fixedDeltaTime = 1.0f / 50.0f;
}
if (settings.maxFrameDeltaTime < 0.0f) {
settings.maxFrameDeltaTime = 0.0f;
}
if (settings.maxFixedStepsPerFrame == 0) {
settings.maxFixedStepsPerFrame = 1;
}
return settings;
}
} // namespace
RuntimeLoop::RuntimeLoop(Settings settings)
: m_settings(SanitizeSettings(settings)) {
}
void RuntimeLoop::SetSettings(const Settings& settings) {
m_settings = SanitizeSettings(settings);
}
void RuntimeLoop::Start(Scene* scene) {
m_fixedAccumulator = 0.0f;
m_paused = false;
m_stepRequested = false;
m_sceneRuntime.Start(scene);
}
void RuntimeLoop::Stop() {
m_sceneRuntime.Stop();
m_fixedAccumulator = 0.0f;
m_paused = false;
m_stepRequested = false;
}
void RuntimeLoop::Tick(float deltaTime) {
if (!IsRunning()) {
return;
}
if (m_paused && !m_stepRequested) {
return;
}
const float clampedDeltaTime = std::clamp(deltaTime, 0.0f, m_settings.maxFrameDeltaTime);
m_fixedAccumulator += clampedDeltaTime;
uint32_t fixedStepsExecuted = 0;
while (m_fixedAccumulator >= m_settings.fixedDeltaTime &&
fixedStepsExecuted < m_settings.maxFixedStepsPerFrame) {
m_sceneRuntime.FixedUpdate(m_settings.fixedDeltaTime);
m_fixedAccumulator -= m_settings.fixedDeltaTime;
++fixedStepsExecuted;
}
m_sceneRuntime.Update(clampedDeltaTime);
m_sceneRuntime.LateUpdate(clampedDeltaTime);
m_stepRequested = false;
}
void RuntimeLoop::Pause() {
if (!IsRunning()) {
return;
}
m_paused = true;
m_stepRequested = false;
}
void RuntimeLoop::Resume() {
if (!IsRunning()) {
return;
}
m_paused = false;
m_stepRequested = false;
}
void RuntimeLoop::StepFrame() {
if (!IsRunning()) {
return;
}
m_paused = true;
m_stepRequested = true;
}
} // namespace Components
} // namespace XCEngine