动态上下文使用示例
五个动态上下文示例展示了基于中继的触发、批量更新、事件日志、紧急切换以及同步的世界对象上下文。
以下示例从单个继电器驱动的触发器逐步扩展到脚本化批量更新以及同步的世界对象上下文。每个示例都列出场景背景、具体的 Inspector 或脚本设置,以及预期的运行时结果。
安全演练:站点跟踪
背景: 一次消防抑制认证演练。一个训练员 NPC 引导操作员通过各个抑制站点。该角色必须始终知道操作员当前所在的站点,以便提供针对站点的指令和危险警告。
设置(继电器 + 触发脚本)
添加
ConvaiDynamicContextRelay到训练员 NPC 的 GameObject(Convai → 动态上下文 → Convai Dynamic Context Relay).在 默认值 部分中,设置 响应模式 到
MustRespond——角色应确认每一次站点变更。启用 立即刷新 ,这样每次站点变更都会立即发送,而不是等待常规批处理窗口。
向每个站点的触发体添加一个小型触发脚本,让每个实例都指向同一个继电器:
using Convai.Runtime.Presentation.DynamicContext;
using UnityEngine;
public class StationZoneTrigger : MonoBehaviour
{
[SerializeField] private ConvaiDynamicContextRelay _relay;
[SerializeField] private string _stationName;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
_relay.SetState("Station", _stationName);
}
}将 站点名称 设为区域标签,例如
灭火区,并为每个额外站点重复此操作。
ConvaiDynamicContextRelay.SetState 需要同时传入状态名和值,因此不能像单参数方法那样直接在 UnityEvent Inspector 中绑定。上面的触发脚本在运行时提供名称和每个区域的值;继电器会解析训练员的 ConvaiCharacter 并应用所配置的响应模式。
预期结果
当操作员进入灭火区触发器时, StationZoneTrigger 调用 SetState("Station", "Fire Suppression Bay") 在继电器上。继电器以 MustRespond 进行暂存并立即刷新:
"你已经到达灭火区。当前危险等级极高,请在接触任何设备前确认你的 PPE 已佩戴完毕。"
该 Station 状态会保留在本地跟踪器中。如果操作员在任何时候问“我在哪儿?”,角色都会用当前站点值回答。
入职引导演练:批量状态更新
背景: 一次企业入职模拟。一名 HR 代表 NPC 会根据新员工已收集的物品和已完成的检查点来调整引导内容。两个条件会在同一时刻满足——将它们一次性发送,这样角色就会同时对二者作出反应。
设置(脚本)
预期结果
OnAccessCardAndBriefingComplete() 会在一次调用中暂存这两个状态。SDK 会将已暂存的更新批量处理,最长可达 ConvaiCharacter.DynamicContextBatchDelaySeconds (默认 0.5 秒),然后向 Convai 发送一次规范重建。因为 MustRespond 请求了,因此 HR 角色会在批处理刷新后响应:
"你已经拿到访客卡并完成了安全简报——你已获准进入楼层。下一步请前往 4B 工作站。"
TryGetStateValue 直接从本地跟踪器读取,无需网络往返。如果该状态已设置,则返回当前值;如果从未设置或已被移除,则返回 false 。
导览:展品跟踪和访客事件
背景: 一次博物馆导览。一名讲解员 NPC 会跟踪当前激活的展品,并按时间顺序记录访客交互,然后利用这些历史记录提供个性化推荐。
设置(两个继电器实例)
ConvaiDynamicContextRelay 每个 GameObject 只允许一个实例,因此每个具有不同默认响应模式的继电器都放在讲解员 NPC 各自的子 GameObject 上。
子 GameObject 1 — "ExhibitRelay"
添加
ConvaiDynamicContextRelay.目标 → 自动解析角色: 已禁用; 角色: 讲解员的
ConvaiCharacter——继电器位于子 GameObject 上,而不是角色自身的 GameObject 上。默认值 → 响应模式:
静默——展品名称会更新,但不会立即响应;游览叙事决定节奏。向每个展品的触发体添加一个触发脚本(与
StationZoneTrigger在第一个示例中相同的模式),调用_relay.SetState("ActiveExhibit", _exhibitName),并让每个实例都指向该继电器。
子 GameObject 2 — "EventRelay"
添加
ConvaiDynamicContextRelay.目标 → 自动解析角色: 已禁用; 角色: 讲解员的
ConvaiCharacter.默认值 → 响应模式:
自动——由 Convai 决定记录的交互是否值得立即响应。将 UI 按钮直接连接到
AddEvent.AddEvent需要一个字符串参数,因此可以从按钮的 On Click () 通过一个静态字符串参数进行绑定——无需脚本。将一个按钮绑定到EventRelay → AddEvent,参数为访客询问了斗兽场重建,再将第二个按钮绑定到AddEvent和访客拍摄了角斗士展品.
预期结果
随着访客继续参观,讲解员的规范上下文会不断累积:
讲解员会同时提及当前展品和访客的具体交互:
"既然你拍摄了角斗士展品,你可能会喜欢隔壁房间里关于罗马军事装备的额外展区。"
应急响应:多状态转换与响应升级
背景: 一次工业安全模拟。一名主管 NPC 必须对从例行检查到应急模式的同时切换作出响应——三个状态变化和一个记录事件会一起发生,而角色必须在一次响应中全部确认。
设置(脚本)
预期结果
这两个调用都在同一个防抖窗口内暂存——从第一次暂存的变更开始,最长可达 ConvaiCharacter.DynamicContextBatchDelaySeconds (默认 0.5 秒)。跟踪器会将这三个状态和该事件合并为一次规范重建,并在整个批次中保留所请求的最强响应(MustRespond 优先于 静默),因此角色只会产生一次响应,涵盖全部四项变更:
"Bay 7 发生化学泄漏——所有人员立即撤离东翼。Bay 7 的通风已启动。在给出解除警报前不得返回。"
调用 SetStates 用于这三个同时发生的转换,而不是三个连续的 SetState 调用,可以将这些变更保留在角色用于叙述过渡的 delta 行中。这个 AddEvent 调用会将警报作为单独的时间顺序行添加,而不会降低该批次已请求的响应级别。
同步的世界对象上下文:共享设备状态
背景: 一次工业安全模拟,工厂车间里有两个 NPC——一名主管和一名安全讲师。两个角色都必须知道一个压力阀是开启还是关闭,而无需任何一个角色的脚本直接轮询该阀。
设置(带有跟踪属性的世界对象)
添加
ConvaiObjectMetadata到阀门的 GameObject(Convai → 世界对象,或在 Add Component).将 对象名称 到
PressureValve和 对象描述 填写阀门位置的事实性描述。在 跟踪属性,添加一个
ConvaiTrackedContextProperty条目:属性名称:
Status源组件: 阀门的控制器脚本
源成员名称: 报告当前状态的公共属性或字段名称,例如
Status初始值:
关闭——仅在反射读取失败时使用响应:
自动——让 Convai 决定该状态变化是否值得提及
SDK 会在共享计时器上轮询每个带有 源组件 的跟踪属性。当 ValveController.Status 变化时, ConvaiObjectMetadata 会使用状态键 PressureValve.Status.
预期结果
一旦阀门打开,监督员和安全讲师都会收到相同的状态更新:
现在任一 NPC 都可以在没有专用脚本向其提供数据的情况下引用该阀门——例如,监督员:“压力阀已打开——在确认关闭前保持原位。” 禁用或移除该 ConvaiObjectMetadata 组件会从 PressureValve.Status 所有正在跟踪它的角色中移除该状态。
下一步
中继组件参考动态上下文脚本 API同步行为与时序场景元数据最后更新于
这有帮助吗?