引言 上一节我们为引擎完善了 ImGUI 事件,对预生成文件进行了修改。今天我们来看看事件系统对输入的轮询。
输入类 首先我们在 Infinite/
下新建 Input.h
头文件,与 Windows
设计类似作为一个接口:
#pragma once #include "Core.h" namespace Infinite { class INFINITE_API Input { public : inline static bool IsKeyPressed (int keycode) { return s_Instance->IsKeyPressedImpl (keycode); } inline static bool IsMouseButtonPressed (int button) { return s_Instance->IsMouseButtonPressedImpl (button); } inline static std::pair<float , float > GetMousePosition () { return s_Instance->GetMousePositionImpl (); } inline static float GetMouseX () { return s_Instance->GetMouseXImpl (); } inline static float GetMouseY () { return s_Instance->GetMouseYImpl (); } protected : virtual bool IsKeyPressedImpl (int keycode) = 0 ; virtual bool IsMouseButtonPressedImpl (int button) = 0 ; virtual std::pair<float , float > GetMousePositionImpl () = 0 ; virtual float GetMouseXImpl () = 0 ; virtual float GetMouseYImpl () = 0 ; private : static Input* s_Instance; }; }
我们设置了一个输入单例,并且配置好了函数实现接口。
Windows输入类 接着我们来到 src/Platform/Windows
文件夹,新建 WindowsWindow.cpp
和 WindowsWindow.h
。
头文件 WindowsInput.h
没什么可说的:
#pragma once #include "Infinite/Input.h" namespace Infinite { class WindowsInput : public Input { protected : virtual bool IsKeyPressedImpl (int keycode) override ; virtual bool IsMouseButtonPressedImpl (int button) override ; virtual std::pair<float , float > GetMousePositionImpl () override ; virtual float GetMouseXImpl () override ; virtual float GetMouseYImpl () override ; }; }
而 WindowsInput.cpp
我们先
#include "ifnpch.h" #include "WindowsInput.h" #include "./Infinite/Application.h" #include <GLFW/glfw3.h> namespace Infinite { Input* Input::s_Instance = new WindowsInput (); bool WindowsInput::IsKeyPressedImpl (int keycode) { } bool WindowsInput::IsMouseButtonPressedImpl (int button) { } std::pair<float , float > WindowsInput::GetMousePositionImpl () { } float WindowsInput::GetMouseXImpl () { } float WindowsInput::GetMouseYImpl () { } }
然而现在我们无法直接获取窗口 GLFWwindow* m_Window
(在 WindowsWindow.h
中),一个可行的解决方案是考虑友元访问,但考虑到封装性最好还是不公开窗口。
因此我们在 Window.h
中加入一个函数 GetNativeWindow
返回一个虚拟的空指针,获取本地窗口:
#pragma once #include "ifnpch.h" #include "Infinite/Core.h" #include "Infinite/Events/Event.h" namespace Infinite { struct WindowProps { std::string Title; unsigned int Width; unsigned int Height; WindowProps(const std::string& title = "Infinite Engine", unsigned int width = 1280, unsigned int height = 720) : Title(title), Width(width), Height(height) { } }; // Interface representing a desktop system based Window class INFINITE_API Window { public: using EventCallbackFn = std::function<void(Event&)>; virtual ~Window() {} virtual void OnUpdate() = 0; virtual unsigned int GetWidth() const = 0; virtual unsigned int GetHeight() const = 0; // Window attributes virtual void SetEventCallback(const EventCallbackFn& callback) = 0; virtual void SetVSync(bool enabled) = 0; virtual bool IsVSync() const = 0; + virtual void* GetNativeWindow() const = 0; static Window* Create(const WindowProps& props = WindowProps()); }; }
声明之后我们就可以在 WindowsWindow.h
返回 m_Window
:
#pragma once #include "Window.h" #include <GLFW/glfw3.h> namespace Infinite { class WindowsWindow : public Window { public: WindowsWindow(const WindowProps& props); virtual ~WindowsWindow(); void OnUpdate() override; inline unsigned int GetWidth() const override { return m_Data.Width; } inline unsigned int GetHeight() const override { return m_Data.Height; } // Window attributes inline void SetEventCallback(const EventCallbackFn& callback) override { m_Data.EventCallback = callback; } void SetVSync(bool enabled) override; bool IsVSync() const override; + inline virtual void* GetNativeWindow() const { return m_Window; } private: virtual void Init(const WindowProps& props); virtual void Shutdown(); private: GLFWwindow* m_Window; struct WindowData { std::string Title; unsigned int Width, Height; bool VSync; EventCallbackFn EventCallback; }; WindowData m_Data; }; }
KeyPressed bool WindowsInput::IsKeyPressedImpl (int keycode) { auto window = static_cast <GLFWwindow*>(Application::Get ().GetWindow ().GetNativeWindow ()); auto state = glfwGetKey (window, keycode); return state == GLFW_PRESS || state == GLFW_REPEAT; }
我们从应用程序 Application
获取窗口 window
,调用 glfw 内置函数 glfwGetKey()
即可。
bool WindowsInput::IsMouseButtonPressedImpl (int button) { auto window = static_cast <GLFWwindow*>(Application::Get ().GetWindow ().GetNativeWindow ()); auto state = glfwGetMouseButton (window, button); return state == GLFW_PRESS; }
获取鼠标按压事件同理,调用 glfwGetMouseButton()
。
MousePos std::pair<float , float > WindowsInput::GetMousePositionImpl () { auto window = static_cast <GLFWwindow*>(Application::Get ().GetWindow ().GetNativeWindow ()); double xpos, ypos; glfwGetCursorPos (window, &xpos, &ypos); return { (float )xpos, (float )ypos }; } float WindowsInput::GetMouseXImpl () { auto [x, y] = GetMousePositionImpl (); return (float )x; } float WindowsInput::GetMouseYImpl () { auto [x, y] = GetMousePositionImpl (); return (float )y; }
如果项目不是 C++17 标准,则使用注释中的实现方法。
调试 在 Application.cpp
加入鼠标坐标调试:
#include "ifnpch.h" #include "Application.h" #include "Events/ApplicationEvent.h" #include "Log.h" #include <glad/glad.h> +#include "Input.h" namespace Infinite { #define BIND_EVENT_FN(x) std::bind(&Application::x, this, std::placeholders::_1) Application* Application::s_Instance = nullptr; Application::Application() { IFN_CORE_ASSERT(!s_Instance, "Application already exists!"); s_Instance = this; m_Window = std::unique_ptr<Window>(Window::Create()); m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent)); } Application::~Application() {} void Application::PushLayer(Layer* layer) { m_LayerStack.PushLayer(layer); layer->OnAttach(); } void Application::PushOverlay(Layer* layer) { m_LayerStack.PushOverlay(layer); layer->OnAttach(); } void Application::OnEvent(Event& e) { EventDispatcher dispatcher(e); dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(OnWindowClose)); for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();) { (*--it)->OnEvent(e); if (e.Handled) break; } } void Application::Run() { while (m_Running) { glClearColor(1, 0, 1, 1); glClear(GL_COLOR_BUFFER_BIT); for (Layer* layer : m_LayerStack) layer->OnUpdate(); + auto [x, y] = Input::GetMousePosition(); + IFN_CORE_TRACE("{0}, {1}", x, y); m_Window->OnUpdate(); } } bool Application::OnWindowClose(WindowCloseEvent& e) { m_Running = false; return true; } }