1.Events文化夹中的事件设置
这里的事件类基本上只完成两件事,根据输入进来的事件参数,利用传入参数把这个事件发生信息推入到输出流中,以及提供指示事件类型和事件类别的函数由EVENT_CLASS_TYPE和EVENT_CLASS_CATEGORY完成
class HAZEL_API MouseScrolledEvent : public Event
{
public:
MouseScrolledEvent(float xOffset, float yOffset)
: m_XOffset(xOffset), m_YOffset(yOffset) {}
inline float GetXOffset() const { return m_XOffset; }
inline float GetYOffset() const { return m_YOffset; }
std::string ToString() const override
{
std::stringstream ss;
ss << "MouseScrolledEvent: " << GetXOffset() << ", " << GetYOffset();
return ss.str();
}
EVENT_CLASS_TYPE(MouseScrolled)
EVENT_CLASS_CATEGORY(EventCategoryMouse | EventCategoryInput)
private:
float m_XOffset, m_YOffset;
};
EVENT_CLASS_TYPE和EVENT_CLASS_CATEGORY宏定义具体情况如下
#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; }
2.WindowsWindow类的回调事件函数
//WindowsWindow.cpp下
glfwSetKeyCallback(m_Window, [](GLFWwindow* window, int key, int scancode, int action, int mods)
{
WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
switch (action)
{
case GLFW_PRESS:
{
KeyPressedEvent event(key, 0);
data.EventCallback(event);
break;
}
case GLFW_RELEASE:
{
KeyReleasedEvent event(key);
data.EventCallback(event);
break;
}
case GLFW_REPEAT:
{
KeyPressedEvent event(key, 1);
data.EventCallback(event);
break;
}
}
});
---------------------------------------------
//在WindowsWinow.h中的WindowsWinow类有
struct WindowData
{
std::string Title;
unsigned int Width, Height;
bool VSync;
EventCallbackFn EventCallback;
//window.h里面有,using EventCallbackFn = std::function<void(Event&)>;
//其实就相当于void EventCallback(Event& 参数)的一个函数声明
};
WindowData m_Data;
这个段代码说明的是WindowsWinow存储数据的实际结构
代码解释
注:glfwSetKeyCallback(m_Window, [](GLFWwindow* window, int key, int scancode, int action, int mods)中的[](GLFWwindow* window, int key, int scancode, int action, int mods)参数是GLFW库自己根据自己的设置依据键盘输入自己推入这个参数的,这个参数不是自己传入的
WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
-
glfwGetWindowUserPointer(window)
- 这是一个库函数,用于从窗口对象获取用户指针。当创建窗口对象并提供用户数据时,这个用户数据就会被存储起来,以供以后获取。这个函数需要一个窗口对象的指针作为参数,然后返回存储的用户数据。 -
(WindowData*)
- 这是一个强制类型转换,它将glfwGetWindowUserPointer(window)
返回的无类型指针(void*)转换为WindowData
类的指针。这个转换假定存储的用户数据确实是WindowData
类的实例。 -
*(WindowData*)
- 解引用指针,获取存储在指针地址的数据。 -
WindowData& data =
- 这是一个引用变量的定义。引用变量是对实际可变数据的别名。这行代码将data
定义为引用变量,它引用的是被解引用的WindowData
实例。这意味着data
现在可以像平常使用对象那样使用,但在实质上它仍然是引用的窗口的用户数据。
逻辑解释
KeyPressedEvent这就是我们之前说的Events文件夹的事件类,作用见上一标题。
data.EventCallback(event);有定义using EventCallbackFn = std::function<void(Event&)>;实际上这个代码储存就是一个void EventCallback(Event& 参数)的一个函数声明,注意目前是没有被实现的。它的实现在WindowsWindow类的一个回调函数,如下图。基本就是通过SetEventCallback来实现的
WindowsWindow.cpp下
inline void SetEventCallback(const EventCallbackFn& callback) override
{
m_Data.EventCallback = callback;
}
上面那个代码片段已经说过了, m_Data是实际上存储数据的一个结构体
当我们运行WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);的时候,data作为WindowData的实例等于类中引用m_Data
WindowData& data=m_Data,相当于这样。
整个事件驱动的逻辑
1.如果思考事件驱动系统的逻辑,那么先是从WindowsWindow类的回调事件函数开始例如函数glfwSetKeyCallback,它会根据操作系统的输入流自己得到对应的事件信息,然后得到的信息化成参数输入到回调函数glfwSetKeyCallback,然后调用该回调函数。
【注:当我们使用GLFW创建窗口和处理输入时,操作系统会根据输入流的到的事件信息会传递给glfw库,glfw库会缓存这些事件,等待你在适当的时间处理。而处理这个函数就是glfwPollEvents()我们把它放在WindowsWindow::OnUpdate()中,在整个程序中的run函数主循环函数中每次调用更新函数m_Window->OnUpdate();】
2.接下来glfwSetKeyCallback首先会把得到的事件信息给对应的事件类,例如上面图片提到的KeyPressedEvent event(key, 0);这一行代码。然后该事件类会把信息推入输出流中打印到控制台,这里对应的是Events基类和子类对应的函数。并且根据得到的事件信息中事件类型定义一个会使用该事件类型作为参数的函数,这里对应的是data.EventCallback(event);实际上等于void EventCallback(Event& event)。
3.然后我们根据SetEventCallback去实现上面所说的定义的那个函数,在Application.cpp中实例中每次触发回调函数都会调用Application中的OnEvent函数,而Application中的OnEvent函数又会调用所有层栈的OnEvent函数。换而言之事件发生会触发回调函数,将对应消息打印并且调用对应的层的OnEvent函数
#define BIND_EVENT_FN(x) std::bind(&Application::x, this, std::placeholders::_1)
Application::Application()
{
m_Window = std::unique_ptr<Window>(Window::Create());
m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));//就是它,就是这个函数
m_ImGuiLayer = new ImGuiLayer();
PushOverlay(m_ImGuiLayer);
}
//----------------------------------------
//Application::OnEvent的实现
void Application::OnEvent(Event& e)
{
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;
}
//--------------------------
Application::OnEvent时机上就是data.EventCallback(event);函数定义的实现了。
总结:WindowsWindow类的事件模式,就是依靠glfw库从输入流中得到的信息传递给Events类,然后Events类把得到的消息推入到输出流中。第二件是就是放在一个函数声明,这函数声明是没有实现的,要根据SetEventCallback函数去实现它