Async Message · 标签、时机与生命周期

理解消息的标签层级、World 和 Endpoint 路由、游戏线程派发、载荷快照与 C++ 监听清理。

本页内容5 节

标签决定监听哪些事件

FAsyncMessageId 内部使用 Gameplay Tag。消息标签必须已经注册,名称中的点号形成父子层级:监听父标签会收到其后代的消息,监听子标签不会收到父标签或其他兄弟标签的消息。

例如 Starry.QuickBar.Message 可以观察 Starry.QuickBar.Message.SlotsChanged 和 Starry.QuickBar.Message.ActiveIndexChanged。两个子消息使用不同载荷,因此父级监听必须先区分真实来源,再按类型解包。新功能使用自己的语义标签,不借用已有伤害、输入或任务标签发送无关数据。

接收消息字段含义
MessageId当前被匹配的监听层级,可能是父标签
MessageSourceId发送时的原始消息标签,不随父级匹配而改变
Payload入队时复制的结构体,蓝图用 Get Instanced Struct Value 检查并读取
QueueTime入队时的时间信息,不是业务持续时间,也不是接收时刻

需要精确匹配时,直接监听叶标签,或将 MessageSourceId 与目标 Id 作相等比较。这里没有与旧消息插件相同的“Exact/Partial Match”选择参数,不套用其他插件的节点签名。

World 和 Endpoint 一起确定范围

World Subsystem 为每个 World 创建一套 FAsyncGameplayMessageSystem,默认 Endpoint 是这套系统的公共收发范围。项目当前发送均未传专用 Endpoint,蓝图监听项目消息时保持 Desired Endpoint 为空。

Endpoint 进一步隔离同一个 World 内的收发组。若使用专用 Endpoint,发送方与监听方必须拿到同一个有效实例;两个 Actor 各自持有一个同类组件,并不代表它们共享 Endpoint。默认监听不会自动旁听专用 Endpoint,Endpoint 也不是网络地址或 Actor 复制通道。

引擎提供 UAsyncMessageBindingComponent 与 IAsyncMessageBindingEndpointInterface 作为原生接入方式,组件代码在初始化时创建 Endpoint、反初始化时释放。Starry 当前没有查到专用 Endpoint 接入,本轮蓝图示例使用已接入的默认范围。需要对象级隔离时,先确认组件实际走到初始化且返回有效 Endpoint;当前实现遇到空或失效 Endpoint 会退回默认范围,不能把未初始化的组件当成可靠隔离。

队列什么时候派发

先登记监听,入队后在接收阶段回调时序
正在整理图表…
先登记监听,入队后在接收阶段回调
查看图表源码
sequenceDiagram
  participant Owner as 使用方
  participant System as World 消息系统
  participant Queue as 接收队列
  participant Listener as 监听者
  Owner->>System: BindListener / 启动蓝图监听
  Owner->>System: QueueMessageForBroadcast
  System->>Queue: 复制载荷并入队
  System-->>Owner: 返回是否加入队列
  Note over Owner,Listener: 此返回不表示接收逻辑已执行
  Queue->>System: 对应 Tick Group 的处理机会
  System->>Listener: 按 Endpoint 和标签派发
  Listener->>Listener: 核对来源、解包、筛选目标
  Owner->>System: 请求解绑
  Note over System,Listener: 后续预处理阶段移除监听,不撤回已开始的回调

蓝图的 Tick Group 是高级引脚,默认 TG_PostUpdateWork,回调在游戏线程运行。调早或调晚 Tick Group 应有实际时序需求;同一组内消息系统与其他 Actor Tick 的先后不由节点摆放顺序保证。

Queue Async Message for Broadcast 的 Success 表示消息被加入了相关接收队列。没有对应标签或父级监听时通常返回 false;World 不存在、Id 无效或系统关闭时也会失败。它不是目标对象已收到、载荷类型正确、处理成功或网络投递成功的回执,专用 Endpoint 是否匹配还要独立核对。

正常玩法代码由 World 的消息系统自动驱动派发,不手动调用 ProcessMessageQueueForBinding。项目专项测试为了确定性验证才直接处理队列。

载荷复制到哪一层

入队创建 FAsyncMessage,其 PayloadCopy 是结构体副本。发送后修改原结构体的数值,不会改掉已经排队的数值;消息系统也会为队列载荷中的反射对象引用提供 GC 引用收集。

副本中的 UObject 字段仍指向原对象,不会深复制 Actor、组件或物品实例,也不冻结对象之后的状态。延迟接收时要检查对象有效性;需要记录事件发生时的数据,就把必要的值直接放进载荷,而不是到回调里再读取已经变化的对象。

C++ 回调的 GetPayloadView() 和结构体指针属于当前消息,不跨回调保存指针或引用。确需后续使用时,复制需要的数据,并由自己的生命周期管理对象引用。

插件支持多生产者入队,以及按 C++ 绑定选项选择命名线程或 UE::Tasks 优先级。结构体被复制不代表其中的 UObject 可以在任意线程访问;UI、Actor 与大部分 UObject 操作仍应在游戏线程完成。

C++ 监听由使用方拥有

