Skip to main content

Write Defaults

文章原址:https://notes.sleightly.dev/write-defaults/

Write Defaults

Write Defaults 是动画状态(Animator States)的一个可选项,这对许多虚拟形象创作者来说都是一个令人头疼的问题,因为它的行为表现比较反直觉。其预期行为如下:

Write Defaults 决定动画中所做的更改是否是持久性的。

如果启用了 Write Defaults ,则模型在离开上一个动画状态后,在当前动画状态绘制前写回上个动画状态中动画属性的默认状态,不保留上个状态的更改(见示例)。

如果禁用了 Write Defaults ,则不会写回默认状态,保留上个状态的更改(见示例)。

默认情况下,Unity 会在新创建的状态上启用 Write Defaults,但 VRChat 提供的示例动画控制器(Animator Controller)没有启用 Write Defaults了。

Untitled.png

默认新创建的动画状态为开启 Write Defaults

示例:

如果您有一个在层级( Hierarchy )中被禁用(即默认情况下关闭)的帽子(hat),并且您有一个看起来像这样的层级:

Untitled.png

在 Write Defaults On 的情况下:

  • 当进入 Hat On 的状态时,帽子会切换为开启状态
  • 当回到 Hat Off (Empty State)的状态时,帽子会切换为关闭状态(因为这是默认状态) 

Write Defaults Off 的情况下: 

  • 当进入 Hat On 的状态时,帽子会切换为开启状态
  • 当进入 Hat Off (Empty State)的状态时,帽子将保持开启状态(因为您没有特意将其切换回关闭状态或写回默认值)

操作规则

在使用 Write Defaults Off 时,请遵循以下几条规则。我将在下方解释原因(仅为完整性起见):

  • 请勿在单个控制器中混合使用 Write Defaults。如此设置的表现将类似于使用 Write Defaults Off
    • 此条有例外情况:具有单个状态(或包含 Direct Blend Trees 的单个状态)的层可以被设置为 Write Defaults On
    • 由于上面的层永远不会离开当前状态,所以您可以这样做,我们从不依赖 Write Defaults On 行为。
  • 请勿在包含 Direct Blend Trees 的状态上上使用 Write Defaults Off
  • 如果您正在使用 Write Defaults Off,则您的所有状态都应包含一个动画片段 (Animation Clip) 或一个 Blend Tree。这个动画片段可以是空的,但通常建议使用绘制了不存在的 GameObject ,且持续至少两帧的动画片段(偶尔被称为缓冲动画,或 buffer clip)。
  • 请注意,用 Write Defaults Off 绘制变换动画会覆盖其他正在播放的动画控制器中的所有变换动画。对于 VRChat 的具体例子是:如果您正在绘制 FX 层中的任何变换,它将覆盖 Gesture 层中的所有变换,除非您应用一个仅启用变换动画并禁用所有其他变换的人形遮罩(Avatar Masks),并将此遮罩应用到您的 FX 层动画层。

有关遮罩的更多信息,请参阅“Avatar Masks”页面。

请注意:如果您因 Gesture 和 FX 层中的 Write Defaults Off 变换动画而必须使用遮罩,则不能在除第一个材质插槽之外的任何插槽上使用材质切换动画,因为无论遮罩是否启用,被遮罩的变换(无论是启用还是禁用)都不能在除第一个材质插槽之外的其他材质插槽上具有材质切换动画。

VRLabs 的 Avatar 3.0 Manager 是用于检查您是否违背了以上规则的实用工具(检测至少前两条规则) 。在该工具的 Write Defaults 选项卡中,它会告诉您是否混合使用了 Write Defaults,并会在您按下相应按钮时为您自动设置。请注意,它不会在设置过程中特意排除 Direct Blend Trees,因此包含它们的动画状态必须在其名称中包含(WD On)字段,以使该工具将其标记为 Write Defaults On

解释

不要在单个控制器中混合使用 Write Defaults

处于单个 Write Defaults Off 状态时,这将导致所有其他 Write Defaults On 状态不写回其默认值,并表现为 Write Defaults Off状态。

不要在 Direct Blend Trees 中使用 Write Defaults Off

 Write Defaults Off 的 Direct Blend Trees 会出现一些奇怪的行为,例如影响其他在使用相同参数的 Direct Blend Trees,并且还会随着时间的推移将乘以它们的结果,导致意外结果的发生。您通常应永远不这么做。

