Hazel 引擎学习笔记 (十三): Layers(层系统)全解析
游戏引擎 12

在 Hazel 游戏引擎中,“Layer(层)”是一个非常核心的概念。它背后代表的是引擎对于逻辑模块、渲染模块、UI 模块等功能的一种组织与调度方式

这一集的主要目的,是引入 Hazel 引擎的“层系统”,为后续的渲染、事件处理、ImGui 覆盖层等特性做准备。

本文将从以下内容展开:

  • 为什么需要 Layer 系统?

  • Layer 类的结构和作用

  • LayerStack 层栈的设计思路

  • 层的压入与移除(Push / Pop)

  • 层的迭代顺序与事件分发逻辑

  • 这一集的完整总结


1. 为什么游戏引擎要设计 Layer 系统?

在游戏中,你通常会有多个不同功能的模块同时在运行,例如:

  • 游戏世界的渲染

  • 背景音乐

  • 物理系统

  • UI / HUD

  • Debug 面板(如 ImGui)

  • 后期处理效果(Post-processing)

  • 输入处理

这些模块之间有一个共同点:

它们都需要参与 Update() 调用,也需要接收输入与窗口事件。

如果全部塞进 Application.cpp 中,不仅代码越来越臃肿,而且模块之间耦合会极高。

因此 Hazel 引擎希望引入一种机制:

  • 能动态添加 / 移除模块

  • 层之间可以有顺序

  • 引擎能自动遍历这些层,调用 Update、OnEvent 等

  • 支持 Overlay(UI、调试窗口)永远在最上层

这就是 Layer 系统被引入的原因。


2. Layer 类结构与作用

Hazel 的 Layer 类非常简单,只是定义了一组回调函数:

class HAZEL_API Layer
{
public:
    Layer(const std::string& name = "Layer");
    virtual ~Layer();

    virtual void OnAttach() {}
    virtual void OnDetach() {}
    virtual void OnUpdate() {}
    virtual void OnEvent(Event& event) {}

    inline const std::string& GetName() const { return m_DebugName; }
protected:
    std::string m_DebugName;
};

这些函数是什么含义?

OnAttach()

  • 当 Layer 被加入到 LayerStack 时触发

  • 一般用于初始化资源,例如加载纹理、初始化状态等

OnDetach()

  • Layer 从 LayerStack 移除时触发

  • 用于释放资源

OnUpdate()

  • 每一帧调用一次,用于逻辑更新与渲染调用

OnEvent(Event& event)

  • 地址窗口输入、按键、鼠标等事件

GetName()

  • 用于 Debug 显示层名字


3. LayerStack:管理所有 Layer 的容器

在 Hazel 中,层不是单独存在的,而是放在一个容器里,即:

std::vector<Layer*> m_Layers;

但 Hazel 并不是简单地把所有层 append 到 vector 里,而是区分:

  • 普通层(GameLayer、RenderLayer)

  • 覆盖层 Overlay(例如 ImGuiLayer)

Overlay 永远放在最上层。

特别设计:m_LayerInsert

LayerStack 使用一个插入点,让普通层与 overlay 分区:

std::vector<Layer*>::iterator m_LayerInsert;

插入普通层:

| 普通层 | 普通层 | ↑ m_LayerInsert | Overlay | Overlay |

Overlay 总是在 vector 末尾 push。


4. 层的加入与移除

PushLayer(插入普通层)

void LayerStack::PushLayer(Layer* layer)
{
    m_LayerInsert = m_Layers.emplace(m_LayerInsert, layer);
}

普通层插入到 m_LayerInsert 前位置,自动维护插入点。

PushOverlay(插入覆盖层)

void LayerStack::PushOverlay(Layer* overlay)
{
    m_Layers.emplace_back(overlay);
}

Overlay 永远在最上层。


5. Layer 的事件分发(重点)

核心逻辑在:

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;
    }
}

事件分发顺序

从顶层(Overlay)开始向下传递
因为越上层(比如 UI)更需要优先处理输入。

例如:

  1. ImGuiLayer

  2. GameLayer

  3. BackgroundLayer

如果 ImGui 拦截了鼠标事件(event.Handled = true),则游戏层不会收到这个事件。


6. Layers 这集的核心意义

这一集 Hazel 搭建了整个引擎架构中的一个关键模块。

它让引擎:

✔ 结构清晰
✔ 扩展容易
✔ 模块之间解耦
✔ 支持 UI Overlay
✔ 支持事件从上到下传播
✔ 支持不同层拥有自己的生命周期(Attach、Update、Detach)

在后续:

  • 渲染系统

  • ImGui 编辑器

  • Sandbox App 游戏逻辑

都必须依赖 Layer 系统。


7. 本集总结

Layers(层系统)为 Hazel 提供了一套优雅的管理机制:

  • Layer 是逻辑模块

  • LayerStack 是容器

  • Update 和事件会自动按顺序遍历所有 Layer

  • Overlay(如 ImGui)永远在最上方

  • 事件从顶层向下传播,直到被捕获

通过这一集,Hazel 引擎拥有了真正的“可扩展结构”。从此以后,只需要创建新的 Layer,就能轻松扩展引擎或游戏的功能。

Hazel 引擎学习笔记 (十三): Layers(层系统)全解析
http://localhost:8090/archives/hazel-yin-qing-xue-xi-bi-ji-di-shi-san-ji-layers-ceng-xi-tong-quan-jie-xi
作者
Zxy
发布于
更新于
许可