I’ve started using Mecanim to animate arbitrary gameplay elements and I’ve run into a fairly odd limitation. If the GameObject an Animator is attached to is deactivated, it appears to be impossible to reliably ensure the default values for animated properties are preserved. The next time the GameObject is set active the animated properties will have different default values (whatever the animated properties happened to be when the GameObject was deactivated) and this sort of breaks the whole idea of the defaults.
I’ll do my best to explain how this happens. I’m hoping someone here knows a way around the issue.
I rely on Write Defaults being enabled on all states which allows me to count on an object being in a very well defined state when it enters any animation state. It appears that the Animator component is initialized when the GameObject becomes active. This happens regardless of whether the Animator is enabled or not. When the Animator initializes, it records the current values of all animated properties and saves them as the ‘defaults’. When the GameObject becomes inactive, the Animator is no longer initialized and it ceases to track and apply defaults. The important point here is that this can happen multiple times. Each time the GameObject is set active, new defaults will be recorded.
I find it a bit odd that your defaults can change over time, but whatever, that should be easy enough to work around. I just have to make sure the object is returned to its original default state before the GameObject is deactivated so that the next time it is activated I end up with the same set of defaults. However, it turns out this can’t be done reliably.
I have a helper script I use alongside the Animator to ensure the original defaults are applied as the GameObject is being set inactive. This is done fairly trivially:
private void OnDisable ()
{
if ( !isDisablingDueToAnimationEvent )
{
animator.Play("None", 0, 0);
animator.Update(float.Epsilon);
animator.Update(float.Epsilon);
}
}
This is more obtuse than it needs to be because of a couple Mechanim bugs, but it’s not too bad. I play an empty animation state and force the Animator to update until it actually writes the default values to the object. I do this in a single frame so there’s no popping due to animated values being changed. isDisablingDueToAnimationEvent is there to short circuit when the GameObject is being set inactive as a result of an AnimationClip event. Updating the Animator from inside an event causes the event to trigger again, recursing, causing a stack overflow and locking up the Editor. Fun. The double update is there because it seems the Animator takes up to 2 frames before writing the default values. It doesn’t appear to matter what value is used for deltaTime, it just has to be done twice.
Unfortunately, this breaks down as soon as the Animator Component is physically above the helper script in the Inspector. The Animator will end up getting disabled first, which causes it to un-initialize, which in turn means it will no longer apply defaults in any circumstance I’ve been able to find (including calling WriteDefaultPose via reflection).
This means that if a GameObject is disabled while in the middle of Animating, it may or may not break defaults based on the order of the Components. Now, this can be worked around in a variety of ways, but all of them require extremely precise management of entire GameObject hierarchies and are error prone. This seems like entirely too much subtle knowledge and fragility for a fairly basic animation setup.
Is this something that should be considered a bug or design flaw in Unity? Does anyone happen to know a reasonable workaround for this?