组件或界面负责保存监听句柄,并在功能退出、EndPlay 或自身明确的停止入口解绑。消息系统的弱对象绑定避免强行延长监听对象寿命,但“对象仍活着、功能已经关闭”仍需主动停止。

C++ 收发与解绑提供组件声明和完整的监听、解包、发送、停止函数:登记时保存原系统的弱引用与句柄,退出时向原系统解绑,再清空记录;回调检查载荷类型与 Target。实际用法集中在消息收发分区,这里只说明所有权与并发边界。

解绑请求在队列预处理时移除监听,不是跨线程等待屏障,不会撤回已在运行的回调。通常的游戏线程监听在使用方退出时配对停止即可;显式选择后台线程的使用方还要按自己的任务模型协调回调和销毁,不能把保存句柄当作已经解决并发。

蓝图的任务保存与停止见蓝图收发与解绑,已有标签和网络边界见项目消息与排查。

实现入口:引擎插件内的 AsyncMessageSystemBase、AsyncMessageStore、AsyncGameplayMessageSystem、AsyncMessageWorldSubsystem、AsyncMessage 与 AsyncAction_ListenForMessage。

Starry手册导览从模块进入子模块与具体功能,查阅功能用途、配置步骤、技术架构和扩展方法。工程分层与系统协作了解 Framework、Gameplay 与通用插件的依赖方向,以及配置装配和运行状态的边界。模块总览与接入从行为系统、执行系统与机会调度了解 AstraAI,再为角色配置第一套组合决策。组合与覆盖正确区分优先级、行为分数与 Stack 顺序,按完整路径组织可撤销的配置。评分与抢占沿着条件、优先级、分数、持续有效性与冷却,解释一个行为为何开始或退出。Logic 与生命周期把动作接入 Start、Cancel、Cleanup,正确处理共享模板、蓝图实例与过期异步回调。BT、StateTree 与 HTN复用控制器上的树组件,正确传入数据、报告整树结果并等待安全退出。Objective 与机会生成把领域事实整理成当前机会,让评分只读数据,让生成器独立管理订阅。命令发布与领取发布一条命令后,经机会评估、Take、执行与 EndUse 完成一次受控使用。行为区域用可挂载的 Zone 组件分配行为层,并把回区机会与实际导航分开。调试与排查从初始化到候选、机会、应用层与后端,沿真实运行路径定位 AI 没有行动的原因。模块总览与使用了解通用调试工具的操作、远程连接与窗口扩展,再由各模块的排查说明定位具体问题。netImgui 远程面板在独立桌面程序查看调试窗口,区分远程 UI、游戏视口绘制与玩法网络权限。窗口扩展与属性绘制复用窗口生命周期、反射 Inspector 和场景绘制,避免为每个调试页重做基础设施。模块总览与接入从配置绑定、动作处理、通道缓存与玩家改键了解 Input,再接入角色的输入配置。绑定与配置生命周期理解跨 IMC 的共享绑定、配置所有权、延迟初始化,以及切换角色时的清理。Handler 与能力分派选择正确的输入阶段,把输入交给移动、GAS、GameplayEvent 或 UI,保持共享 Handler 无玩家状态。修饰器与双击选择输入值变换所在层,理解玩家设置修饰器与 Double Tap 的时序规则。通道输入缓存按 GameplayTag 通道开窗,保留首批或末批输入,正常关闭执行,中断取消丢弃。改键与玩家设置通过 GameSettings 统一编辑与应用,区分 SharedSettings 偏好和 Enhanced Input 键映射数据。调试与排查使用 Gamepad 与 Actions 观察和模拟输入,沿设备、映射、绑定、缓存与能力定位问题。模块总览与接入使用 UE 5.8 原生 Async Message System,把事件通知与接收方解耦,查阅消息收发、路由和 Starry 的实际接入。蓝图收发与解绑通过消息 Id、监听、载荷封装与解包的核心连线,掌握蓝图收发和任务解绑。C++ 收发与解绑按取得系统、登记监听、读取载荷、发送与解绑的顺序,查阅原生 C++ 接口用法。标签、时机与生命周期理解消息的标签层级、World 和 Endpoint 路由、游戏线程派发、载荷快照与 C++ 监听清理。项目消息与排查查阅 Starry 已接入的消息标签、载荷和触发点,区分本地通知、状态复制与 RPC 转发,并沿真实链路排查。模块总览与项目接入管理软引用加载、有序回调与任务作用域,了解指示器画布的实际接入,以及加载完成后的资源和宿主生命周期。软引用加载与 C++ 用法用组件加载软对象和控件类,保存加载结果,理解路径数组、同步回调、失败结果,以及重载与退出时的取消清理。主资产与资源驻留按主资产 ID 和 Bundle 预加载资源,理解句柄保留、取消与 GC 引用的关系,以及 AssetManager 和打包配置的职责。回调顺序与独立作用域理解并行加载与有序回调的区别,使用 FAsyncScope 分开取消任务,了解 AsyncEvent、开始完成通知和等待条件的当前边界。在文档中说明蓝图用法将真实蓝图节点嵌入正文,结合步骤、引脚和连线说明功能,节点文本随文章保存。维护这本手册本地文件与网页编辑共用一份 MDX,日常只改内容与关系,沿用共享版式和图表。