不止是这样,当后加入世界的人重新加载你的模型,Animator 也会被重新初始化。另外最新的 VRChat 版本的距离隐藏和数量隐藏功能会将被隐藏的模型的 Animator 暂停,这都会导致设计不佳的状态机在本地和远程失去同步。
因此,在设计 Animator Controller 的状态机时,如果不想出现同步错误,必须要考虑:
对于同样的状态(也就是同步的参数)输入,能否让不同状态的状态机恢复到相同状态?
以下是针对状态机同步问题常见的几种情况。
一、如果你的状态机不依赖参数变化的先后顺序,也就是时序无关的纯组合逻辑,则总是能保证最终状态一致。
因为不管当前状态如何,状态机对于同样的输入总是会跳转到相同状态。
但仍须注意的是,如果状态机的 State 上存在 VRC Parameter Driver 脚本,或者其它设计,只要状态转移本身具有副作用,那么当不同的状态转移顺序会导致不同的结果时,同步也是不可靠的。
二、依赖状态转移顺序,或者状态转移本身具有副作用的状态机,无法保证正确同步。
例如,枪械控制的弹药计数、依赖触发顺序的魔法施放,这些都需要状态转移本身产生副作用。如果本地和远程运行相同的状态机,那么在后加入的用户或者重新加载你的模型的用户看来,状态机必然会失去同步,这可能会导致状态机卡住、无法正确计算弹药数、错误触发特效等问题。
好消息是,本地 Animator 总是可靠的,因此,对于这些具有副作用的逻辑,应当尽量放在本地进行计算,再通过参数同步指定远程进行表现。这时,副作用只在本地发生,远程状态机只依赖参数的排列组合产生相同的效果。
三、有些情况下,最好不要同步,例如使用 Contact 组件的触摸表情等。
因为这些功能是否同步并不会导致严重逻辑问题,而且如果强行同步还会导致效果出现延迟。
VRChat Avatar SDK 中对于 Contact 有这样一段描述:
Parameter
- The parameter updated on the animation controller. This parameter DOES NOT need to be defined on the synced Avatar Parameter list. The parameter can be a Float, Bool, or Int, depending on the Receiver Type used.
Parameter
- Animator Controller 上更新的参数。此参数不需要在参数列表中设置为同步。该参数可以是 Float、Bool 或 Int,具体取决于所使用的接收器类型。
这段内容实际的含义:Contact 的触发依赖于 Contact 的坐标位置,但一般情况下,Contact 坐标已经被身体的运动间接同步了;或者 Contact 的位置也不重要。与此同时,Contact 引用的参数会被直接写入 Animator,因此如果没有需求,不需要在 VRC Expression Parameters 列表中注册。
考虑这样一种场景:使用 Contact 触发触摸表情。
由于网络延迟,本地和远程的运动并不是立刻同步的,如果强行将参数设置为同步,会导致远程用户看上去应该触发 Contact 时,本地用户必须等待两人网络延迟总和才能触发本地 Contact,本地 Contact 修改参数被同步后又经过一次两人网络延迟总和的时间,最终远程用户才能看到触发效果。
当然,如果你的设计目标就是要可靠同步,那就必须这么做,因为没有任何简单可靠的手段能获取远程状态机上的参数值:参数的同步总是单向的。
考虑这样一种场景:使用 Contact 触发一些效果时(比如开关某些状态),如果希望保证状态一致性,应当将 Contact 设置为 local only,并使用 Contact 输出的参数驱动其它状态或直接同步 Contact 输出的参数。这是由于参数的同步是单向的,如果要保证本地和远程的一致性,就只能保留一份本地副本。
当然,这样做的代价是延迟较高。对于延迟问题的缓解,可以使用预测的手段:在远程 Animator 上也检查 Contact 参数,提前触发效果。如果一段时间内参数同步后表明本地的状态和远程一致,则保持状态;否则回滚状态。即,使用预测回滚的策略降低延迟。
上面这些内容只是粗略的简单描述,如果想要更进一步,这里再列出一些补充知识点:
- Animator Controller 的每个 Layer 是一个确定有穷状态机
- 学习一些布尔代数的运算规则有助于处理复杂的条件设计,Animator Controller 的 transition 可以被等价转换为布尔函数的析取范式
- 了解数字电路设计中的时序逻辑和组合逻辑,有助于理清 Animator Controller 的设计思路
- WolframAlpha 免费的在线网站,可以用于布尔函数的组合、取反和化简,如 BooleanMinimize 函数可用于计算析取范式