Files
XCEngine/editor/src/Core/PlaySessionController.cpp

319 lines
10 KiB
C++
Raw Normal View History

#include "Core/PlaySessionController.h"
#include "Core/EditorEvents.h"
#include "Core/EventBus.h"
#include "Core/IEditorContext.h"
#include "Core/ISceneManager.h"
#include "Core/IUndoManager.h"
#include <XCEngine/Input/InputManager.h>
#include <XCEngine/Scripting/ScriptEngine.h>
namespace XCEngine {
namespace Editor {
namespace {
bool IsModifierKeyDown(const GameViewInputFrameEvent& input, XCEngine::Input::KeyCode key) {
const size_t index = static_cast<size_t>(key);
return index < input.keyDown.size() && input.keyDown[index];
}
bool IsGameViewInputActive(const GameViewInputFrameEvent& input) {
return input.hovered || input.focused;
}
} // namespace
void PlaySessionController::Attach(IEditorContext& context) {
if (m_playStartRequestedHandlerId == 0) {
m_playStartRequestedHandlerId = context.GetEventBus().Subscribe<PlayModeStartRequestedEvent>(
[this, &context](const PlayModeStartRequestedEvent&) {
StartPlay(context);
});
}
if (m_playStopRequestedHandlerId == 0) {
m_playStopRequestedHandlerId = context.GetEventBus().Subscribe<PlayModeStopRequestedEvent>(
[this, &context](const PlayModeStopRequestedEvent&) {
StopPlay(context);
});
}
if (m_playPauseRequestedHandlerId == 0) {
m_playPauseRequestedHandlerId = context.GetEventBus().Subscribe<PlayModePauseRequestedEvent>(
[this, &context](const PlayModePauseRequestedEvent&) {
PausePlay(context);
});
}
if (m_playResumeRequestedHandlerId == 0) {
m_playResumeRequestedHandlerId = context.GetEventBus().Subscribe<PlayModeResumeRequestedEvent>(
[this, &context](const PlayModeResumeRequestedEvent&) {
ResumePlay(context);
});
}
if (m_playStepRequestedHandlerId == 0) {
m_playStepRequestedHandlerId = context.GetEventBus().Subscribe<PlayModeStepRequestedEvent>(
[this, &context](const PlayModeStepRequestedEvent&) {
StepPlay(context);
});
}
if (m_gameViewInputFrameHandlerId == 0) {
m_gameViewInputFrameHandlerId = context.GetEventBus().Subscribe<GameViewInputFrameEvent>(
[this](const GameViewInputFrameEvent& event) {
m_pendingGameViewInput = event;
m_hasPendingGameViewInput = true;
});
}
}
void PlaySessionController::Detach(IEditorContext& context) {
StopPlay(context);
ClearRuntimeSceneSyncSubscription(context);
if (m_playStartRequestedHandlerId != 0) {
context.GetEventBus().Unsubscribe<PlayModeStartRequestedEvent>(m_playStartRequestedHandlerId);
m_playStartRequestedHandlerId = 0;
}
if (m_playStopRequestedHandlerId != 0) {
context.GetEventBus().Unsubscribe<PlayModeStopRequestedEvent>(m_playStopRequestedHandlerId);
m_playStopRequestedHandlerId = 0;
}
if (m_playPauseRequestedHandlerId != 0) {
context.GetEventBus().Unsubscribe<PlayModePauseRequestedEvent>(m_playPauseRequestedHandlerId);
m_playPauseRequestedHandlerId = 0;
}
if (m_playResumeRequestedHandlerId != 0) {
context.GetEventBus().Unsubscribe<PlayModeResumeRequestedEvent>(m_playResumeRequestedHandlerId);
m_playResumeRequestedHandlerId = 0;
}
if (m_playStepRequestedHandlerId != 0) {
context.GetEventBus().Unsubscribe<PlayModeStepRequestedEvent>(m_playStepRequestedHandlerId);
m_playStepRequestedHandlerId = 0;
}
if (m_gameViewInputFrameHandlerId != 0) {
context.GetEventBus().Unsubscribe<GameViewInputFrameEvent>(m_gameViewInputFrameHandlerId);
m_gameViewInputFrameHandlerId = 0;
}
ResetRuntimeInputBridge();
}
void PlaySessionController::Update(IEditorContext& context, float deltaTime) {
(void)context;
if (!m_runtimeLoop.IsRunning()) {
return;
}
ApplyGameViewInputFrame(deltaTime);
m_runtimeLoop.Tick(deltaTime);
}
bool PlaySessionController::StartPlay(IEditorContext& context) {
if (context.GetRuntimeMode() != EditorRuntimeMode::Edit) {
return false;
}
auto& sceneManager = context.GetSceneManager();
if (!sceneManager.HasActiveScene()) {
return false;
}
m_editorSnapshot = sceneManager.CaptureSceneSnapshot();
if (!m_editorSnapshot.hasScene) {
return false;
}
if (!sceneManager.RestoreSceneSnapshot(m_editorSnapshot)) {
return false;
}
sceneManager.SetSceneDocumentDirtyTrackingEnabled(false);
XCEngine::Scripting::ScriptEngine::Get().SetRuntimeFixedDeltaTime(m_runtimeLoop.GetSettings().fixedDeltaTime);
ResetRuntimeInputBridge();
XCEngine::Input::InputManager::Get().Shutdown();
XCEngine::Input::InputManager::Get().Initialize(nullptr);
m_runtimeLoop.Start(sceneManager.GetScene());
EnsureRuntimeSceneSyncSubscription(context);
context.GetUndoManager().ClearHistory();
context.SetRuntimeMode(EditorRuntimeMode::Play);
context.GetEventBus().Publish(PlayModeStartedEvent{});
return true;
}
bool PlaySessionController::StopPlay(IEditorContext& context) {
if (!IsEditorRuntimeActive(context.GetRuntimeMode())) {
return false;
}
ClearRuntimeSceneSyncSubscription(context);
auto& sceneManager = context.GetSceneManager();
m_runtimeLoop.Stop();
ResetRuntimeInputBridge();
XCEngine::Input::InputManager::Get().Shutdown();
sceneManager.SetSceneDocumentDirtyTrackingEnabled(true);
if (!sceneManager.RestoreSceneSnapshot(m_editorSnapshot)) {
return false;
}
context.GetUndoManager().ClearHistory();
context.SetRuntimeMode(EditorRuntimeMode::Edit);
context.GetEventBus().Publish(PlayModeStoppedEvent{});
m_editorSnapshot = {};
return true;
}
bool PlaySessionController::PausePlay(IEditorContext& context) {
if (context.GetRuntimeMode() != EditorRuntimeMode::Play || !m_runtimeLoop.IsRunning()) {
return false;
}
m_runtimeLoop.Pause();
context.SetRuntimeMode(EditorRuntimeMode::Paused);
context.GetEventBus().Publish(PlayModePausedEvent{});
return true;
}
bool PlaySessionController::ResumePlay(IEditorContext& context) {
if (context.GetRuntimeMode() != EditorRuntimeMode::Paused || !m_runtimeLoop.IsRunning()) {
return false;
}
m_runtimeLoop.Resume();
context.SetRuntimeMode(EditorRuntimeMode::Play);
context.GetEventBus().Publish(PlayModeResumedEvent{});
return true;
}
bool PlaySessionController::StepPlay(IEditorContext& context) {
if (context.GetRuntimeMode() != EditorRuntimeMode::Paused || !m_runtimeLoop.IsRunning()) {
return false;
}
m_runtimeLoop.StepFrame();
return true;
}
void PlaySessionController::ResetRuntimeInputBridge() {
m_pendingGameViewInput = {};
m_appliedGameViewInput = {};
m_hasPendingGameViewInput = false;
}
void PlaySessionController::EnsureRuntimeSceneSyncSubscription(IEditorContext& context) {
if (m_runtimeSceneChangedHandlerId != 0) {
return;
}
m_runtimeSceneChangedHandlerId = context.GetEventBus().Subscribe<SceneChangedEvent>(
[this, &context](const SceneChangedEvent&) {
SyncRuntimeSceneIfNeeded(context);
});
}
void PlaySessionController::ClearRuntimeSceneSyncSubscription(IEditorContext& context) {
if (m_runtimeSceneChangedHandlerId == 0) {
return;
}
context.GetEventBus().Unsubscribe<SceneChangedEvent>(m_runtimeSceneChangedHandlerId);
m_runtimeSceneChangedHandlerId = 0;
}
void PlaySessionController::SyncRuntimeSceneIfNeeded(IEditorContext& context) {
if (!IsEditorRuntimeActive(context.GetRuntimeMode())) {
return;
}
auto* currentScene = context.GetSceneManager().GetScene();
if (currentScene == m_runtimeLoop.GetScene()) {
return;
}
m_runtimeLoop.ReplaceScene(currentScene);
}
void PlaySessionController::ApplyGameViewInputFrame(float deltaTime) {
using XCEngine::Input::InputManager;
using XCEngine::Input::KeyCode;
using XCEngine::Input::MouseButton;
InputManager& inputManager = InputManager::Get();
inputManager.Update(deltaTime);
const GameViewInputFrameEvent input = m_hasPendingGameViewInput
? m_pendingGameViewInput
: GameViewInputFrameEvent{};
m_hasPendingGameViewInput = false;
const bool inputActive = IsGameViewInputActive(input);
const bool alt = inputActive &&
(IsModifierKeyDown(input, KeyCode::LeftAlt) || IsModifierKeyDown(input, KeyCode::RightAlt));
const bool ctrl = inputActive &&
(IsModifierKeyDown(input, KeyCode::LeftCtrl) || IsModifierKeyDown(input, KeyCode::RightCtrl));
const bool shift = inputActive &&
(IsModifierKeyDown(input, KeyCode::LeftShift) || IsModifierKeyDown(input, KeyCode::RightShift));
for (size_t index = 0; index < input.keyDown.size(); ++index) {
const bool wasDown = m_appliedGameViewInput.keyDown[index];
const bool isDown = inputActive && input.keyDown[index];
if (wasDown == isDown) {
continue;
}
const KeyCode key = static_cast<KeyCode>(index);
if (isDown) {
inputManager.ProcessKeyDown(key, false, alt, ctrl, shift, false);
} else {
inputManager.ProcessKeyUp(key, alt, ctrl, shift, false);
}
}
for (size_t index = 0; index < input.mouseButtonDown.size(); ++index) {
const bool wasDown = m_appliedGameViewInput.mouseButtonDown[index];
const bool isDown = inputActive && input.mouseButtonDown[index];
if (wasDown == isDown) {
continue;
}
inputManager.ProcessMouseButton(
static_cast<MouseButton>(index),
isDown,
static_cast<int>(input.mousePosition.x),
static_cast<int>(input.mousePosition.y));
}
if (inputActive &&
(input.mousePosition != m_appliedGameViewInput.mousePosition || input.mouseDelta != XCEngine::Math::Vector2::Zero())) {
inputManager.ProcessMouseMove(
static_cast<int>(input.mousePosition.x),
static_cast<int>(input.mousePosition.y),
static_cast<int>(input.mouseDelta.x),
static_cast<int>(input.mouseDelta.y));
}
if (inputActive && input.mouseWheel != 0.0f) {
inputManager.ProcessMouseWheel(
input.mouseWheel,
static_cast<int>(input.mousePosition.x),
static_cast<int>(input.mousePosition.y));
}
m_appliedGameViewInput = {};
if (inputActive) {
m_appliedGameViewInput = input;
}
}
} // namespace Editor
} // namespace XCEngine