为了更清楚地说明乘法行为,此处列出最终结果:在 Write Defaults Off 的 Direct Blend Trees 中,最终的权重会乘以一个因子x/(1-yx),其中 x 是当前子级的权重,y 是其他子级的权重之和,除以当前子级的权重。请注意,如果这个分数的分母为 0 或更低,则数值变为无穷大且永远不会停止增加。这意味着,如果您有 n 个子级,并将每个子级的权重设置为 1/n,则最终结果是乘数为 1 ,因此这允许您使用 Write Defaults Off 的 Direct Blend Trees。然而,这并不意味着这样做就是明智的。因此由于这会影响其他混合树和表现出其他奇怪的行为,您可以使用 Write Defaults On ,它更为简单。

如果您正在使用 Write Defaults Off,则您的所有状态都应包含一个动画切片或 Blend Tree。

不包含任何动画片段的 Write Defaults Off状态将会使用上一个状态中动画片段的属性默认值,覆写上方图层中的动画片段。

优点/缺点

Write Default On

优点:

  • 由于可以使用空动画状态 (或 buffer clip) 来恢复到上传时模型状态,因此使用稍简单些
  • 允许在 FX 层上使用变换动画,且不会将动画覆写到 Gesture 层
  • 在基础的双状态开关切换中,相对 Write Defaults Off 减少了33%的帧时间(在Unity 2019中)
  • 更容易更改默认值(如果您依赖上传时状态)

缺点:

  • 依赖上传时状态
  • 通常在混合使用 Write Defaults 时会出现问题
  • 不允许在不使用 Parameter Drivers 或 AAP 的情况下进行顺序迭代操作

Write Default Off

优点:

  • 不依赖于上传时状态
  • 通常与混合使用 Write Defaults 兼容
  • 允许顺序迭代操作

缺点:

  • 在空状态中需要缓冲动画
  • 在没有遮罩的情况下对变换进行动画处理时,会覆盖所有手势变换
  • 在基础的双状态开关切换中,相对 Write Defaults On 增加了50%的帧时间(在 Unity 2019中)

创作指南

当创建应与 Write Defaults On 和 Write Defaults Off 兼容的预制件时,这些指南将非常有用:

  • 请确保每个图层中都包含一组其专有,不同于其他图层所拥有的动画属性(例如,图层中的每个状态都应动画化相同的一组属性)。
  • 如果需要在多个图层上动画化同一属性,请确保每个状态都包含一个动画片段,且较低图层会覆写较高图层,并始终在顶部提供一个具有默认状态的“重置”图层。

如果您遵循本文档中概述的指南,每个属性都将始终动画化,并且您的系统将始终与 Write Defaults On 和 Write Defaults Off一起正常工作。

Blendshape 值翻倍

在非常特定的条件下,使用 Write Defaults On 时,Blendshape 值会翻倍。

(例如:您有一个空的 Write Defaults On 状态,它过渡到一个绘制 Blendshape 的 Write Defaults On 状态。过渡到空的 Write Defaults On 状态时,之前被动画化的 Blendshape 的默认值会乘以 3 。)

这个问题比其他问题更加微妙和模糊,所以请勿把这里的描述当作事实,而更多地当作我的观察结果:

  • 它似乎是由使用 Write Defaults Off 的 Additive layer 引起的。
  • 它被一个只有 Write Defaults Off 状态(如默认状态)的手势层所阻止。播放一个使用 Write Defaults On 的状态会导致它再次发生。
  • 它与任何遮罩和实际可过渡到的动画都无关。只需存在于图层上就足够了。
  • 只要您使用 0 或 100 的值,就不会发生这个问题,因为 0 乘 3=0 且 100 乘 3=100 (由 Blendshape 钳制导致的,您可以在 Unity 项目中通过转到 Project Settings → Player → Other Settings → Clamp Blendshapes (Deprecated) 来启用它。这个功能在 VRChat 中已被启用。)
  • 修复此问题的正确方法是始终明确设置 Blendshape 值。
  • 糊弄式修复此问题的方法是设置空闲图层中的状态为 Write Defaults On