319 lines
10 KiB
C++
319 lines
10 KiB
C++
#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
|