游戏引擎从零开始(8)-集成imGui

用了两年的Macbook开不了机了,耽搁了几天,积累了多年的资料都没了。难过😞。

前言

完整的引擎都支持编辑的功能,提升开发效率,也让美术、交互等角色参与进来,通过点击拖拽就能实现一些原型效果。

虚幻5的编辑器:

这节我们引入一个非常知名的、轻量的GUI框架,实现编辑的功能。

集成imGui

imGui轻量简洁、功能很强大,能实现非常丰富、绚丽的编辑效果。

下载了最新的imGui代码到本地,代码结构简单,分渲染后端、Demo、辅助工具、控件。

clone imGui

根目录下,clone imgui代码到vendor目录下

bash 复制代码
git submodule add https://github.com/ocornut/imgui.git Sandbox/Hazel/vendor/imgui

更新CMake,添加需要参与编译的源码,include需要的头文件

后端我们采用OpenGL,所以将backends/imgui_impl_opengl3.h、imgui_impl_opengl3.cpp、imgui_impl_opengl3_loader.h三个文件纳入到编译列表中。如果你用的是Metal、或者vulkan等后端,则需要添加上对应的编译文件。
更好的做法是,根据编译条件,比如根据window、mac、linux等环境变量,添加对应的后端。此处图省事,只添加了OpenGL,不对平台做兼容。

Sandbox/Hazel/CMakeLists.txt

bash 复制代码
...
file (GLOB SRC_LIST_IMGUI
        vendor/imgui/*.h
        vendor/imgui/*.cpp
)

set(SRC_LIST_IMGUI_BACKENDS
        vendor/imgui/backends/imgui_impl_opengl3.h
        vendor/imgui/backends/imgui_impl_opengl3.cpp
        vendor/imgui/backends/imgui_impl_opengl3_loader.h
        vendor/imgui/backends/imgui_impl_glfw.h
        vendor/imgui/backends/imgui_impl_glfw.cpp
)

set(SRC_LIST
        ...
        src/Hazel/ImGui/ImGuiLayer.cpp
        src/Hazel/ImGui/ImGuiLayer.h
)


add_library(hazel STATIC ${SRC_LIST} ${SRC_LIST_IMGUI} ${SRC_LIST_IMGUI_BACKENDS})
...

target_include_directories(${PROJECT_NAME} PUBLIC
        ...
        vendor/imgui
        vendor/imgui/backends
        ...

前置开发-完善Application

增加获取Application 和 window的接口,后面封装ImGui用的上。

Sandbox/Hazel/src/Hazel/Application.h

c++ 复制代码
    inline Window& GetWindow() {
        return *m_Window;
    }
    inline static Application& Get() {
        return *s_Instance;
    }

完善PushLayer PushOverlayer,增加layer的OnAttach(). Sandbox/Hazel/src/Hazel/Application.cpp

c++ 复制代码
namespace Hazel{
    ...
    Application* Application::s_Instance = nullptr;

    Application::Application() {
        s_Instance = this;
      }
      
    void Application::PushLayer(Layer *layer) {
        m_LayerStack.PushLayer(layer);
        layer->OnAttach();
    }

    void Application::PushOverlay(Layer *layer) {
        m_LayerStack.PushOverlay(layer);
        layer->OnAttach();
    }

实现ImGuiLayer

ImGui的操作封装到ImGuiLayer类中。ImGuiLayer继承Layer,渲染到整个界面的最上层。

创建文件夹ImGui及ImGuiLayer.h/.cpp文件

重写基类Layer的虚函数, Sandbox/Hazel/src/Hazel/ImGui/ImGuiLayer.h

c++ 复制代码
#pragma once

#include "Layer.h"
namespace Hazel {
class ImGuiLayer : public Layer{
public:
    ImGuiLayer();
    ~ImGuiLayer() override;

    void OnAttach() override;

    void OnDetach() override;

    void OnUpdate() override;

    void OnEvent(Event &event) override;
private:
    float m_Time = 0.0f;
};
}

imGui的使用不是我们的重点,参考下面注释理解。

重点看两个函数。OnAttach()实现了初始化, OnUpdate()实现每帧的更新,这里我们ImGui::ShowDemoWindow显示一个简单的界面.

Sandbox/Hazel/src/Hazel/ImGui/ImGuiLayer.cpp

c++ 复制代码
#include <imgui.h>
#include <GLFW/glfw3.h>
#include <backends/imgui_impl_opengl3.h>
#include "ImGuiLayer.h"
#include "Application.h"

namespace Hazel {
    ImGuiLayer::~ImGuiLayer() {

    }
    // 初始化ImGui
    void ImGuiLayer::OnAttach() {
        ImGui::CreateContext();
        ImGui::StyleColorsDark();
    
    // ImGuiIO用于设置输入事件、窗口属性
        ImGuiIO io = ImGui::GetIO();
        io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
        io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;
        
    // 配置需要监听的按键
        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 330");
    }

    void ImGuiLayer::OnDetach() {
    }
  
  //
    void ImGuiLayer::OnUpdate() {
        ImGuiIO& io = ImGui::GetIO();
        Application& app = Application::Get();
        io.DisplaySize = ImVec2( app.GetWindow().GetWidth(), 2  * app.GetWindow().GetHeight());

        float time = 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(Hazel::Event &event) {
        Layer::OnEvent(event);
    }

    ImGuiLayer::ImGuiLayer() : Layer("ImGuiLayer"){}
}

更新Hazel.h文件

arduino 复制代码
#include "Hazel/ImGui/ImGuiLayer.h"

增加ImGuiLayer

调用的逻辑只有一行. Sandbox/src/SandBoxApp.cpp

c++ 复制代码
Sandbox(){
    PushOverlay(new ExampleLayer());
    PushOverlay(new Hazel::ImGuiLayer());
}

实现效果

不出意外的话, 界面上会出现一个imGui的列表

shit!暂时还有点丑陋,我们后面继续完善。

本节完整代码参考Integrated imGui

相关推荐
汉克老师8 小时前
GESP2025年3月认证C++五级( 第三部分编程题(1、平均分配))
c++·算法·贪心算法·排序·gesp5级·gesp五级
智者知已应修善业11 小时前
【51单片机2个按键控制流水灯运行与暂停】2023-9-6
c++·经验分享·笔记·算法·51单片机
云泽80812 小时前
C++11 核心特性全解:列表初始化、右值引用与移动语义实战
开发语言·c++
AI进化营-智能译站13 小时前
ROS2 C++开发系列12-用多态与虚函数构建可扩展的ROS2机器人行为模块
开发语言·c++·ai·机器人
Morwit13 小时前
QML组件之间的通信方案(暴露子组件)
c++·qt·职场和发展
qeen8713 小时前
【数据结构】建堆的时间复杂度讨论与TOP-K问题
c语言·数据结构·c++·学习·
图码13 小时前
如何用多种方法判断字符串是否为回文?
开发语言·数据结构·c++·算法·阿里云·线性回归·数字雕刻
handler0113 小时前
Linux 内核剖析:进程优先级、上下文切换与 O(1) 调度算法
linux·运维·c语言·开发语言·c++·笔记·算法
zhouwy11313 小时前
Linux进程与线程编程详解
linux·c++
A7bert77715 小时前
【YOLOv8pose部署至RDK X5】模型训练→转换bin→Sunrise 5部署
c++·python·深度学习·yolo·目标检测