From 641b255cb2598035f8be2a89019a4170dd9598fd Mon Sep 17 00:00:00 2001 From: atdunbg Date: Mon, 5 Oct 2026 11:34:27 +0800 Subject: [PATCH] Unify input behind Engine::Input facade; fix Ctrl+P and Pause button New Engine/Core/Input.h/.cpp: static input facade with a Key/MouseButton enum (values map to GLFW key codes internally) so scripts and game logic no longer touch GLFW directly - window backend becomes swappable and the facade is the surface to expose when embedding scripting later. Wired up in Application's constructor; Camera (editor camera WASD/RMB/MMB), the WASDMove script and SandboxApp key handling now query Input only. Ctrl+P hotkey fix: the !m_Playing guard made the hotkey dead exactly when it was needed - after pressing Play. Now toggles both ways. Toolbar Pause button now reads "Resume" while paused (tooltip follows). --- App/editor/Editor.cpp | 15 ++--- App/main.cpp | 9 +-- App/scripts/WASDMove.cpp | 17 +++--- Engine/include/Engine/Core/Input.h | 89 ++++++++++++++++++++++++++++++ Engine/src/Core/Application.cpp | 5 ++ Engine/src/Core/Input.cpp | 35 ++++++++++++ Engine/src/Renderer/Camera.cpp | 20 +++---- 7 files changed, 161 insertions(+), 29 deletions(-) create mode 100644 Engine/include/Engine/Core/Input.h create mode 100644 Engine/src/Core/Input.cpp diff --git a/App/editor/Editor.cpp b/App/editor/Editor.cpp index 70602e8..b000b46 100644 --- a/App/editor/Editor.cpp +++ b/App/editor/Editor.cpp @@ -26,8 +26,6 @@ #include // DockBuilder*(内部 API) #include -#include - #include #include @@ -283,8 +281,9 @@ void Editor::DrawMenuBar() void Editor::DrawToolbar() { const ImGuiIO& io = ImGui::GetIO(); - // Ctrl+P:文本输入中(改名/路径框)不触发,防打字误切 PIE - if (!m_Playing && io.KeyCtrl && !io.WantTextInput && + // Ctrl+P 全局热键:编辑态进 PIE,运行态停回编辑器(双向)。 + // 文本输入中(改名/路径框)不触发,防打字误切 + if (io.KeyCtrl && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_P, false)) { TogglePlay(); @@ -293,12 +292,14 @@ void Editor::DrawToolbar() // 按钮加大触感(工具栏行高 = 一个按钮高;DockSpace avail 自适应) ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 4.0f)); const ImGuiStyle& st = ImGui::GetStyle(); + const char* pauseLabel = m_Paused ? "Resume" : "Pause"; // Play 组宽度 → 水平居中(编辑态 1 钮 / 运行态 2 钮) float groupW = 0.0f; if (m_Playing) { - groupW = ImGui::CalcTextSize("Pause").x + ImGui::CalcTextSize("Stop").x + + groupW = ImGui::CalcTextSize(pauseLabel).x + + ImGui::CalcTextSize("Stop").x + 4.0f * st.FramePadding.x + st.ItemSpacing.x; } else @@ -309,7 +310,7 @@ void Editor::DrawToolbar() if (m_Playing) { - // Pause:暂停中亮黄常驻(UE 按下态),运行中暗黄 + // Pause/Resume:暂停中亮黄常驻(UE 按下态),运行中暗黄 ImGui::PushStyleColor(ImGuiCol_Button, m_Paused ? ImVec4(0.85f, 0.62f, 0.12f, 1.0f) : ImVec4(0.55f, 0.42f, 0.10f, 1.0f)); @@ -318,7 +319,7 @@ void Editor::DrawToolbar() : ImVec4(0.65f, 0.50f, 0.14f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.95f, 0.76f, 0.24f, 1.0f)); - if (ImGui::Button("Pause")) + if (ImGui::Button(pauseLabel)) { TogglePause(); } diff --git a/App/main.cpp b/App/main.cpp index 923b184..f996316 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -24,8 +24,8 @@ #include #include -#include - +#include + #include "Editor.h" #include "EditorContext.h" @@ -94,11 +94,12 @@ namespace void OnKeyPress(const Event& event) override { - if (event.key == GLFW_KEY_GRAVE_ACCENT) + // 键码统一走 Input::Key(App 层零 GLFW 依赖) + if (event.key == static_cast(Input::Key::GraveAccent)) { SetImGuiEnabled(!GetImGuiEnabled()); } - if (event.key == GLFW_KEY_F1) + if (event.key == static_cast(Input::Key::F1)) { STATS.PrintComparison(); } diff --git a/App/scripts/WASDMove.cpp b/App/scripts/WASDMove.cpp index d3a8665..9ea3888 100644 --- a/App/scripts/WASDMove.cpp +++ b/App/scripts/WASDMove.cpp @@ -12,15 +12,16 @@ #include #include -#include +#include #include #include #include -#include #include #include +using namespace Engine; // Input 门面(统一输入入口) + namespace { class WASDMove final : public Engine::Scripting::ScriptBehavior { @@ -29,7 +30,8 @@ namespace { void OnUpdate(float dt) override { // 位移方向 = 视线水平投影(W 前进 / A 左 / D 右),UE Scene - // view 移动惯例;Y 锁 0(升降/跳跃归物理或将来扩展) + // view 移动惯例;Y 锁 0(升降/跳跃归物理或将来扩展)。 + // 键鼠查询统一走 Input 门面(脚本层零 GLFW 依赖) const glm::quat cq = App().GetRenderer() .GetEditorCamera() .GetTransform() @@ -37,12 +39,11 @@ namespace { const glm::vec3 fwd = cq * glm::vec3(0.0f, 0.0f, -1.0f); const glm::vec3 right = cq * glm::vec3(1.0f, 0.0f, 0.0f); - auto& win = App().GetWindow(); glm::vec3 move(0.0f); - if (win.IsKeyPressed(GLFW_KEY_W)) move += fwd; - if (win.IsKeyPressed(GLFW_KEY_S)) move -= fwd; - if (win.IsKeyPressed(GLFW_KEY_D)) move += right; - if (win.IsKeyPressed(GLFW_KEY_A)) move -= right; + if (Input::IsKeyDown(Input::Key::W)) move += fwd; + if (Input::IsKeyDown(Input::Key::S)) move -= fwd; + if (Input::IsKeyDown(Input::Key::D)) move += right; + if (Input::IsKeyDown(Input::Key::A)) move -= right; move.y = 0.0f; if (glm::length(move) > 1e-4f) { move = glm::normalize(move); diff --git a/Engine/include/Engine/Core/Input.h b/Engine/include/Engine/Core/Input.h new file mode 100644 index 0000000..829b351 --- /dev/null +++ b/Engine/include/Engine/Core/Input.h @@ -0,0 +1,89 @@ +// +// Input.h:统一输入门面(脚本/游戏逻辑唯一入口)。 +// +// 目的: +// 1. 上层代码(脚本、SandboxApp、编辑器相机)不再直接依赖 GLFW—— +// 只依赖本枚举与门面,Window 后端将来可换(SDL/平台原生) +// 2. 嵌入脚本时暴露的也是这一套(Lua/Python 绑定包一层即可) +// +// 键码值 = GLFW 物理键码(glf3.h 标准值),强转 int 可直接传给 +// Window::IsKeyPressed;但调用方不得依赖这一点。 +// +// 语义:轮询接口沿用 Window 的门控规则——UI 捕获键盘(编辑器文本框 +// 输入中)时 IsKeyDown 返回 false,游戏侧静默;鼠标捕获同理。 +// + +#pragma once + +#include + +namespace Engine { + + class Window; + + namespace Input { + + enum class Key : int { + // ---- 可打印区(值 = ASCII)---- + Space = 32, + Apostrophe = 39, + Comma = 44, + Minus = 45, + Period = 46, + Slash = 47, + Num0 = 48, Num1 = 49, Num2 = 50, Num3 = 51, Num4 = 52, + Num5 = 53, Num6 = 54, Num7 = 55, Num8 = 56, Num9 = 57, + Semicolon = 59, + Equal = 61, + A = 65, B = 66, C = 67, D = 68, E = 69, F = 70, G = 71, + H = 72, I = 73, J = 74, K = 75, L = 76, M = 77, N = 78, + O = 79, P = 80, Q = 81, R = 82, S = 83, T = 84, U = 85, + V = 86, W = 87, X = 88, Y = 89, Z = 90, + BracketLeft = 91, + Backslash = 92, + BracketRight = 93, + GraveAccent = 96, + // ---- 控制键(值 = GLFW 键码)---- + Escape = 256, + Enter = 257, + Tab = 258, + Backspace = 259, + Insert = 260, + Delete = 261, + Right = 262, + Left = 263, + Down = 264, + Up = 265, + PageUp = 266, + PageDown = 267, + Home = 268, + End = 269, + F1 = 290, F2 = 291, F3 = 292, F4 = 293, F5 = 294, F6 = 295, + F7 = 296, F8 = 297, F9 = 298, F10 = 299, F11 = 300, + F12 = 301, + // ---- 修饰键 ---- + LeftShift = 340, + LeftControl = 341, + LeftAlt = 342, + LeftSuper = 343, + RightShift = 344, + RightControl = 345, + RightAlt = 346, + RightSuper = 347, + }; + + enum class MouseButton : int { + Left = 0, // GLFW_MOUSE_BUTTON_1 + Right = 1, + Middle = 2, + }; + + // 装配:Application 构造时自动调用(后端 Window 就绪后) + void Init(const Window& window); + + // 轮询查询(UI 捕获对应输入时返回 false——游戏侧静默) + bool IsKeyDown(Key key); + bool IsMouseButtonDown(MouseButton button); + } + +} // namespace Engine diff --git a/Engine/src/Core/Application.cpp b/Engine/src/Core/Application.cpp index daf15a7..4f51e7b 100644 --- a/Engine/src/Core/Application.cpp +++ b/Engine/src/Core/Application.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,10 @@ namespace Engine { // 资产层不感知 Vulkan 具体类。 // 声明在 Renderer 之后 → 析构先于 Renderer(它引用其子模块) m_Assets = CreateScope(m_Renderer->GetAssetUploader()); + + // 统一输入门面装配:脚本/游戏逻辑经 Input 查询键鼠,不直接 + // 依赖 Window 后端(GLFW 解耦) + Input::Init(*m_Window); } Application::~Application() diff --git a/Engine/src/Core/Input.cpp b/Engine/src/Core/Input.cpp new file mode 100644 index 0000000..88b4c52 --- /dev/null +++ b/Engine/src/Core/Input.cpp @@ -0,0 +1,35 @@ +// +// Input.cpp:统一输入门面实现(转发 Window 后端轮询接口)。 +// + +#include "Engine/Core/Input.h" +#include "Engine/Core/Window.h" + +namespace Engine { + namespace Input { + + namespace { + // Application 生命周期内有效(成员析构序:Input 无状态依赖, + // 仅 Application::Run 期间被查询) + const Window* s_Window = nullptr; + } + + void Init(const Window& window) + { + s_Window = &window; + } + + bool IsKeyDown(const Key key) + { + return s_Window != nullptr && + s_Window->IsKeyPressed(static_cast(key)); + } + + bool IsMouseButtonDown(const MouseButton button) + { + return s_Window != nullptr && + s_Window->IsMouseButtonPressed(static_cast(button)); + } + + } +} // namespace Engine diff --git a/Engine/src/Renderer/Camera.cpp b/Engine/src/Renderer/Camera.cpp index 02d5052..7f14e1c 100644 --- a/Engine/src/Renderer/Camera.cpp +++ b/Engine/src/Renderer/Camera.cpp @@ -10,7 +10,7 @@ #include -#include "GLFW/glfw3.h" +#include "Engine/Core/Input.h" // 键查询走统一输入门面(不依赖 GLFW) namespace Engine { @@ -35,9 +35,9 @@ namespace Engine { // 光标型交互互斥共用捕获;编辑器模式 flyRequiresRMB=true 时 // 移动键仅 RMB 按住生效(否则与 Gizmo W/E/R 热键冲突)---- const bool wantLook = m_MouseLookEnabled && - window.IsMouseButtonPressed(GLFW_MOUSE_BUTTON_RIGHT); + Input::IsMouseButtonDown(Input::MouseButton::Right); const bool wantOrbit = m_MouseLookEnabled && - window.IsMouseButtonPressed(GLFW_MOUSE_BUTTON_MIDDLE); + Input::IsMouseButtonDown(Input::MouseButton::Middle); if (wantLook || wantOrbit) { @@ -70,7 +70,7 @@ namespace Engine { if (wantOrbit) { - if (window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) + if (Input::IsKeyDown(Input::Key::LeftShift)) { // ---- 屏幕平面平移:内容跟手(拖右 → 相机左移、 // 拖下 → 相机上移),pivot 同步平移保持环绕点的 @@ -120,12 +120,12 @@ namespace Engine { if (wantLook || !flyRequiresRMB) { const f32 speed = m_MoveSpeed * deltaTime; - if (window.IsKeyPressed(GLFW_KEY_W)) { transform.SetPosition(transform.GetPosition() + forward * speed); } - if (window.IsKeyPressed(GLFW_KEY_S)) { transform.SetPosition(transform.GetPosition() - forward * speed); } - if (window.IsKeyPressed(GLFW_KEY_A)) { transform.SetPosition(transform.GetPosition() - right * speed); } - if (window.IsKeyPressed(GLFW_KEY_D)) { transform.SetPosition(transform.GetPosition() + right * speed); } - if (window.IsKeyPressed(GLFW_KEY_SPACE)) { transform.SetPosition(transform.GetPosition() + glm::vec3(0, speed, 0)); } - if (window.IsKeyPressed(GLFW_KEY_LEFT_SHIFT)) { transform.SetPosition(transform.GetPosition() - glm::vec3(0, speed, 0)); } + if (Input::IsKeyDown(Input::Key::W)) { transform.SetPosition(transform.GetPosition() + forward * speed); } + if (Input::IsKeyDown(Input::Key::S)) { transform.SetPosition(transform.GetPosition() - forward * speed); } + if (Input::IsKeyDown(Input::Key::A)) { transform.SetPosition(transform.GetPosition() - right * speed); } + if (Input::IsKeyDown(Input::Key::D)) { transform.SetPosition(transform.GetPosition() + right * speed); } + if (Input::IsKeyDown(Input::Key::Space)) { transform.SetPosition(transform.GetPosition() + glm::vec3(0, speed, 0)); } + if (Input::IsKeyDown(Input::Key::LeftShift)) { transform.SetPosition(transform.GetPosition() - glm::vec3(0, speed, 0)); } } // ---- 滚轮推拉:有 pivot 时改环绕距离(指数步进,近处精细/