引言
上一节我们讨论了 ImGUI 并运行了官方示例,今天我们来为我们的引擎编写 ImGUI 事件。
事件声明
在头文件 ImGuiLayer.h
声明 ImGUI 的交互事件:
#pragma once
#include "../Layer.h"
#include "../Events/KeyEvent.h" #include "../Events/MouseEvent.h" #include "../Events/ApplicationEvent.h"
namespace Infinite {
class INFINITE_API ImGuiLayer : public Layer { public: ImGuiLayer(); ~ImGuiLayer();
void OnAttach(); void OnDetach(); void OnUpdate(); void OnEvent(Event& event); private: + bool OnMouseButtonPressedEvent(MouseButtonPressedEvent& e); + bool OnMouseButtonReleasedEvent(MouseButtonReleasedEvent& e); + bool OnMouseMovedEvent(MouseMovedEvent& e); + bool OnMouseScrolledEvent(MouseScrolledEvent& e); + bool OnKeyPressedEvent(KeyPressedEvent& e); + bool OnKeyReleasedEvent(KeyReleasedEvent& e); + bool OnKeyTypedEvent(KeyTypedEvent& e); + bool OnWindowResizeEvent(WindowResizeEvent& e);
private: float m_Time = 0.0f;
}; }
|
事件实现
在 ImGuiLayer.cpp
中添加头文件和事件的模板:
#include "ifnpch.h" #include "ImGuiLayer.h"
#include "../imgui.h" #include "Platform/OpenGL/ImGuiOpenGLRenderer.h"
// TEMPORARY +#include <GLFW/glfw3.h> +#include <glad/glad.h>
#include "../Application.h"
namespace Infinite {
ImGuiLayer::ImGuiLayer() : Layer("ImGuiLayer") {
}
ImGuiLayer::~ImGuiLayer() {
}
void ImGuiLayer::OnAttach() { ImGui::CreateContext(); ImGui::StyleColorsDark();
ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;
// TEMPORARY: should eventually use Infinite key codes io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
ImGui_ImplOpenGL3_Init("#version 410"); }
void ImGuiLayer::OnDetach() {
}
void ImGuiLayer::OnUpdate() { ImGuiIO& io = ImGui::GetIO(); Application& app = Application::Get(); io.DisplaySize = ImVec2(app.GetWindow().GetWidth(), app.GetWindow().GetHeight());
float time = (float)glfwGetTime(); io.DeltaTime = m_Time > 0.0f ? (time - m_Time) : (1.0f / 60.0f); m_Time = time;
ImGui_ImplOpenGL3_NewFrame(); ImGui::NewFrame();
static bool show = true; ImGui::ShowDemoWindow(&show);
ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); }
+ void ImGuiLayer::OnEvent(Event& event) + { + + }
+ bool ImGuiLayer::OnMouseButtonPressedEvent(MouseButtonPressedEvent& e) + { + + }
+ bool ImGuiLayer::OnMouseButtonReleasedEvent(MouseButtonReleasedEvent& e) + { + + }
+ bool ImGuiLayer::OnMouseMovedEvent(MouseMovedEvent& e) + { + + }
+ bool ImGuiLayer::OnMouseScrolledEvent(MouseScrolledEvent& e) + { + + }
+ bool ImGuiLayer::OnKeyPressedEvent(KeyPressedEvent& e) + { + + }
+ bool ImGuiLayer::OnKeyReleasedEvent(KeyReleasedEvent& e) + { + + }
+ bool ImGuiLayer::OnKeyTypedEvent(KeyTypedEvent& e) + { + + }
+ bool ImGuiLayer::OnWindowResizeEvent(WindowResizeEvent& e) + { + + } }
|
下面我们依次实现这些事件:
Event
void ImGuiLayer::OnEvent(Event& event) { EventDispatcher dispatcher(event); dispatcher.Dispatch<MouseButtonPressedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnMouseButtonPressedEvent)); dispatcher.Dispatch<MouseButtonReleasedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnMouseButtonReleasedEvent)); dispatcher.Dispatch<MouseMovedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnMouseMovedEvent)); dispatcher.Dispatch<MouseScrolledEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnMouseScrolledEvent)); dispatcher.Dispatch<KeyPressedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnKeyPressedEvent)); dispatcher.Dispatch<KeyTypedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnKeyTypedEvent)); dispatcher.Dispatch<KeyReleasedEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnKeyReleasedEvent)); dispatcher.Dispatch<WindowResizeEvent>(IFN_BIND_EVENT_FN(ImGuiLayer::OnWindowResizeEvent)); }
|
这里的 IFN_BIND_EVENT_FN
则在 Core.h
中定义:
#define IFN_BIND_EVENT_FN(fn) std::bind(&fn, this, std::placeholders::_1)
|
Mouse
bool ImGuiLayer::OnMouseButtonPressedEvent(MouseButtonPressedEvent& e) { ImGuiIO& io = ImGui::GetIO(); io.MouseDown[e.GetMouseButton()] = true;
return false; }
|
bool ImGuiLayer::OnMouseButtonReleasedEvent(MouseButtonReleasedEvent& e) { ImGuiIO& io = ImGui::GetIO(); io.MouseDown[e.GetMouseButton()] = false;
return false; }
|
MouseMoved
bool ImGuiLayer::OnMouseMovedEvent(MouseMovedEvent& e) { ImGuiIO& io = ImGui::GetIO(); io.MousePos = ImVec2(e.GetX(), e.GetY());
return false; }
|
bool ImGuiLayer::OnMouseScrolledEvent(MouseScrolledEvent& e) { ImGuiIO& io = ImGui::GetIO(); io.MouseWheelH += e.GetXOffset(); io.MouseWheel += e.GetYOffset();
return false; }
|
Key
KeyPressed
bool ImGuiLayer::OnKeyPressedEvent(KeyPressedEvent& e) { ImGuiIO& io = ImGui::GetIO(); io.KeysDown[e.GetKeyCode()] = true;
io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; return false; }
|
KeyReleased
bool ImGuiLayer::OnKeyReleasedEvent(KeyReleasedEvent& e) { ImGuiIO& io = ImGui::GetIO(); io.KeysDown[e.GetKeyCode()] = false;
return false; }
|
KeyTyped
KeyTyped 的实现比较复杂,首先我们需要先在 Event.h
添加 KeyTyped
枚举类型:
enum class EventType { None = 0, WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved, AppTick, AppUpdate, AppRender, + KeyPressed, KeyReleased, KeyTyped, MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled };
|
接着在 KeyEvent.h
中添加事件:
class INFINITE_API KeyTypedEvent : public KeyEvent { public: KeyTypedEvent(int keycode) : KeyEvent(keycode) {}
std::string ToString() const override { std::stringstream ss; ss << "KeyTypedEvent: " << m_KeyCode; return ss.str(); }
EVENT_CLASS_TYPE(KeyTyped) };
|
最后回到 ImGuiLayer.cpp
实现事件:
bool ImGuiLayer::OnKeyTypedEvent(KeyTypedEvent& e) { ImGuiIO& io = ImGui::GetIO(); int keycode = e.GetKeyCode(); if (keycode > 0 && keycode < 0x10000) io.AddInputCharacter((unsigned short)keycode);
return false; }
|
Windows
bool ImGuiLayer::OnWindowResizeEvent(WindowResizeEvent& e) { ImGuiIO& io = ImGui::GetIO(); io.DisplaySize = ImVec2(e.GetWidth(), e.GetHeight()); io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); glViewport(0, 0 , e.GetWidth(), e.GetHeight());
return false; }
|
预生成文件
workspace "Infinite" architecture "x64" startproject "Sandbox"
configurations { "Debug", "Release", "Dist" }
outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"
-- Include directories relative to root folder (solution directory) IncludeDir = {} IncludeDir["GLFW"] = "Infinite/vendor/GLFW/include" IncludeDir["GLAD"] = "Infinite/vendor/GLAD/include" IncludeDir["ImGui"] = "Infinite/vendor/imgui/include"
include "Infinite/vendor/GLFW" include "Infinite/vendor/GLAD" include "Infinite/vendor/imgui"
project "Infinite" location "Infinite" kind "SharedLib" language "C++" + staticruntime "off"
targetdir ("bin/" .. outputdir .. "/%{prj.name}") objdir ("bin-int/" .. outputdir .. "/%{prj.name}")
pchheader "ifnpch.h" pchsource "Infinite/src/ifnpch.cpp"
files { "%{prj.name}/src/**.h", "%{prj.name}/src/**.cpp" }
includedirs { "%{prj.name}/src", "%{prj.name}/vendor/spdlog/include", "%{IncludeDir.GLFW}", "%{IncludeDir.GLAD}", "%{IncludeDir.ImGui}" }
links { "GLFW", "GLAD", "ImGui", "opengl32.lib" }
filter "system:windows" cppdialect "C++17" systemversion "latest"
defines { "IFN_PLATFORM_WINDOWS", "IFN_BUILD_DLL", "GLFW_INCLUDE_NONE" }
postbuildcommands { + ("{COPY} %{cfg.buildtarget.relpath} \"../bin/" .. outputdir .. "/Sandbox/\"") }
filter "configurations:Debug" defines "IFN_DEBUG" + runtime "Debug" symbols "On"
filter "configurations:Release" defines "IFN_RELEASE" + runtime "Release" optimize "On"
filter "configurations:Dist" defines "IFN_DIST" + runtime "Release" optimize "On"
project "Sandbox" location "Sandbox" kind "ConsoleApp" + staticruntime "off"
language "C++"
targetdir ("bin/" .. outputdir .. "/%{prj.name}") objdir ("bin-int/" .. outputdir .. "/%{prj.name}")
files { "%{prj.name}/src/**.h", "%{prj.name}/src/**.cpp" }
includedirs { "Infinite/vendor/spdlog/include", "Infinite/src/Infinite", "Infinite/src/Events", "Infinite/src/**.h", "Infinite/src/**.cpp" }
links { "Infinite" }
filter "system:windows" cppdialect "C++17" systemversion "latest"
defines { "IFN_PLATFORM_WINDOWS" }
filter "configurations:Debug" defines "IFN_DEBUG" + runtime "Debug" symbols "On"
filter "configurations:Release" defines "IFN_RELEASE" + runtime "Release" optimize "On"
filter "configurations:Dist" defines "IFN_DIST" + runtime "Release" optimize "On"
|
调试
在调试过程中由于没有给事件实现添加作用域,运行时报错外部符号无法解析,以后需要多加注意。
Mouse
Key