Hazel游戏引擎(013)Layers游戏的层级
文中若有代码、术语等错误,欢迎指正
文章目录
前言
-
此节目的
-
为完成008事件系统设计的第四步,将事件从Application传递分发给Layer层。
-
使引擎事件系统模块完整
-
-
Layer的理解
想象同Ps中一张图有多个层级,可以在层级上绘制图画
-
Layer的设计
-
数据结构:vector
-
渲染顺序
从前往后渲染各个层的图像,这样后面渲染的会覆盖前面渲染的图像,在屏幕的最顶层。
-
处理事件顺序
从后往前依次处理事件,当一个事件被一个层处理完不会传递给前一个层,结合渲染顺序,这样在屏幕最顶层的(也就是在vector最后的layer)图像最先处理事件。
-
例子解释
比如常见的3D游戏有UI。
渲染顺序:将3D图形先渲染,再渲染2DUI,这样屏幕上2DUI永远在3D图形上方,显示正确;
事件顺序:点击屏幕的图形,应该是2DUI最先处理,如果是相应UI事件,处理完后不传递给前一个3D层,若不是自己的UI事件,才传递给前一个3D层。
-
增加Layer后的主要类图
注意区分LayerStack、Layer以及ExampleLayer
-
LayerStack
管理Layer层的类
-
Layer
所有层的父类,定义了虚函数
-
Examplayer
真正需要更新和处理事件的层,被添加到LayerStack的vector中
项目相关
代码
-
Layer
#pragma once #include "Hazel/Core.h" #include "Hazel/Events/Event.h" namespace Hazel { 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; }; }
-
LayerStack
#pragma once namespace Hazel { class HAZEL_API LayerStack{ public: LayerStack(); ~LayerStack(); void PushLayer(Layer* layer); // vector在头部添加一个层 void PushOverlay(Layer* overlay);// 在vector末尾添加一个覆盖层,在屏幕的最上方的层 void PopLayer(Layer* layer); // vector弹出指定层 void PopOverlay(Layer* overlay);// vector弹出覆盖层 std::vector<Layer*>::iterator begin() { return m_Layers.begin(); } std::vector<Layer*>::iterator end() { return m_Layers.end(); } private: std::vector<Layer*> m_Layers; std::vector<Layer*>::iterator m_LayerInsert; }; }
namespace Hazel { LayerStack::LayerStack(){ m_LayerInsert = m_Layers.begin(); } LayerStack::~LayerStack(){ for (Layer* layer : m_Layers) delete layer; } void LayerStack::PushLayer(Layer* layer){ // emplace在vector容器指定位置之前插入一个新的元素。返回插入元素的位置 // 插入 1 2 3,vector是 3 2 1 m_LayerInsert = m_Layers.emplace(m_LayerInsert, layer); } void LayerStack::PushOverlay(Layer* overlay){ m_Layers.emplace_back(overlay); } void LayerStack::PopLayer(Layer* layer){ auto it = std::find(m_Layers.begin(), m_Layers.end(), layer); if (it != m_Layers.end()){ m_Layers.erase(it); m_LayerInsert--; // 指向Begin } } void LayerStack::PopOverlay(Layer* overlay){ auto it = std::find(m_Layers.begin(), m_Layers.end(), overlay); if (it != m_Layers.end()) m_Layers.erase(it); } }
-
SandboxApp
class ExampleLayer : public Hazel::Layer{ public: ExampleLayer() : Layer("Example"){} void OnUpdate() override{ HZ_INFO("ExampleLayer::Update"); // 最终会被输出 } void OnEvent(Hazel::Event& event) override{ HZ_TRACE("{0}", event); // 最终会被输出 } }; class Sandbox : public Hazel::Application{ public: Sandbox(){ PushLayer(new ExampleLayer()); } ~Sandbox(){} };
-
Application
void Application::PushLayer(Layer* layer){ m_LayerStack.PushLayer(layer); } void Application::PushOverlay(Layer* layer){ m_LayerStack.PushOverlay(layer); } // 回调glfw窗口事件的函数 void Application::OnEvent(Event& e){ // 4.用事件调度器,拦截自己层想要拦截的事件并处理 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(); m_Window->OnUpdate(); // 更新glfw } }
项目流程
-
文字
- Application定义了LayerStack对象m_LayerStack
- 在Sandbox构造函数中,执行PushLayer(new ExampleLayer());,将ExampleLayer放入m_LayerStack的vector中
- Application的OnEvent函数从后往前顺序遍历m_LayerStack的vector,得到ExampleLayer对象,并把事件e作为参数执行它的OnEvent函数,所以一直在控制台输出窗口事件
- Application的OnUpdate函数从前往后遍历m_LayerStack的vector,得到ExampleLayer对象,并执行它的OnUpdate函数,所以一直在控制台输出**“ExampleLayer::Update”**
-
图示
以下是以活动图的样子绘制的,并不符合活动图的规范,但大意是这样
效果
LayerStack类的错误
LayerStack的vector管理layer有错
-
简化的例子
#include <iostream> #include <vector> using namespace std; void Test1() { vector<int> vec; std::vector<int>::iterator m_LayerInsertIndex = vec.begin();// 头部插入位置 // 在头部插入1 2,此时vector 2 1 m_LayerInsertIndex = vec.emplace(m_LayerInsertIndex, 1); m_LayerInsertIndex = vec.emplace(m_LayerInsertIndex, 2); // 在尾部插入4, 此时vector 2 1 4 vec.emplace_back(4); // 在头部插入3, 此时vector并不是 3 2 1 4,而是会报错 //m_LayerInsertIndex = vec.emplace(m_LayerInsertIndex, 3); for (int i = 0; i < vec.size(); i++) { cout << vec[i] << endl; } } int main() { Test1(); return 0; }
-
报错结果
-
分析原因
不太准确,个人猜测可能在尾部插入元素(emplace_back)使vector在内存位置发生改变,会破坏m_LayerInsertIndex这个迭代器无效吧
-
解决方法
在尾部插入元素(emplace_back)后让m_LayerInsertIndex迭代器重新指向头部
#include <iostream> #include <vector> using namespace std; void Test1() { vector<int> vec; std::vector<int>::iterator m_LayerInsertIndex = vec.begin();// 头部插入位置 // 在头部插入1 2,此时vector 2 1 m_LayerInsertIndex = vec.emplace(m_LayerInsertIndex, 1); m_LayerInsertIndex = vec.emplace(m_LayerInsertIndex, 2); // 在尾部插入4, 此时vector 2 1 4 vec.emplace_back(4); // 在头部插入3, 此时vector并不是 3 2 1 4,而是会报错 m_LayerInsertIndex = vec.begin(); // 需要重新让头部迭代器指向头部 m_LayerInsertIndex = vec.emplace(m_LayerInsertIndex, 3); for (int i = 0; i < vec.size(); i++) { cout << vec[i] << endl; } } int main() { Test1(); return 0; }