在 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)更需要优先处理输入。
例如:
ImGuiLayer
GameLayer
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,就能轻松扩展引擎或游戏的功能。