跟着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"
}
-- 引用hazel
links{
"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"
}
-- 引用hazel
links{
"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"即可