跟着cherno手搓游戏引擎【3】事件系统和预编译头文件
不多说了直接上代码,课程中的架构讲的比较宽泛,而且有些方法写完之后并未测试。所以先把代码写完。理解其原理,未来使用时候会再此完善此博客。
文件架构:

Event.h:核心基类
#pragma once
#include"../Core.h"
#include<string>
#include<functional>
namespace YOTO {// Hazel中的事件当前是阻塞的,这意味着当一个事件发生时,它立即被分派,必须立即处理。//将来,一个更好的策略可能是在事件总线中缓冲事件,并在更新阶段的“事件”部分处理它们。//事件类型enum class EventType{None = 0,WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,AppTick, AppUpdate, AppRender,KeyPressed, KeyReleased,MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled};//事件分类enum EventCategory {None = 0,EventCategoryApplication = BIT(0),EventCategoryInput = BIT(1),EventCategoryKeyboard = BIT(2),EventCategoryMouse = BIT(3),EventCategoryMouseButton = BIT(4)};//定义一些重复的重写的函数,只需要传入固定参数,就能少些一大部分代码
#define EVENT_CLASS_TYPE(type) static EventType GetStaticType(){return EventType::##type; }\virtual EventType GetEventType() const override {return GetStaticType();}\virtual const char* GetName() const override {return #type;}
#define EVENT_CLASS_CATEGORY(category) virtual int GetCategoryFlags() const override {return category; }/// <summary>/// 事件基类,事件需要继承此类/// </summary>class Event{public://获得该事件的具体类型virtual EventType GetEventType() const = 0;//获得事件的名称virtual const char* GetName() const = 0;//获得该事件的大类型virtual int GetCategoryFlags() const = 0;//toString方法,返回事件名virtual std::string ToString() const { return GetName(); }//判断该事件是否是category类inline bool IsInCategory(EventCategory category) {return GetCategoryFlags() & category;}protected://事件是否被处理了bool m_Handled = false;};/// <summary>/// 基于事件类型调度事件的方法(暂时没有测试,不知道怎么用)/// </summary>class EventDispatcher{template <typename T>using EventFn = std::function<bool(T&)>;public:EventDispatcher(Event& event):m_Event(event) {}template <typename T>bool Dispatch(EventFn<T> func) {if (m_Event.GetEventType() == T::GetStaticType()) {m_Event.m_Handled = func(*(T*)&m_Event);return true;}return false;}private:Event& m_Event;};//暂无测试,方便输出inline std::ostream& operator<<(std::ostream& os, const Event& e){return os << e.ToString();}}
ApplicationEvent.h:处理appwindow的各种事件
#pragma once
#include"Event.h"
#include<sstream>
namespace YOTO {class YOTO_API WindowResizeEvent :public Event {public:WindowResizeEvent(unsigned int width, unsigned int height) :m_Width(width), m_Height(height) {}inline unsigned int GetWidth() const { return m_Width; }inline unsigned int GetHeight() const { return m_Height; }std::string ToString() const override {std::stringstream ss;ss << "窗口大小改变事件:" << m_Width << "," << m_Height;return ss.str();}EVENT_CLASS_TYPE(WindowResize)EVENT_CLASS_CATEGORY(EventCategoryApplication)private:unsigned int m_Width, m_Height;};class YOTO_API WindowCloseEvent :public Event{public:WindowCloseEvent(){}EVENT_CLASS_TYPE(WindowClose)EVENT_CLASS_CATEGORY(EventCategoryApplication)};class YOTO_API AppTickEvent :public Event {public:AppTickEvent(){}EVENT_CLASS_TYPE(AppTick)EVENT_CLASS_CATEGORY(EventCategoryApplication)};class YOTO_API AppUpdateEvent :public Event {public:AppUpdateEvent(){}EVENT_CLASS_TYPE(AppUpdate)EVENT_CLASS_CATEGORY(EventCategoryApplication)};class YOTO_API AppRenderEvent :public Event {public:AppRenderEvent() {}EVENT_CLASS_TYPE(AppRender)EVENT_CLASS_CATEGORY(EventCategoryApplication)};
}
KeyEvent.h:处理键盘事件
#pragma once
#include"Event.h"
#include<sstream>
namespace YOTO {class YOTO_API KeyEvent:public Event{public:inline int GetKeyCode() const { return m_KeyCode; }EVENT_CLASS_CATEGORY(EventCategoryKeyboard | EventCategoryInput)protected:KeyEvent(int keycode):m_KeyCode(keycode){}int m_KeyCode;};class YOTO_API KeyPressedEvent :public KeyEvent{public:KeyPressedEvent(int keycode, int repeatCount):KeyEvent(keycode),m_RepeatCount(repeatCount){}inline int GetRepeatCount() const { return m_RepeatCount; }std::string ToString() const override{std::stringstream ss;ss << "键盘按下事件:" << m_KeyCode << "(" << m_RepeatCount << "重复)";return ss.str();}//static EventType GetStaticType() { return EventType::KeyPressed; }//virtual EventType GetEventType()const override { return GetStaticType(); }//virtual const char* GetName()const override { return "KeyPressed"; }EVENT_CLASS_TYPE(KeyPressed)private:int m_RepeatCount;};class KeyReleasedEvent:public KeyEvent{public:KeyReleasedEvent(int keycode):KeyEvent(keycode){}std::string ToString()const override {std::stringstream ss;ss << "键盘释放事件:" << m_KeyCode;return ss.str(); }EVENT_CLASS_TYPE(KeyReleased)};}
MouseEvent.h:处理鼠标事件
#pragma once
#include"Event.h"
#include<sstream>
namespace YOTO {class YOTO_API MouseMovedEvent :public Event{public:MouseMovedEvent(float x, float y):m_MouseX(x), m_MouseY(y) {}inline float GetX() const { return m_MouseX; }inline float GetY() const { return m_MouseY; }std::string ToString() const override {std::stringstream ss;ss << "鼠标移动事件:" << m_MouseX << "," << m_MouseY;return ss.str();}EVENT_CLASS_TYPE(MouseMoved)EVENT_CLASS_CATEGORY(EventCategoryMouse |EventCategoryInput )private:float m_MouseX, m_MouseY;};class YOTO_API MouseScrolledEvent :public Event{public:MouseScrolledEvent(float x, float y):m_XOffset(x), m_YOffset(y) {}inline float GetXOffset() const { return m_XOffset; }inline float GetYOffset() const { return m_YOffset; }std::string ToString() const override {std::stringstream ss;ss << "鼠标滚动事件:" << GetXOffset() << "," << GetYOffset();return ss.str();}EVENT_CLASS_TYPE(MouseScrolled)EVENT_CLASS_CATEGORY(EventCategoryMouse | EventCategoryInput)private:float m_XOffset, m_YOffset;};class YOTO_API MouseButtonEvent :public Event{public:inline int GetMouseButton() const { return m_Button; }EVENT_CLASS_CATEGORY(EventCategoryMouse | EventCategoryInput)protected:MouseButtonEvent(int button):m_Button(button){}int m_Button;};class YOTO_API MouseButtonPressedEvent :public MouseButtonEvent{public:MouseButtonPressedEvent(int button) :MouseButtonEvent(button) {}std::string ToString() const override {std::stringstream ss;ss << "鼠标按下事件:" << m_Button;return ss.str();}EVENT_CLASS_TYPE(MouseButtonPressed)};class YOTO_API MouseButtonReleasedEvent :public MouseButtonEvent{public:MouseButtonReleasedEvent(int button):MouseButtonEvent(button) {}std::string ToString() const override {std::stringstream ss;ss << "鼠标松开事件:" << m_Button;return ss.str();}EVENT_CLASS_TYPE(MouseButtonReleased)};
}
说白了就是写了个基类,然后写了实现类。
修改:
Core.h
#pragma once
//用于dll的宏
#ifdef YT_PLATFORM_WINDOWS
#ifdef YT_BUILD_DLL
#define YOTO_API __declspec(dllexport)
#else
#define YOTO_API __declspec(dllimport) #endif // DEBUG
#else
#error YOTO_ONLY_SUPPORT_WINDOWS
#endif // YOTO_PLATFORM_WINDOWS
#define BIT(x)(1<<x)
记得加Event.h的头文件
premake5.lua
workspace "YOTOEngine" -- sln文件名architecture "x64" configurations{"Debug","Release","Dist"}
-- https://github.com/premake/premake-core/wiki/Tokens#value-tokens
-- 组成输出目录:Debug-windows-x86_64
outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"project "YOTOEngine" --Hazel项目location "YOTOEngine"--在sln所属文件夹下的Hazel文件夹kind "SharedLib"--dll动态库language "C++"targetdir ("bin/" .. outputdir .. "/%{prj.name}") -- 输出目录objdir ("bin-int/" .. outputdir .. "/%{prj.name}")-- 中间目录-- 包含的所有h和cpp文件files{"%{prj.name}/src/**.h","%{prj.name}/src/**.cpp"}-- 包含目录includedirs{"%{prj.name}/src","%{prj.name}/vendor/spdlog-1.x/include"}-- 如果是window系统filter "system:windows"cppdialect "C++17"-- On:代码生成的运行库选项是MTD,静态链接MSVCRT.lib库;-- Off:代码生成的运行库选项是MDD,动态链接MSVCRT.dll库;打包后的exe放到另一台电脑上若无这个dll会报错staticruntime "On" systemversion "latest" -- windowSDK版本-- 预处理器定义defines{"YT_PLATFORM_WINDOWS","YT_BUILD_DLL"}-- 编译好后移动Hazel.dll文件到Sandbox文件夹下postbuildcommands{("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")}-- 不同配置下的预定义不同filter "configurations:Debug"defines "YT_DEBUG"symbols "On"filter "configurations:Release"defines "YT_RELEASE"optimize "On"filter "configurations:Dist"defines "YT_DIST"optimize "On"project "Sandbox"location "Sandbox"kind "ConsoleApp"language "C++"targetdir ("bin/" .. outputdir .. "/%{prj.name}")objdir ("bin-int/" .. outputdir .. "/%{prj.name}")files{"%{prj.name}/src/**.h","%{prj.name}/src/**.cpp"}-- 同样包含spdlog头文件includedirs{"YOTOEngine/vendor/spdlog-1.x/include","YOTOEngine/src"}-- 引用hazellinks{"YOTOEngine"}filter "system:windows"cppdialect "C++17"staticruntime "On"systemversion "latest"defines{"YT_PLATFORM_WINDOWS"}filter "configurations:Debug"defines "YT_DEBUG"symbols "On"filter "configurations:Release"defines "YT_RELEASE"optimize "On"filter "configurations:Dist"defines "YT_DIST"optimize "On"
测试:
Application.cpp
#include "Application.h"
#include"Event/ApplicationEvent.h"
#include"Log.h"
namespace YOTO {Application::Application() {}Application::~Application() {}void Application::Run() {WindowResizeEvent e(1280, 720);if (e.IsInCategory(EventCategoryApplication)) {YT_CORE_TRACE(e);}if (e.IsInCategory(EventCategoryInput)) {YT_CORE_ERROR(e);}while (true){}}
}

预编译头文件:
ytpch.h
#pragma once
#include<iostream>
#include<memory>
#include<utility>
#include<algorithm>
#include<functional>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<sstream>
#ifdef YT_PLATFORM_WINDOWS
#include<Windows.h>
#endif // YT_PLATFORM_WINDOWS
ytpch.cpp
#include "ytpch.h"
premake5.lua:
workspace "YOTOEngine" -- sln文件名architecture "x64" configurations{"Debug","Release","Dist"}
-- https://github.com/premake/premake-core/wiki/Tokens#value-tokens
-- 组成输出目录:Debug-windows-x86_64
outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"project "YOTOEngine" --Hazel项目location "YOTOEngine"--在sln所属文件夹下的Hazel文件夹kind "SharedLib"--dll动态库language "C++"targetdir ("bin/" .. outputdir .. "/%{prj.name}") -- 输出目录objdir ("bin-int/" .. outputdir .. "/%{prj.name}")-- 中间目录pchheader "ytpch.h"pchsource "YOTOEngine/src/ytpch.cpp"-- 包含的所有h和cpp文件files{"%{prj.name}/src/**.h","%{prj.name}/src/**.cpp"}-- 包含目录includedirs{"%{prj.name}/src","%{prj.name}/vendor/spdlog-1.x/include"}-- 如果是window系统filter "system:windows"cppdialect "C++17"-- On:代码生成的运行库选项是MTD,静态链接MSVCRT.lib库;-- Off:代码生成的运行库选项是MDD,动态链接MSVCRT.dll库;打包后的exe放到另一台电脑上若无这个dll会报错staticruntime "On" systemversion "latest" -- windowSDK版本-- 预处理器定义defines{"YT_PLATFORM_WINDOWS","YT_BUILD_DLL"}-- 编译好后移动Hazel.dll文件到Sandbox文件夹下postbuildcommands{("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")}-- 不同配置下的预定义不同filter "configurations:Debug"defines "YT_DEBUG"symbols "On"filter "configurations:Release"defines "YT_RELEASE"optimize "On"filter "configurations:Dist"defines "YT_DIST"optimize "On"project "Sandbox"location "Sandbox"kind "ConsoleApp"language "C++"targetdir ("bin/" .. outputdir .. "/%{prj.name}")objdir ("bin-int/" .. outputdir .. "/%{prj.name}")files{"%{prj.name}/src/**.h","%{prj.name}/src/**.cpp"}-- 同样包含spdlog头文件includedirs{"YOTOEngine/vendor/spdlog-1.x/include","YOTOEngine/src"}-- 引用hazellinks{"YOTOEngine"}filter "system:windows"cppdialect "C++17"staticruntime "On"systemversion "latest"defines{"YT_PLATFORM_WINDOWS"}filter "configurations:Debug"defines "YT_DEBUG"symbols "On"filter "configurations:Release"defines "YT_RELEASE"optimize "On"filter "configurations:Dist"defines "YT_DIST"optimize "On"
把所有使用库文件的地方都换成#include"ytpch.h"即可
相关文章:
跟着cherno手搓游戏引擎【3】事件系统和预编译头文件
不多说了直接上代码,课程中的架构讲的比较宽泛,而且有些方法写完之后并未测试。所以先把代码写完。理解其原理,未来使用时候会再此完善此博客。 文件架构: Event.h:核心基类 #pragma once #include"../Core.h" #inclu…...
排序算法之快速排序
快速排序是一种高效的排序算法,它的基本思想是采用分治策略,将一个无序数组分割成两个子数组,分别对子数组进行排序,然后将两个排序好的子数组合并成一个有序数组。快速排序的性能优于归并排序,尤其在处理大规模数据时…...
Docker 从入门到实践:Docker介绍
前言 在当今的软件开发和部署领域,Docker已经成为了一个不可或缺的工具。Docker以其轻量级、可移植性和标准化等特点,使得应用程序的部署和管理变得前所未有的简单。无论您是一名开发者、系统管理员,还是IT架构师,理解并掌握Dock…...
用IDEA创建/同步到gitee(码云)远程仓库(保姆级详细)
前言: 笔者最近在学习java,最开始在用很笨的方法:先克隆远程仓库到本地,再把自己练习的代码从本地仓库上传到远程仓库,很是繁琐。后发现可以IDEA只需要做些操作可以直接把代码上传到远程仓库,也在网上搜了些…...
【Linux】进程控制深度了解
> 作者简介:დ旧言~,目前大二,现在学习Java,c,c,Python等 > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:熟练掌握Linux下的进程控制 > 毒鸡汤ÿ…...
kbdnso.dll文件缺失,软件或游戏报错的快速修复方法
很多小伙伴遇到电脑报错,提示“kbdnso.dll文件缺失,程序无法启动执行”时,不知道应该怎样处理,还以为是程序出现了问题,想卸载重装。 首先,先要了解“kbdnso.dll文件”是什么? kbdnso.dll是Win…...
Spring技术内幕笔记之IOC的实现
IOC容器的实现 依赖反转: 依赖对象的获得被反转了,于是依赖反转更名为:依赖注入。许多应用都是由两个或者多个类通过彼此的合作来实现业务逻辑的,这使得每个对象都需要与其合作的对象的引用,如果这个获取过程需要自身…...
kotlin foreach 循环
java中的foreach循环也使用于kotlin ,先回顾下java里面的foreach循环 java foreach循环格式 for(元素类型t 元素变量x : 遍历对象obj){引用了x的语句;} 例如: int[] intary {1,2,3,4};for (int a: intary) {Log.d("intary", String.value…...
分享相关知识
直接使用海龟图进行创作移动动态的游戏 这段代码是一个简单的turtle模块实现的小游戏,主要功能包括: 窗口和无人机初始化: 创建了一个turtle窗口,设置了窗口的背景颜色和标题。创建了一个表示无人机的turtle,形状为正…...
RabbitMQ(七)ACK 消息确认机制
目录 一、简介1.1 背景1.2 定义1.3 如何查看确认/未确认的消息数? 二、消息确认机制的分类2.1 消息发送确认1)ConfirmCallback方法2)ReturnCallback方法3)代码实现方式一:统一配置a.配置类a.生产者c.消费者d.测试结果 …...
ubuntu 编译内核报错
Ubuntu 编译 Linux 内核经常会遇到如下错误: 如果报错 canonical-certs.pem: 如下: make[1]: *** No rule to make target ‘debian/canonical-certs.pem’, needed by ‘certs/x509_certificate_list’. Stop. make: *** [Makefile:1868: …...
Python之自然语言处理库snowNLP
一、介绍 SnowNLP是一个python写的类库,可以方便的处理中文文本内容,是受到了TextBlob的启发而写的,由于现在大部分的自然语言处理库基本都是针对英文的,于是写了一个方便处理中文的类库,并且和TextBlob不同的是&…...
C# 语法进阶 委托
1.委托 委托是一个引用类型,其实他是一个类,保存方法的指针 (指针:保存一个变量的地址)他指向一个方法,当我们调用委托的时候这个方法就立即被执行 关键字:delegate 运行结果: 思…...
开源可观测性平台Signoz(四)【链路监控及数据库中间件监控篇】
转载说明:如果您喜欢这篇文章并打算转载它,请私信作者取得授权。感谢您喜爱本文,请文明转载,谢谢。 前文链接: 开源可观测性平台Signoz系列(一)【开篇】 开源可观测性平台Signoz&…...
【嵌入式开发 Linux 常用命令系列 4.2 -- git .gitignore 使用详细介绍】
文章目录 .gitignore 使用详细介绍.gitignore 文件的位置.gitignore 语法规则使用示例注意事项 .gitignore 使用详细介绍 .gitignore 文件是一个特殊的文本文件,它告诉 Git 哪些文件或目录是可以被忽略的,即不应该被纳入版本控制系统。这主要用于避免一…...
【熔断限流组件resilience4j和hystrix】
文章目录 🔊博主介绍🥤本文内容起因resilience4j落地实现pom.xml依赖application.yml配置接口使用 hystrix 落地实现pom.xml依赖启动类上添加注解接口上使用 📢文章总结📥博主目标 🔊博主介绍 🌟我是廖志伟…...
微服务雪崩问题及解决方案
雪崩问题 微服务中,服务间调用关系错综复杂,一个微服务往往依赖于多个其它微服务。 微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况。 如果服务提供者A发生了故障,当前的应用的部分业务…...
008、所有权
所有权可以说是Rust中最为独特的一个功能了。正是所有权概念和相关工具的引入,Rust才能够在没有垃圾回收机制的前提下保障内存安全。 因此,正确地了解所有权概念及其在Rust中的实现方式,对于所有Rust开发者来讲都是十分重要的。在本文中&…...
千里马2023年终总结-android framework实战
背景: hi粉丝朋友们: 2023年马上就过去了,很多学员朋友也都希望马哥这边写个年终总结,因为这几个月时间都忙于新课程halsystracesurfaceflinger专题的开发,差点都忘记了这个事情了,今天特别花时间来写个bl…...
vue3中pinia的使用及持久化(详细解释)
解释一下pinia: Pinia是一个基于Vue3的状态管理库,它提供了类似Vuex的功能,但是更加轻量化和简单易用。Pinia的核心思想是将所有状态存储在单个store中,并且将store的行为和数据暴露为可响应的API,从而实现数据&#…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...
鸿蒙HarmonyOS 5军旗小游戏实现指南
1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发,采用DevEco Studio实现,包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...
