For the complete documentation index, see llms.txt. This page is also available as Markdown.

角色具身化

了解角色的具身化模块如何共享一个 composition root、以确定性方式逐帧执行,并且永远不会重复写同一根骨骼。

Convai 角色上的每个具身模块——注视、身体动画、肢体语言、对话流程和情绪——都接入该角色的同一个组合根。这个根负责该角色的 tick 顺序,也是通往 Animator 和面部的唯一路径。理解这一点就能解释为什么各模块会协作,而不是争夺同一根骨骼或 blendshape。


组合根

EmbodimentContext (SDK/Runtime/Embodiment/EmbodimentContext.cs)是组合根:每个角色一个实例。Convai 会在该角色上的任意具身模块第一次解析自身时自动添加它——你无需手动添加,而且它携带 [AddComponentMenu("")] 因此它不会出现在“添加组件”菜单中。

该上下文暴露出每个模块都会读取的共享基础设施:

成员
类型
它为模块提供什么

CharacterRoot

Transform

角色的根变换。

EventHub

IEventHub

模块用于监听领域事件的事件总线。

日志记录器

ILogger

结构化诊断日志记录。

RigBinding

IStandardRigBinding

对角色骨架的语义化访问——按角色而不是按名称访问骨骼和 blendshape。

Character

ConvaiCharacter

层级中存在时,对应的所属角色。

它还会触发三个模块可订阅的事件: RigBindingChanged (语义骨架已重建或替换), EmbodimentConfigurationChanged (模块的配置在运行时发生了变化,例如预设切换),以及 DependenciesPopulated (仅运行时基础设施,例如 EventHub 已变得可用)。

每个模块控制器—— ConvaiGazeController, ConvaiBodyAnimationController, ConvaiBodyLanguageController, ConvaiConversationFlowController,以及 ConvaiEmotionController ——都派生自 ConvaiCharacterModule<TProfile> (SDK/Runtime/Embodiment/ConvaiCharacterModule.cs),它会在其中解析这个相同的共享上下文 OnEnable 并将该模块注册为其上的配置接收器。无法解析上下文的模块——因为它不在 Convai 角色上——会自行禁用并记录原因,而不是在没有驱动的情况下运行。


自动提供的基础设施

该上下文还会在模块首次需要时按需提供另外四个基础设施组件: StandardRigBinding (为 Humanoid、ARKit、CC3/CC4 及其他常见骨架确定哪些骨骼和 blendshape 扮演哪些语义角色), AnimatorConductor, EmbodimentTickScheduler,以及 FacialBlendshapeCompositorHost。这四个都是普通组件,你可以在层级视图中选中并检查它们——SDK 刻意不隐藏它们,因为看不见的组件就无法调试。 AnimatorConductor, EmbodimentTickScheduler,以及 FacialBlendshapeCompositorHost 携带 [AddComponentMenu("")];请将这三者都视为 Convai 提供的行为,绝不要把它们当作你添加的组件或直接调用的 API。


确定性的 tick 顺序

每个具有逐帧工作的模块都会实现 IEmbodimentTickable 并通过 RegisterTickable/UnregisterTickable 而不是依赖 Unity 自身的 Update/LateUpdate 顺序向上下文注册。 OnEnable 中以及 OnDisable 中注销,是受支持的模式;同一实例注册两次不会产生任何效果。

调度器会让每个可 tick 对象经历三个阶段,且始终按以下顺序:

Phase
运行内容

认知

对话流程、注视和情绪导演会采样信号并更新其读数。

表情

注视、身体和面部执行器会将这些读数转换为对骨骼、blendshape 和 Animator 参数的写入。

Finalize

在认知和表情都稳定后,面部合成器和 Animator 指挥器会完成它们的写入。

在同一阶段内,一个可 tick 对象的 TickOrder 优先级决定谁先运行——数值越小越早——若相同则按注册顺序决定。这就是 tick 顺序不依赖层级顺序或哪个模块先启用的原因:重设 GameObject 的父子关系,或者以不同顺序添加模块,都不会悄无声息地改变哪个模块先写入共享骨骼。


对 Animator 和面部的单写者访问

两个模块在同一帧写入同一个 Animator 参数或同一个 blendshape 的 bug,通常只会表现为可见的卡顿/异常,而不会导致编译错误。Convai 通过让 Animator 和角色的面部 blendshape 各自只有一个写入者来避免这一点:

  • AnimatorConductor 是 Animator 参数的唯一权威写入者。模块通过指挥器提交带名称的参数写入,而不是调用 Animator.SetFloat ;指挥器会记录每个参数由哪个模块注册,并拒绝来自其他模块的冲突注册。

  • FacialBlendshapeCompositorHost 是面部 blendshape 的唯一写入者。它会将每个模块的贡献——情绪、唇同步以及任何自定义层——合成为一组 blendshape 值,并使用 ConvaiFacialCompositionProfile 中的区域规则来决定每个贡献者能分到多少嘴部、眉部、眼部、脸颊和下颌区域。

二者都是自动提供的基础设施,而不是你直接配置的组件。如果你需要影响它们写入的内容,请通过拥有该行为的模块来做——表情用 Emotion,嘴型用 LipSync——而不是自己直接在同一个网格上写 blendshape。

跨模块契约,例如注视如何告诉身体动画转身, internal 包内部的。它们不是对外公开的扩展点——公开的只有这些组件及其配置文件。


这五个具身模块

模块

组件

“添加组件”菜单路径

ModuleIds 常量

注视

ConvaiGazeController

Convai/Embodiment/Gaze

ModuleIds.Gaze (convai.gaze)

身体动画

ConvaiBodyAnimationController

Convai/Embodiment/Body Animation

ModuleIds.BodyAnimation (convai.body-animation)

肢体语言

ConvaiBodyLanguageController

Convai/Embodiment/Body Language

ModuleIds.BodyLanguage (convai.body-language)

对话流程

ConvaiConversationFlowController

Convai/Embodiment/Conversation Flow

ModuleIds.ConversationFlow (convai.conversation-flow)

情绪

ConvaiEmotionController

Convai/Embodiment/Emotion

ModuleIds.Emotion (convai.emotion)

每个 ModuleIds (SDK/Domain/Embodiment/Modules/ModuleIds.cs)常量是 slot 使用的路由键,用于把该模块的设置资源交给它。 ConvaiEmbodimentPreset slot 用来把该模块的设置资源交给它。五个模块都是可选的,且可独立添加——角色可以只运行 Gaze、只运行 Emotion,或任意组合;当某个同伴模块缺失时,每个模块都会优雅降级。


下一步

对话状态资源所有权与写时复制

最后更新于

这有帮助吗?