前言
游戏中的元素并不会平铺到一层上处理,而是分到不同的层级中。有点像photoshop中的图层。
一个点击事件到底是触发前面的按钮,还是触发后面的人物模型动画呢?
按优先级,应该是优先按钮消费,如果所有元素平铺到同一层上处理,逻辑就耦合到一起。通过分层,按照优先级,上面的层级优先消费,不消费再继续传到下一层。这样设计结构更清晰、也更容易拓展。
成熟的引擎,或者客户端平台比如Android、iOS,都有层级、布局的概念,帮开发者封装好了事件传递机制,不需要开发者关心事件的优先级。
Layer & LayerStack
Layer的处理流程:
Window->SandboxApp->LayerStack->Layer
类调用关系:
Layer
Layer是基类,设计5个函数,主要作用是接收事件和更新。真正的操作在派生类里实现。
Sandbox/Hazel/src/Hazel/Layer.h
c++
#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;
};
}
Sandbox/Hazel/src/Hazel/Layer.cpp 现在是空实现
c++
#include "hzpch.h"
#include "Layer.h"
namespace Hazel {
Layer::Layer(const std::string& debugName)
: m_DebugName(debugName)
{}
Layer::~Layer()
{}
}
LayerStack
用Vector容器实现,封装了PushLayer、PushOverlay、PopLayer、PopOverlay的操作。
Overlay层放到队列末尾,对vector从后往前遍历,则Overlay层优先处理,相当于盖在最上面。
Sandbox/Hazel/src/Hazel/LayerStack.h
c++
#pragma once
#include "Hazel/Core.h"
#include "Layer.h"
#include <vector>
namespace Hazel {
class LayerStack
{
public:
LayerStack();
~LayerStack();
void PushLayer(Layer* layer);
void PushOverlay(Layer* overlay);
void PopLayer(Layer* layer);
void PopOverlay(Layer* overlay);
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;
};
}
LayerStack.cpp中对push pop的实现
Sandbox/Hazel/src/Hazel/LayerStack.cpp
c++
#include "LayerStack.h"
namespace Hazel{
LayerStack::LayerStack() {
m_LayerInsert = m_Layers.begin();
}
LayerStack::~LayerStack() {
for(Layer* layer : m_Layers){
delete layer;
}
}
void LayerStack::PushLayer(Layer *layer) {
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--;
}
}
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);
}
}
}
Application中添加LayerStack
头文件增加声明: Sandbox/Hazel/src/Hazel/Application.h
c++
#include "LayerStack.h"
namespace Hazel{
class Application {
public:
...
void PushLayer(Layer* layer);
void PushOverlay(Layer* layer);
private:
...
LayerStack m_LayerStack;
};
对应实现: Sandbox/Hazel/src/Hazel/Application.cpp
c++
void Application::PushLayer(Layer *layer) {
m_LayerStack.PushLayer(layer);
}
void Application::PushOverlay(Layer *layer) {
m_LayerStack.PushOverlay(layer);
}
OnEvent中,透传给LayerStack,只要有一个layer消费掉了event,该事件就不在往下传递了。
c++
void Application::OnEvent(Event &e) {
...
for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();) {
(*--it)->OnEvent(e);
if (e.Handled) {
break;
}
}
}
有些场景下,是允许多个layer都要接收event事件的,此时前面layer消费event时就不要将该event的Handled置为true。本节layer的实现中还没有对event做处理,默认都不消费event.
更新Hazel.h
在统一的对外头文件Hazel.h中增加Layer.h,保证业务方能拿到layer类
Sandbox/Hazel/src/Hazel.h
arduino
#include <stdio.h>
...
#include "Layer.h"
...
更新Hazel引擎的CMake文件
Sandbox/Hazel/CMakeLists.txt
css
project(hazel)
set(SRC_LIST
...
src/Hazel/Layer.h src/Hazel/Layer.cpp
src/Hazel/LayerStack.cpp src/Hazel/LayerStack.h)
CMakeLists.txt中将.h文件也包含到需要编译的源码中,否则,IDE中有"非项目文件"的提示,很烦~
SandBoxApp中增加Layer测试代码
增加Layer的实现类ExamleLayer,在OnUpdate和OnEvent中打印日志.
Sandbox/src/SandBoxApp.cpp
c++
class ExampleLayer : public Hazel::Layer {
public:
ExampleLayer() : Layer("Example") {
}
void OnUpdate() override{
HZ_INFO("ExampleLayer::Update");
}
void OnEvent(Hazel::Event& event) override {
HZ_TRACE("ExampleLayer::OnEvent {0}", event.ToString());
}
};
class Sandbox : public Hazel::Application {
public:
Sandbox(){
PushOverlay(new ExampleLayer());
}
实现效果、完整代码
OK,run起来,可以看到ExampleLayer的日志
这次代码分两次提交,参考 add layers first commit 、 add layers second commit