事件消息系统概述
事件消息系统对游戏引擎来说至关重要,因为它们允许系统之间相互通信。Open 3D Engine (O3DE) 使用几种类型的通信方法,它们被分为以下模块:
- 事件总线 (EBus): 所有模块都可使用的单一全局总线,用于调用请求和发送信息,分别称为 请求 总线和 通知 总线。
AZ::Interface
: 全局接口的 C++ 模板类,其他组件可从该接口调用请求,相当于 EBus 系统的请求总线。AZ::Event
: 一个 C++ 模板类,可以发布单值消息,其他组件可以订阅,相当于 EBus 系统的通知总线。
这三个模块的功能在某些方面存在重叠: AZ::Interface
提供了一个请求接口,AZ::Event
提供了一个发布-订阅接口,而 EBus 则同时提供了这两个接口。不过,这三个模块各有优缺点,因此更适合不同的应用场景。
下表比较了 EBus、AZ::Interface
和AZ::Event
的一些关键特性。您可以在 O3DE 的代码库中找到所有这三个事件消息传递模块。
EBus | AZ::Interface | AZ::Event | |
---|---|---|---|
通信模型 | EBus 是一种通用的事件消息传递系统,其设计重点在于抽象和解耦系统。它允许许多源组件提供请求或发布消息。它还允许许多监听器组件调用这些请求或订阅这些消息–所有这些组件之间无需相互了解。与AZ::Event 不同的是,即使 EBus 还不存在,监听器也可以订阅 EBus 专化。 | 一个 AZ::Interface<T> 模板特化定义了一个全局单子式请求接口,其他系统可以调用该接口。要访问请求接口,其他系统可调用 AZ::Interface<T>.Get() 。 | AZ::Event 事件被定义为组件的成员。因此,其他组件必须引用该组件才能订阅事件。要监听和处理事件,订阅组件必须实现一个处理程序。一个处理程序只能连接一个事件,但一个事件可以有多个处理程序。跨实体通信的一些模式包括:
|
生命周期 | EBus 是一个全局单例,在应用程序启动时初始化,并在应用程序终止时销毁。 | 一次只能有一个 AZ::Interface<T> 专用化实例。实例在应用程序启动时初始化,并在应用程序终止时销毁。 | 一个 AZ::Event<T> 特化可以有多个实例。每个实例都必须附加到一个组件。实例在组件创建时初始化,并在组件销毁时销毁。 |
脚本 | EBus 的通知总线(如 AZ::Event )和请求总线(不同于 AZ::Interface )都支持脚本绑定。 | AZ::Interface 不支持脚本绑定。您仍然可以使用 AZ::Interface ,但必须使用 EBus 将其暴露给脚本模块。 | AZ::Event 支持脚本绑定。 |
优势 | EBus 是 O3DE 最灵活、功能最强大的事件消息系统。任何系统都可以连接到它,只需提供系统的 EBus 名称即可与任何其他系统通信。EBus 通常用于事件流比事件源更重要的情况。 | 与 EBus 相比, AZ::Interface 提高了运行时性能,改善了可调试性,并与代码自动完成兼容。 | 与 EBus 相比,AZ::Event 提高了运行时性能,允许使用更简单的语法来实现,使用更少的文件,并删除了处理程序只关心事件子集的聚合接口。 |
示例 | EBus 示例类似于 AZ::Interface 和 AZ::Event 。在大多数情况下,使用这两种方法更可取,因为它们进行了优化。但是,您必须使用 EBus 为请求总线添加脚本绑定支持。 | 生成器-被生成物系统 生成器接口包含管理产卵器的函数。如果你想让另一个类生成某些东西,该类可以通过 AZ::Interface<Ispawnner> 提出请求。通过调用 AZ::Interface<ISpawner>.Get() ,其他组件可从生成器调用请求。 | 在网络层中,当远程过程调用(RPC)发送时,会发出 AZ::Event<NetworkEntityRpcMessage&> 信号。然后,连接的处理程序会监听该信号的变化并处理NetworkEntityRpcMessage 。 |