查看原文
状态机切换打断机制
在默认的动画系统中,切换不能被打断:一旦开始从一个状态切换到另一个状态,就不能中途退出。就像飞越大西洋的航班,一旦开始,只能到目的地,中途不能改变。大多数情况下,动画状态机的这个机制能工作的很好。
但是如果想要更进一步地控制切换,Mecanim 可以通过多种配置方式来达到需求。这可以更加灵活地控制动画,但是在复杂地情况下,也很容易发生错误。
我们通过几个粒子来解决这些问题。可以从一个简单地,只有4个状态地状态机开始,分别是状态A,B,C,D,以及进行切换的触发器。
默认的,当出发A->B的切换时,状态机会切换到B,并且没有办法中止。但是如果我们到A->B切换的Inspector窗口,并且将 Interruption Source 从 “None” 改成 “Current State”,从A->B的切换,可以被状态A的某些触发器打断。
为什么只是某些呢?因为 “Ordered Interruption” 复选框,默认时选中的。这意味着,只有这个状态上拥有更高的优先级的切换会打断。看状态A的Inspector,可以看到更高优先级的是A->C
所以,如果我们触发A->B的切换,然后很短的时间内触发A->D,切换不会被打断,因为A->D的优先级低于A->B。如果触发A->C,则A->B的切换,会被中途打断,执行到C的切换。
在动画系统内部,会记录下正在进行的切换的动画姿态pose(X),并将该静态姿态(不会再更新该动画)与目标动画进行融合。
之所以用静态姿态,而不是更加平滑的动画融合,简单说,就是效率。当有一连串的打断发生时,处理大量的动画融合过程,会严重影响动画系统的执行效率。
现在,如果我们取消对 Ordered Interruption 复选框的选中,则A->C和A->D触发器都可以打断A->B的切换,但是如果它们在同一帧里同时触发,则会执行A->C,因为它的优先级更高。
如果我们将 Interruption Source 该为 “Next State”,A->C和A->D就不能再打断A->B的切换了。 如果再A->B切换过程中触发B->D,这时会立即开始A->D的切换,而不会完成到B的切换。
而且这时 “Ordered Interruption” 复选框是无效的(任何B上的切换,都可以将该切换打断,因为这些切换是相对与一个中间切换的,没有A->B相关的优先级顺序),但是当切换再同一帧触发时,B状态会决定哪个切换的优先级高,来执行。当B->C和B->D再同一帧触发,则会选择B->D。
最后,我们可以设置 Interruption Source 为 “Current State Then Next State” 或者 “Next State Then Current State”。这两个选项会同时对当前和下个状态进行上面的判断逻辑,但又指定了它们的相对顺序。
让我们假定下面的设置
再A->B的切换过程中,用户同时触发了4个切换,A->C, A->D, B->C, B->D,那么哪个将被执行?
因为选中了 “Ordered Interruption”,所以可以忽略A->D,因为它的优先级比当前切换低。又因为 Current State 被优先处理,所以最终执行 A->C。
同时,如果再该配置下,只有B->C,B->D 被触发了,则执行B->D,它的优先级更高。
再举个粒子:如果我们设置A->C可以被next state 打断,那么当A->B被A->C打断后,C->D可以再次打断。
有一点需要牢记:无论打断时如何发生的,source state 在切换完成前,不会改变,Animator.GetCurrentAnimatorStateInfo() 也只会返回该 source state。
此外,要知道,AnyState 的切换,永远被优先考虑。
总之,切换打断功能很强大,并且非常灵活,但是也会带来困惑。所以使用打断时,要确切地知道,将要发生什么。如果不确定,那么在编辑器测试好结果。