博客原文:Wait, I’ve changed my mind! State Machine Transition interruptions
I recently investigated a tricky bug reported by a user involving the combination of empty states, override layers, and transition interruptions. As I was digging in, I found that our documentation about transition interruptions in the animation system was a bit… minimalist. A long conversation with my team later, we concluded that a blog post was in order.
我最近调查了一个用户诡异的BUG报告,其中涉及了空状态、覆盖层级、转换中断。在深入探究后,我发现我们关于动画系统中转换中断的文档有一些……极简主义。经过与团队的长时间讨论之后,我们总结出了这篇博客。
So let’s dive into some intricate details of State Machine Transitions and interruptions!
By default in the animation system, transitions cannot be interrupted: once you start going from one state to the other, there’s no way out. Like a passenger on a transatlantic flight, you’re cozily nestled in your seat until you reach your destination and you can’t change your mind. For most users, this is fine.
让我们来看看一些复杂的状态机转换和中断的细节!
默认的情况下,动画系统的转换是不可中断的:当你开始从一个状况转换到另一个状态,是没有其他出路的。就像跨大西洋航班的乘客一样,在到达了你的目的地之前,你只能待在位置上。对于大多数使用者,这是OK的。
But if you need more control over transitions, Mecanim can be configured in a variety of ways to meet your needs. If you’re unhappy with your current destination, you can hop in the pilot’s seat and can change plans midway through your flight. This means more responsive animations, but also many opportunities to get lost in the complexity.
但是如果你对转换有更多控制需求,可以通过各种方式配置Mecanim,以满足这些需求。如果你对当前航班的目的地不满意,你可以在中途改变它。也就是说,这将是一个更灵活的动画,但使用者会很有可能搞不清这些复杂的机制。
So let’s walk through a few examples to sort that out. We can begin with a fairly simple state machine with four states, labeled A to D, and triggers hooked to every transition on the state machine.
那么让我们通过一些简单的例子来搞懂它吧。我们先建立一个简单的状态机,其中有A B C D四个状态,同时将触发器挂载到每个转换上。
建立状态机
By default, when we trigger the A->B transition, our state machine transitions towards B and nothing can keep it from reaching its destination. But if we go on the A->B transition inspector and change the interruption source from “None” to “Current State”, our journey from A to B can be interrupted by some triggers on state A.
在默认情况下,当我们触发了A->B的转换时,我们的状态机开始向转向B,而且没有什么可以阻止它到达目的地。但是如果我们去A->B的转换面板上修改Interruption Source(中断源) "None" -> "Current State",从A到B的转换可以被A上的一些触发器中断。
A->B的转换面板
Why only “some”? Because the “Ordered Interruption” checkbox is also checked by default. This means only transitions on state A that have a higher priority than the current one are allowed. Looking at the inspector of state A, we can see that this only applies to the A->C transition.
为什么是「一些」?因为"Ordered Interruption"(有序中断)是被默认勾选的。也就是说,只有A上有更高优先级的转换是生效的。在A状态的面板上,我们可以看到只有A->C的转换是生效的。
A状态的面板
So if we activate the A->B trigger, then shortly after the A->D trigger, our transition remains uninterrupted. However, if we press the A->C trigger instead, then the transition is immediately interrupted and the state machine starts transitioning towards C.
所以,如果我们激活了A->B的触发器,然后触发A->D的触发器,我们的转换依然不会被中断。然而,如果我们触发A->C的触发器,当前的转换会立即被中断,同时状态机开始向C转换。
Internally, the animation system records the pose at the time of the interruption, and will now blend between that static pose (X) and the new destination animation.
这个机制内部,动画系统记录了中断时的pose数据,并使用这个静态的pose数据与新的目标动画进行混合。
动画混合示意图
Why a static pose, instead of a possibly smoother blend between the current and new transitions? Simply put: performance. When a game faces a cascade of interruptions, keeping track of several dynamic transitions taking place simultaneously would quickly made the animation system unscalable.
为什么是静态pose数据,而不是在当前和新的转换之间可能更平滑的融合? 简单地说:性能。 当游戏面临一连串的中断时,跟踪同时发生的几个动态转换将迅速使动画系统不可扩展。
Now, if we uncheck that “Ordered Interruption” checkbox, then both A->C and A->D can interrupt the transition. However, if they are both triggered on the same frame, A->C will still take precedence because it has a higher priority.
如果我们取消"Ordered Interruption"(有序中断)的勾选, A->C 和 A->D都可以中断 A->B 的转换。尽管如此,如果它们都在同一帧被触发,依然是A->C优先,因为它有更高的优先级。
If we change the interruption source to “Next State”, A->C and A->D can no longer interrupt the transition, regardless of their order. However, if we press the B->D trigger, we will immediately start transitioning from A to D, without completing the transition towards B.
如果我们将Interruption Source(中断源) 改为"Next State",A->C 和 A->D 将不再中断转换,无论她们的优先级。尽管如此,如果我们触发了B->D 的触发器,在完成向B的转换之前,状态机将会立即开始A->D的转换。
Transition order matters on state B too. The “Ordered Interruption” checkbox is not available anymore (any triggered transition on B can interrupt the transition because they do not have a priority ranking relative to A->B), but the order of the transitions on B will determine which transition wins if both are triggered within the same frame. In this case, if B->D and B->C are triggered in the same frame, B->D will be selected.
转换的优先情况也在B上。"Ordered Interruption"(有序中断)不再生效(触发任何B上的任何转换条件都可以撞断转换,因为它们相对A->B没有优先级),但是如果多个转换条件在同一帧被触发,依然由在B上的优先级决定哪个生效。例如:如果 B->D 和 B->C 在同一帧被触发,B->D 将会生效。
B上的转换条件
Finally, for complete control, we can set the Interruption Source to “Current State Then Next State”, or “Next State Then Current State”. In that case, the transitions will be analyzed independently on one state, then the other. So, let’s assume we have the following configuration.
最后,为了完全控制,我们可以设置Interruption Source(中断源) 为"Current State Then Next State"或"Next State Then Current State"。在这种情况下,转换将在一个状态下独立分析,然后另一个状态。我们假设有以下配置:
配置
During the A->B transition, a very excited player triggers four transitions within the same frame: A->C, A->D, B->C and B->D. What happens?First, “Ordered Interruption” is checked, so we can ignore A->D right away: it has lower priority than A->B. The current state gets resolved first, so we do not even have to look at state B to know that transition A->C wins.
在A->B的转换期间,在同一帧触发了A->C A->D B->C B->D,会发生什么呢?
首先,"Ordered Interruption"(有序触发)是生效的,我们可以忽略A->D,因为它的优先级比A->B低。
其次,"Current State Then Next State",我们可以忽略B上的转换(B->C B->D),因为当前的状态高于下一个状态。
最终,我们发现A->C竞争成功。
A上的转换条件
B上的转换条件
However, with the same configuration, if only B->C and B->D get triggered, transition B->D will take place (it has greater precedence than B->C).
在相同的配置下,如果在同一帧只触发了B->C B->D,那么B->D将会生效,因为它有着更高的优先级。
Now, this is only for one transition… All other transitions can also be interruptible too, with their own specific rules. So if we make transition A->C interruptible from the next state, we can have transition A->B interrupted by A->C which in turn could be interrupted by C->D.
以上为只有一个转换的情况。其他的转换条件同样可以根据具体的规则中断它。
所有如果我们使A->C可以在"Next State"中被中断,那么我们让A->C中断A->B,在其转换过程中同样有可能被C->D中断。
One important thing to keep in mind: regardless of interruptions taking place, the source state remains the same until the transition is complete, and Animator.GetCurrentAnimatorStateInfo() will always return that source state.
一个非常重要的事情需要被记得:无论中断是否发生,在转换完成之前,源状态是不会改变的。所以 Animator.GetCurrentAnimatorStateInfo()将一直返回源状态。
In short, transition interruptions settings are powerful and offer a lot of flexibility, but they can quickly become very confusing. So use transition interruptions wisely, and when in doubt, test it out in the Editor.
简而言之,转换中断机制是一个强大且灵活的,但也能很快变得非常复杂。所以明智地使用转换中断,当有疑问时,在编辑器中测试它。
作者:万士辰
链接:https://www.jianshu.com/p/c188e1f48522
来源:
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。