游戏引擎从零开始(7)-图层

前言

游戏中的元素并不会平铺到一层上处理,而是分到不同的层级中。有点像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 commitadd layers second commit

相关推荐
rjszcb27 分钟前
一文说完c++全部基础知识,IO流(二)
c++
小字节,大梦想1 小时前
【C++】二叉搜索树
数据结构·c++
吾名招财1 小时前
yolov5-7.0模型DNN加载函数及参数详解(重要)
c++·人工智能·yolo·dnn
我是哈哈hh2 小时前
专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结
服务器·数据结构·c++·算法·机器学习·深度优先·剪枝
憧憬成为原神糕手2 小时前
c++_ 多态
开发语言·c++
郭二哈2 小时前
C++——模板进阶、继承
java·服务器·c++
挥剑决浮云 -2 小时前
Linux 之 安装软件、GCC编译器、Linux 操作系统基础
linux·服务器·c语言·c++·经验分享·笔记
丶Darling.2 小时前
LeetCode Hot100 | Day1 | 二叉树:二叉树的直径
数据结构·c++·学习·算法·leetcode·二叉树
labuladuo5202 小时前
Codeforces Round 977 (Div. 2) C2 Adjust The Presentation (Hard Version)(思维,set)
数据结构·c++·算法
小柯J桑_3 小时前
C++:STL简介
c++·stl