下面我们来深入研究一下状态机的转换和中断的细节。
默认情况下,在动画系统中,转换是不能被中断的:一旦开始由一个状态转换为另一个状态,这个过程没有出口。就像一个乘坐飞越大西洋航班的乘客,只能舒适的靠在座位上直到抵达目的地,不能改变自己的目的地。对大多数用户来说,这是好事。
但是如果需要对转换过程有更多控制,也可以通过使用多种不同方式配置Mecanim来满足需求。如果对当前目的地不满意,可以跳到驾驶员的位置在中途改变飞行计划。这意味着响应动画更多,同时也意味着很有可能会迷失在这复杂的情况中。
下面通过几个例子来说明一下。首先是一个简单的包含4个状态的状态机,标记为A到D,并且触发器都对应了状态机上的每个转换。
默认情况下,当触发了A->B的转换后,状态机会将状态从A转换为B,并且没什么可以阻止它最终变为状态B。但是如果打开A->B转换的检视面板,并将Interruption Source(中断源)从“None(无)”变为“Current State(当前状态)”后,从A到B的这个过程就可以被状态A的某些触发器中断了。
为什么是“某些”触发器?因为“Ordered Interruption(顺序中断)”默认情况下是勾选的。这意味着在状态A的转换过程中,只有比当前转换优先级更高的转换能够被执行。通过查看状态A的检视面板可以知道,只有 A->C这个转换的优先级比A->B高。
因此如果触发了A->B,马上又触发了A->D,那么转换过程不会被中断。然而如果后触发的是A->C,那么A->B的转换会被马上中断并且状态机会转换至状态C。
在内部,动画系统记录了中断发生时的姿势,并且将静态的姿势(X)和新的目标动画进行合成。
为什么是静态姿势,而不是在当前转换和新转换之间进行可能更流畅的融合?简单来说:性能原因。当游戏面临中断的级联时,同时持续追踪很多正在进行的动态转换将使得动画系统不可扩展(因为每增加一个新状态都会消耗更多的系统资源)。
现在,如果取消勾选“Ordered Interruption(顺序中断)”,那么A->C和A->D都可以中断A->B的转换。但如果它们在同一帧触发,那么A->C仍然会优先执行,因为A->C的优先级更高。
如果将中断源改为“Next State(下一状态)”,A->C和A->D将不再中断转换,不论它们的顺序如何。但是如果触发了B->D,则会马上开始A到D的转换,并不会完成到B的转换。
转换顺序对于状态B来说也很重要。“Ordered Interruption(顺序中断)”的勾选已经不能用了(任何从状态B触发的转换都可以中断A->B的转换,因为它们都没有相对于A->B的优先级排序),但状态B的转换顺序会决定在同一帧都被触发的情况下最终将转换到哪个状态。在这个例子中,如果B->D 和 B->C在同一帧触发了, 则B->D会被选中。
最后,为了实现完全控制,可以将中断源设置为“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,因为它的优先级不如A->B高。当前状态会最先处理,所以不用看A->B了,这里进行的是A->C的转换。
但是,相同配置条件下,如果只触发了B->C和B->D,那就会进行B->D的转换(它比B->C的优先级高)。
现在还只是一个转换,所有其他的转换也是可能以其特定的规则被中断的。所以如果让A->C的转换从下一个状态中断,那可能A->B的转换会被A->C中断,而反过来A->C的转换也可能会被C->D中断。
有一点很重要,不管中断在何处发生,原状态会保持不变直至转换结束,而Animator.GetCurrentAnimatorStateInfo()将始终返回起始状态。
简而言之,状态转换的中断设置非常强大,也提供了极高的灵活性,但也会让人非常迷惑。所以要明智地使用状态转换中断,有疑问一定要现在编辑器中测试。