Hazel游戏引擎(013)Layers游戏的层级

news2024/11/16 17:55:30

文中若有代码、术语等错误,欢迎指正

文章目录

  • 前言
  • 增加Layer后的主要类图
  • 项目相关
    • 代码
    • 项目流程
    • 效果
  • LayerStack类的错误

前言

  • 此节目的

    • 为完成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
        }
    }
    

项目流程

  • 文字

    1. Application定义了LayerStack对象m_LayerStack
    2. 在Sandbox构造函数中,执行PushLayer(new ExampleLayer());,将ExampleLayer放入m_LayerStack的vector中
    3. Application的OnEvent函数从后往前顺序遍历m_LayerStack的vector,得到ExampleLayer对象,并把事件e作为参数执行它的OnEvent函数,所以一直在控制台输出窗口事件
    4. 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;
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/634764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在VSCode下利用PlateFormIO开发Arduino的MicroROS遇到的一些问题

简介 我是按照鱼香ROS的教程【3.搭建PlateFormIO开发环境】进行的&#xff0c;但是在进行的过程中&#xff0c;遇到了一些问题&#xff0c;这里记录下来&#xff0c;供有同样问题的同学进行参考。其实只要你使用的板子的MCU是ESP32&#xff0c;都可以按照他这个教程进行操作。…

k8s实践之mysql集群搭建(十五)

先下载 k8s实践之mysql集群搭建资料 主从模式简介&#xff1a; 当master主服务器上的数据发生改变时&#xff0c;则将其改变写入二进制&#xff08;binlog&#xff09;事件日志文件中&#xff1b; slave从服务器会在一定时间间隔内对master主服务器上的二进制日志进行探测&am…

掌握Vue生命周期,让你的前端开发效率翻倍!

1 Vue实例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Vue实例</title><script src../vue.js></script> </head> <body><div id"root"><!-- v…

位图以及布隆过滤器

本文主要讲解哈希思想的实际应用&#xff0c;位图和布隆过滤器。 位图 讲解位图之前我们先来解答这样一道腾讯的面试题 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中。【腾讯】 很多人立马就想到了用…

Seata TCC 模式理论学习、生产级使用示例搭建及注意事项 | Spring Cloud55

一、前言 通过以下系列章节&#xff1a; docker-compose 实现Seata Server高可用部署 | Spring Cloud 51 Seata AT 模式理论学习、事务隔离及部分源码解析 | Spring Cloud 52 Spring Boot集成Seata利用AT模式分布式事务示例 | Spring Cloud 53 Seata XA 模式理论学习、使用…

STL——set容器、map容器

初识STL **set容器/multiset容器****set容器——构造和赋值****set容器——大小和交换****set容器——插入和删除****set容器的查找和统计****set和multiset的区别****set的相关操作源码&#xff1a;****multiset的相关操作源码** **pair使用——pair队组的创建****set容器——…

FreeRTOS-定时器详解

✅作者简介&#xff1a;嵌入式入坑者&#xff0c;与大家一起加油&#xff0c;希望文章能够帮助各位&#xff01;&#xff01;&#xff01;&#xff01; &#x1f4c3;个人主页&#xff1a;rivencode的个人主页 &#x1f525;系列专栏&#xff1a;玩转FreeRTOS &#x1f4ac;保持…

2023全国计算机二级考试时间(全年各阶段考试时间安排)

2023全国计算机二级考试时间(全年各阶段考试时间安排) 2023年全国计算机二级考试时间分别为&#xff1a;3月25日至27日(上半年3月)、9月23日至25日(下半年9月)。 其中3月和9月开考全部级别全部科目&#xff0c;5月和12月考试开考一、二级全部科目&#xff0c;各省级承办机构可根…

RabbitMQ集群部署之镜像模式

RabbitMQ集群的普通模式中&#xff0c;一旦创建队列的主机宕机&#xff0c;队列就会不可用。不具备高可用能力。如果要解决这个问题&#xff0c;必须使用官方提供的镜像集群方案。 官方文档地址&#xff1a;https://www.rabbitmq.com/ha.html 1.镜像模式的特征 默认情况下&a…

离心式冷水机组

