feat: add pauzing of the running game
This commit is contained in:
@@ -55,7 +55,9 @@ public:
|
|||||||
virtual void onPlayModeChanged(bool) {}
|
virtual void onPlayModeChanged(bool) {}
|
||||||
|
|
||||||
[[nodiscard]] bool isPlaying() const { return playing; }
|
[[nodiscard]] bool isPlaying() const { return playing; }
|
||||||
|
[[nodiscard]] bool isPaused() const { return paused; }
|
||||||
void requestPlayModeToggle() { playModeToggleRequested = true; }
|
void requestPlayModeToggle() { playModeToggleRequested = true; }
|
||||||
|
void setPaused(bool pause);
|
||||||
|
|
||||||
virtual void customWindowsMenu() {}
|
virtual void customWindowsMenu() {}
|
||||||
|
|
||||||
@@ -75,7 +77,6 @@ protected:
|
|||||||
bool cleanedUp{false};
|
bool cleanedUp{false};
|
||||||
bool cleaningUp{false};
|
bool cleaningUp{false};
|
||||||
bool customInitStarted{false};
|
bool customInitStarted{false};
|
||||||
bool gamePaused{false};
|
|
||||||
|
|
||||||
bool frameLimit{false};
|
bool frameLimit{false};
|
||||||
float frameTime{0.f};
|
float frameTime{0.f};
|
||||||
@@ -110,6 +111,8 @@ private:
|
|||||||
void exitPlayMode();
|
void exitPlayMode();
|
||||||
|
|
||||||
bool playing{true};
|
bool playing{true};
|
||||||
|
bool paused{false};
|
||||||
|
bool mouseCapturedBeforePause{false};
|
||||||
bool playModeToggleRequested{false};
|
bool playModeToggleRequested{false};
|
||||||
std::optional<nlohmann::json> editSnapshot;
|
std::optional<nlohmann::json> editSnapshot;
|
||||||
std::weak_ptr<Scene> editScene;
|
std::weak_ptr<Scene> editScene;
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ public:
|
|||||||
|
|
||||||
void SetPlaying(bool playing) { m_playing = playing; }
|
void SetPlaying(bool playing) { m_playing = playing; }
|
||||||
[[nodiscard]] bool IsPlaying() const { return m_playing; }
|
[[nodiscard]] bool IsPlaying() const { return m_playing; }
|
||||||
|
void SetPaused(bool paused) { m_paused = paused; }
|
||||||
|
[[nodiscard]] bool IsPaused() const { return m_paused; }
|
||||||
|
|
||||||
void Update(float dt);
|
void Update(float dt);
|
||||||
void FixedUpdate(float dt);
|
void FixedUpdate(float dt);
|
||||||
@@ -55,7 +57,11 @@ private:
|
|||||||
|
|
||||||
std::vector<std::shared_ptr<Scene>> m_activeScenes;
|
std::vector<std::shared_ptr<Scene>> m_activeScenes;
|
||||||
std::vector<std::shared_ptr<Scene>> m_scenes;
|
std::vector<std::shared_ptr<Scene>> m_scenes;
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsTicking() const { return m_playing && !m_paused; }
|
||||||
|
|
||||||
bool m_playing{true};
|
bool m_playing{true};
|
||||||
|
bool m_paused{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //SCENEMANAGER_H
|
#endif //SCENEMANAGER_H
|
||||||
|
|||||||
+45
-3
@@ -188,6 +188,10 @@ if (!isRunning) break;
|
|||||||
{
|
{
|
||||||
playModeToggleRequested = true;
|
playModeToggleRequested = true;
|
||||||
}
|
}
|
||||||
|
if (playing && InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_F7))
|
||||||
|
{
|
||||||
|
setPaused(!paused);
|
||||||
|
}
|
||||||
|
|
||||||
// Consume SDL events before updating the base camera so held and
|
// Consume SDL events before updating the base camera so held and
|
||||||
// newly pressed inputs are applied in the same frame.
|
// newly pressed inputs are applied in the same frame.
|
||||||
@@ -431,21 +435,57 @@ void App::drawWindowsMenu()
|
|||||||
|
|
||||||
void App::drawPlayControls()
|
void App::drawPlayControls()
|
||||||
{
|
{
|
||||||
const char* state = playing ? "PLAYING" : "EDIT";
|
const char* state = !playing ? "EDIT" : paused ? "PAUSED" : "PLAYING";
|
||||||
const char* action = playing ? "Stop (F5)" : "Play (F5)";
|
const char* action = playing ? "Stop (F5)" : "Play (F5)";
|
||||||
|
const char* pauseAction = paused ? "Resume (F7)" : "Pause (F7)";
|
||||||
const ImGuiStyle& style = ImGui::GetStyle();
|
const ImGuiStyle& style = ImGui::GetStyle();
|
||||||
const float width = ImGui::CalcTextSize(state).x + ImGui::CalcTextSize(action).x +
|
float width = ImGui::CalcTextSize(state).x + ImGui::CalcTextSize(action).x +
|
||||||
style.ItemSpacing.x * 2.0f + style.FramePadding.x * 2.0f;
|
style.ItemSpacing.x * 2.0f + style.FramePadding.x * 2.0f;
|
||||||
|
if (playing)
|
||||||
|
{
|
||||||
|
width += ImGui::CalcTextSize(pauseAction).x + style.ItemSpacing.x + style.FramePadding.x * 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// Centered like Godot's run buttons, unless the menus already reach past the middle
|
// Centered like Godot's run buttons, unless the menus already reach past the middle
|
||||||
ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), (ImGui::GetWindowWidth() - width) * 0.5f));
|
ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), (ImGui::GetWindowWidth() - width) * 0.5f));
|
||||||
ImGui::TextColored(playing ? ImVec4{0.4f, 0.9f, 0.4f, 1.0f} : ImVec4{0.7f, 0.7f, 0.7f, 1.0f}, "%s", state);
|
const ImVec4 stateColor = !playing ? ImVec4{0.7f, 0.7f, 0.7f, 1.0f}
|
||||||
|
: paused ? ImVec4{0.95f, 0.75f, 0.3f, 1.0f}
|
||||||
|
: ImVec4{0.4f, 0.9f, 0.4f, 1.0f};
|
||||||
|
ImGui::TextColored(stateColor, "%s", state);
|
||||||
|
if (playing && ImGui::MenuItem(pauseAction))
|
||||||
|
{
|
||||||
|
setPaused(!paused);
|
||||||
|
}
|
||||||
if (ImGui::MenuItem(action))
|
if (ImGui::MenuItem(action))
|
||||||
{
|
{
|
||||||
playModeToggleRequested = true;
|
playModeToggleRequested = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::setPaused(bool pause)
|
||||||
|
{
|
||||||
|
if (!playing || pause == paused)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paused = pause;
|
||||||
|
SceneManager::GetInstance().SetPaused(paused);
|
||||||
|
|
||||||
|
// Hand the mouse to the editor while paused, give it back to the game on resume
|
||||||
|
auto& input = InputManager::GetInstance();
|
||||||
|
if (paused)
|
||||||
|
{
|
||||||
|
mouseCapturedBeforePause = input.IsMouseCaptured();
|
||||||
|
input.SetMouseCaptured(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
input.SetMouseCaptured(mouseCapturedBeforePause);
|
||||||
|
}
|
||||||
|
spdlog::info(paused ? "Paused play mode" : "Resumed play mode");
|
||||||
|
}
|
||||||
|
|
||||||
void App::applyPlayModeToggle()
|
void App::applyPlayModeToggle()
|
||||||
{
|
{
|
||||||
if (playing)
|
if (playing)
|
||||||
@@ -500,7 +540,9 @@ void App::exitPlayMode()
|
|||||||
{
|
{
|
||||||
auto& sceneManager = SceneManager::GetInstance();
|
auto& sceneManager = SceneManager::GetInstance();
|
||||||
playing = false;
|
playing = false;
|
||||||
|
paused = false;
|
||||||
sceneManager.SetPlaying(false);
|
sceneManager.SetPlaying(false);
|
||||||
|
sceneManager.SetPaused(false);
|
||||||
InputManager::GetInstance().SetMouseCaptured(false);
|
InputManager::GetInstance().SetMouseCaptured(false);
|
||||||
|
|
||||||
const std::shared_ptr<Scene> scene = editScene.lock();
|
const std::shared_ptr<Scene> scene = editScene.lock();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Scene& SceneManager::GetCurrentScene() const {
|
|||||||
|
|
||||||
void SceneManager::Update(float dt) {
|
void SceneManager::Update(float dt) {
|
||||||
for (const auto& scene : m_activeScenes) {
|
for (const auto& scene : m_activeScenes) {
|
||||||
if (m_playing) {
|
if (IsTicking()) {
|
||||||
scene->Update(dt);
|
scene->Update(dt);
|
||||||
} else {
|
} else {
|
||||||
scene->CommitPendingAdditions();
|
scene->CommitPendingAdditions();
|
||||||
@@ -25,7 +25,7 @@ void SceneManager::Update(float dt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::FixedUpdate(float dt) {
|
void SceneManager::FixedUpdate(float dt) {
|
||||||
if (!m_playing) {
|
if (!IsTicking()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const auto& scene : m_activeScenes) {
|
for (const auto& scene : m_activeScenes) {
|
||||||
@@ -34,7 +34,7 @@ void SceneManager::FixedUpdate(float dt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::LateUpdate(float dt) {
|
void SceneManager::LateUpdate(float dt) {
|
||||||
if (!m_playing) {
|
if (!IsTicking()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const auto& scene : m_activeScenes) {
|
for (const auto& scene : m_activeScenes) {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ void LightKeeper::updateViewMode()
|
|||||||
auto& input = InputManager::GetInstance();
|
auto& input = InputManager::GetInstance();
|
||||||
FirstPersonController* player = mainScene.playerController;
|
FirstPersonController* player = mainScene.playerController;
|
||||||
|
|
||||||
if (isPlaying() && player != nullptr && input.WasKeyPressed(SDL_SCANCODE_F6))
|
if (isPlaying() && !isPaused() && player != nullptr && input.WasKeyPressed(SDL_SCANCODE_F6))
|
||||||
{
|
{
|
||||||
player->SetPossessed(!player->IsPossessed());
|
player->SetPossessed(!player->IsPossessed());
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ void LightKeeper::updateViewMode()
|
|||||||
|
|
||||||
bool LightKeeper::isPlayerViewActive() const
|
bool LightKeeper::isPlayerViewActive() const
|
||||||
{
|
{
|
||||||
return isPlaying() && mainScene.playerController != nullptr && mainScene.playerController->IsPossessed();
|
return isPlaying() && !isPaused() && mainScene.playerController != nullptr && mainScene.playerController->IsPossessed();
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraComponent* LightKeeper::getSceneCamera() const
|
CameraComponent* LightKeeper::getSceneCamera() const
|
||||||
|
|||||||
@@ -304,6 +304,14 @@ namespace {
|
|||||||
check(counter->updates == 1 && counter->fixedUpdates == 1 && counter->lateUpdates == 1,
|
check(counter->updates == 1 && counter->fixedUpdates == 1 && counter->lateUpdates == 1,
|
||||||
"play mode must tick components");
|
"play mode must tick components");
|
||||||
|
|
||||||
|
sceneManager.SetPaused(true);
|
||||||
|
sceneManager.Update(0.1f);
|
||||||
|
sceneManager.FixedUpdate(0.1f);
|
||||||
|
sceneManager.LateUpdate(0.1f);
|
||||||
|
sceneManager.SetPaused(false);
|
||||||
|
check(counter->updates == 1 && counter->fixedUpdates == 1 && counter->lateUpdates == 1,
|
||||||
|
"a paused game must not tick components");
|
||||||
|
|
||||||
sceneManager.Destroy();
|
sceneManager.Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user