WAYNETS.ORG

Game and Program

GLFW与引擎事件系统的桥接机制

作者:

发表于

Context Polling System Post-Processing Renderer

GLFW与引擎事件系统的桥接机制是一个经典的软件架构设计问题,也是构建现代游戏引擎不可或缺的设计理念。可以将其理解为:底层平台依赖(GLFW)与上层引擎逻辑(你的事件系统) 之间的分层、解耦与通信机制

首先要明白,为什么需要桥接?

  • 不让引擎逻辑依赖 GLFW(未来可能换为SDL、Win32 API、Mac Cocoa…)
  • 所有事件统一走引擎层的事件系统(不管平台如何变化)
  • 能够在引擎逻辑中 自由响应事件(窗口关闭、输入、鼠标等)
  • 事件能穿越多个系统(如 UI、游戏逻辑、渲染等)

所以桥接机制是为了解耦系统之间的依赖,是 依赖反转原则(DIP) 的一种典范实现。

因此,整个桥接机制可以分为三个层面:

  1. 平台层(Platform)GLFW 回调 -> Event 对象
  2. 桥接层(Bridge)回调函数内:触发引擎事件分发机制
  3. 引擎层(Engine)Application::OnEvent + 事件系统

平台层:GLFW 层(外部依赖)

GLFW 本身通过回调机制,感知系统级事件(窗口关闭、大小变动、按键等):

glfwSetWindowCloseCallback(window, [](GLFWwindow* w) {
    WindowCloseEvent event;
    data.EventCallback(event);
});

它只是一个 “事件源”。但是它不负责怎么处理这个事件 —— 它只负责告诉你“事件发生了”。

桥接层:Window 封装 & EventCallback

在 Window 中注册一个回调接口(SetEventCallback),当 GLFW 有事件时,通过它把事件推送进引擎。

class Window {
public:
    using EventCallbackFn = std::function<void(Event&)>;
    void SetEventCallback(const EventCallbackFn& callback);
};

这一层实现了:

  • 隔离平台依赖(GLFW)
  • 提供一个标准接口 EventCallback,由引擎注册
  • 当 GLFW 回调触发时,构造事件对象,调用 EventCallback(event);

引擎层:Application + Event 系统

定义了事件基类 Event,又有具体类型如 WindowCloseEventKeyPressedEvent 等。

然后通过事件派发器 EventDispatcher,在 Application::OnEvent 中完成如下操作:

EventDispatcher dispatcher(e);
dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(OnWindowClosed));

这一层实现了:

  • 动态识别事件类型(运行时多态)
  • 动态调用对应处理逻辑(回调函数)
  • 支持事件向下分发(LayerStack)

以关闭窗口为例,完整的流程链为:

第一步:平台层GLFW 检测到用户关闭窗口

在类WindowsWindow中,使用glfwSetWindowSizeCallback()注册了一个 Lambda函数。

在该Lambda表达式中,将 GLFW 的原始信息(window, width, height)转为自定义事件WindowCloseEvent,然后使用 data.EventCallback 触发初始化窗口时设置的回调函数

glfwSetWindowCloseCallback(m_Window, [](GLFWwindow* window)
{
    WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
    WindowCloseEvent event;
    data.EventCallback(event);
});

这是由GLFW 库提供的一个窗口尺寸变化回调函数注册接口,属于 GLFW 的 API。它会注册一个回调函数,当GLFW 管理的窗口被用户调整大小时,就会调用该回调函数。

具体调用链为:修改窗口->GLFW窗口修改接口->接口中定义的Lambda->引擎层事件函数

它的官方函数签名:

GLFWwindow* window;
GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback);

第二步:桥接层 Window 类内部调用了事件回调

由于此前在类Application的构造函数中,绑定了窗口回调和类Application的函数OnEvent():

m_Window->SetEventCallback(BIND_EVENT_FN(Application::OnEvent));

所以,当调用data.EventCallback(event),就会直接进入:

Application::OnEvent(Event& e)

第三步:引擎层Application类进行事件分发

使用EventDispatcher来判断事件类型,判断到传入的事件类型是WindowCloseEvent,则调用绑定的函数OnWindowClosed()来实现具体引擎层逻辑。

void Application::OnEvent(Event& e)
{
	EventDispatcher dispatcher(e);
	dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(OnWindowClosed));

	HZ_CORE_TRACE("{}", e.ToString());

	for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();)
	{
		(*--it)->OnEvent(e);
		if (e.Handled) break;
	}
}

如果想实现引擎层的事件响应逻辑,则需要在Application类中定义事件函数。

对于窗口关闭事件,它的引擎层函数OnWindowClosed()代码逻辑如下:

bool Application::OnWindowClosed(WindowCloseEvent& e)
{
	m_Running = false;
	return true;
}

至此,引擎层事件逻辑处理完毕,窗口被关闭。

总结:

GLFW 与引擎之间的桥接机制,是通过“回调注入 + 事件对象转发”的方式实现了解耦和模块间通信,从而形成了一个灵活、可拓展、平台无关的事件响应体系。

它通过平台回调转发为引擎事件对象,实现了从平台层到引擎层的事件传递与解耦
它的最大优点是让引擎逻辑不依赖底层平台,具备更强的可移植性、可测试性与系统扩展能力

这么做的优势为:

优点描述
解耦GLFW 只产生事件,不处理;引擎只处理事件,不管来源
可扩展换 SDL、Win32 只需改 Window 封装层
可测试性强引擎可手动创建事件并测试逻辑
多系统协同同一个事件可以被 UI 层、游戏逻辑层等多个系统响应
层级传播机制LayerStack 控制传播顺序 + 可中断传播(Handled = true

还要许多地方会应用这个机制,比如:

桥接类型说明
GLFW → 引擎事件系统当前例子
OpenGL/DirectX → 渲染抽象层(Renderer API)隐藏具体图形 API
平台窗口 → 引擎 GUI 系统(如 ImGui)输入响应与 GUI 联动
物理库 → 自定义物理接口Bullet/PhysX 解耦封装
输入系统 → 游戏逻辑键盘/鼠标 → 角色行为

Leave a comment