离心式冷水机组是利用电作为动力源&#xff0c;氟利昂制冷剂在蒸发器内蒸发吸收载冷剂水的热量进行制冷&#xff0c;蒸发吸热后的氟利昂湿蒸汽被压缩机压缩成高温高压气体&#xff0c;经水冷冷凝器冷凝后变成液体&#xff0c;经膨胀阀节流进入蒸发器再循环。从而制取7℃-12℃冷…

解决jvm内存溢出的方法

上一篇问题讲了怎么实现jvm内存溢出,现在已经实现了,那怎么去解决它呢. java.lang.OutOfMemoryError: GC overhead limit exceeded 简单来说&#xff0c;java.lang.OutOfMemoryError: GC overhead limit exceeded发生的原因是&#xff0c;当前已经没有可用内存&#xff0c;经…

【IMX6ULL驱动开发学习】06.APP与驱动程序传输数据_自动创建设备节点(hello驱动)

一、APP与驱动之间传输数据 /*驱动从APP获取数据*/ unsigned long copy_from_user(void *to, const void *from, unsigned long n)/*驱动传输数据到APP*/ unsigned long copy_to_user(void *to, const void *from, unsigned long n)二、使用copy_to_user、copy_from_user在AP…

如何把视频声音转成文字?分享三个实用的方法!

在日常学习中&#xff0c;有些小伙伴可能想将视频课程中老师的讲解内容整理出来&#xff0c;但是逐字逐句地打字既低效又耗时。那么&#xff0c;如何将视频声音快速转换为文字呢&#xff1f;答案是使用记灵在线工具&#xff01;下面我将分享几种使用记灵在线工具快速将视频声音…

Raft is not great?

Raft相比于paxos不好的地方有下面这些地方 1.Term raft的逻辑时钟是通过term&#xff0c;和votefor来确定的&#xff0c;同时&#xff0c;raft的votefor只能是None < 有&#xff0c;有的话&#xff0c;就不可比&#xff0c;也就是一个偏序关系。这个不可比的特性会增加选举…

Javaweb学习路线(2)——Maven

一、概念 Maven 是 apache 旗下的一个开源项目&#xff0c;是一款用于管理和构建java项目的工具。 二、作用 依赖管理&#xff1a; 动态管理jar包&#xff0c;避免版本冲突。统一项目结构&#xff1a; 提供标准、统一的项目结构。项目构建&#xff1a; 标准跨平台的自动化项目…

深入探讨软件测试的质量度量指标

本文的目的是介绍项目中使用到主要质量指标&#xff0c;这些质量指标可以分为以下三类&#xff1a; 质量保证过程指标生产事故管理指标度量质量文化指标 质量保证过程指标 质量保证指标可以通过测试覆盖率来度量功能和非功能测试的覆盖率&#xff0c;同时也可以根据测试发现…

shell学习记录(函数)

Shell 函数 linux shell 可以用户定义函数&#xff0c;然后在shell脚本中可以随便调用。 shell中函数的定义格式如下&#xff1a; [ function ] funname [()] { action; [return int;] } 说明&#xff1a; 1、可以带function fun() 定义&#xff0c;也可以直接fun()…

对“java子类不能继承父类构造方法“的理解

目录 子类构造函数&#xff0c;什么时候需要写&#xff0c;什么时候可以不写&#xff1f;为什么要这么设计&#xff1f;子类已经继承父类&#xff0c;为什么就不能继承父类的构造器了&#xff1f;子类直接用父类的构造器不就行了&#xff1f; 子类构造函数&#xff0c;什么时候…

今年适合进入软件测试行业吗?

对于“自己适不适合转行做软件测试&#xff1f;”一直都是测试行业里的热门话题。随着近几 年IT互联网行业的快速发展&#xff0c;软件测试岗位也受到越来越多人的关注&#xff0c;因此有很多人都想要从事这一行业&#xff0c;这里主要分为两类人&#xff0c;一种是刚毕业的应届…

【Deno】极简入门

极简系列 极简入门 简介 Deno是一个 JavaScript/TypeScript 的运行时&#xff0c;默认使用安全环境执行代码&#xff0c;有着卓越的开发体验。 Deno建立在 V8、Rust 和 Tokio 的基础上。 优势 适合不喜欢复杂配置开箱即用的人群&#xff08;如本人小溪&#xff09; 安全性…