Async Message · C++ 收发与解绑
按取得系统、登记监听、读取载荷、发送与解绑的顺序,查阅原生 C++ 接口用法。
本页内容7 节
模块与头文件
在使用这些接口的模块中加入 AsyncMessageSystem 依赖;StarryGame 已具备。当前 UE 5.8 的 FInstancedStruct 位于 CoreUObject,不需要再按图中旧写法添加 StructUtils 模块。
PublicDependencyModuleNames.AddRange(new string[]
{
"AsyncMessageSystem"
});相关头文件为:
#include "AsyncGameplayMessageSystem.h"
#include "AsyncMessage.h"
#include "AsyncMessageBindingOptions.h"
#include "AsyncMessageHandle.h"
#include "AsyncMessageWorldSubsystem.h"
#include "GameplayTagContainer.h"
#include "StructUtils/InstancedStruct.h"下面按图示展示接口片段。AMyClass、FMyCustomStruct、PlayerName、Level 与 Message.Player.LevelUp 都沿用图中的示例名称,不是 Starry 已有类型、字段或消息;使用时替换为实际内容,不需要另建消息组件。项目已有消息与载荷见项目消息。
取得系统
以下两种方式任选一种,均取得当前 World 的 FAsyncGameplayMessageSystem。调用时需要有效的 World,取得后检查系统是否有效。
// 方式一:使用静态辅助函数。
TSharedPtr<FAsyncGameplayMessageSystem> GameplayMessageSystem =
UAsyncMessageWorldSubsystem::GetSharedMessageSystem<FAsyncGameplayMessageSystem>(GetWorld());// 方式二:先取得 World Subsystem。
auto* Subsystem = GetWorld()->GetSubsystem<UAsyncMessageWorldSubsystem>();
TSharedPtr<FAsyncGameplayMessageSystem> GameplayMessageSystem =
Subsystem->GetSharedMessageSystem<FAsyncGameplayMessageSystem>();设置消息与接收选项
保存系统和返回的 ListenerHandle,用于之后配对解绑。示例消息 Message.Player.LevelUp 需要先注册为 Gameplay Tag;用名称构造 Id 也会查找这个标签。
FAsyncMessageHandle ListenerHandle;
FAsyncMessageBindingOptions Options;
// 默认在游戏线程 TG_PostUpdateWork 接收。
// Options.SetTickGroup(TG_PostUpdateWork);
// 也可指定后台接收;该回调必须遵守相应线程约束。
// Options.SetNamedThreads(ENamedThreads::AnyBackgroundThread);
const FAsyncMessageId MessageId(TEXT("Message.Player.LevelUp"));
// 也可直接传入已有的 Gameplay Tag:FAsyncMessageId MessageId(GameplayTag);监听:成员函数
将 AMyClass 替换为当前类名,在该类声明 void OnMessageReceived(const FAsyncMessage& Message);,然后登记:
ListenerHandle = GameplayMessageSystem->BindListener(
MessageId,
MakeWeakObjectPtr(this),
&AMyClass::OnMessageReceived,
Options);
void AMyClass::OnMessageReceived(const FAsyncMessage& Message)
{
if (const FMyCustomStruct* Payload =
Message.GetPayloadView().GetPtr<const FMyCustomStruct>())
{
UE_LOG(LogTemp, Log, TEXT("%s Level up! New value: %d"),
*Payload->PlayerName, Payload->Level);
}
}绑定代码放在现有宿主的初始化逻辑中,成员函数实现放在对应 .cpp。图中接收指针与 GetPtr 的结构体名称不一致,这里统一为 FMyCustomStruct。GetPtr 在类型不匹配时返回空;载荷指针只在当前回调内使用。
监听:Lambda
也可以使用 Lambda,与上面的成员函数方式二选一。需要访问宿主时,用捕获的弱引用检查其是否仍有效。
ListenerHandle = GameplayMessageSystem->BindListener(
MessageId,
[WeakThis = MakeWeakObjectPtr(this)](const FAsyncMessage& Message)
{
if (!WeakThis.IsValid())
{
return;
}
if (const FMyCustomStruct* Payload =
Message.GetPayloadView().GetPtr<const FMyCustomStruct>())
{
// 读取实际载荷字段;通过 WeakThis 访问宿主。
}
},
Options);两种绑定形式均可在最后额外传入 BindingEndpoint;省略时使用默认 World 范围。路由与接收线程见标签、时机与生命周期。
发送
先填写自己的结构体,再用 FInstancedStruct::Make 复制为载荷并入队。发送与接收需要约定同一个消息 Id 和载荷类型。
FMyCustomStruct Data;
Data.PlayerName = TEXT("Jack");
Data.Level = 3;
FInstancedStruct Payload = FInstancedStruct::Make(Data);
GameplayMessageSystem->QueueMessageForBroadcast(MessageId, Payload);FInstancedStruct::Make 创建结构体副本。系统在处理对应接收队列时分发,不在发送这一行同步执行监听回调。
解绑
对登记时的系统调用 UnbindListener,再清空保存的句柄。只把句柄设为无效不会移除监听。
if (ListenerHandle.IsValid())
{
GameplayMessageSystem->UnbindListener(ListenerHandle);
ListenerHandle = FAsyncMessageHandle::Invalid;
}这段代码在功能关闭或宿主退出时执行,前提是登记时的系统仍有效。重复开始前先解绑已有监听,避免覆盖旧句柄;弱对象检查不能代替主动解绑。蓝图任务的保存和停止接线见蓝图收发与解绑。
实现入口:引擎插件的 AsyncMessageSystemBase.h、AsyncGameplayMessageSystem.h、AsyncMessageWorldSubsystem.h、AsyncMessage.h、AsyncMessageHandle.h 与 AsyncMessageBindingOptions.h。以上为原生接口片段,未向项目新增类型或运行 PIE。