Search Unity

Impossible to maintain Animator default values in all scenarios?

Discussion in 'Animation' started by Sycobob, Mar 29, 2016.

  1. Sycobob

    Sycobob

    Joined:
    Feb 1, 2013
    Posts:
    78
    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:

    Code (csharp):
    1. private void OnDisable ()
    2. {
    3.     if ( !isDisablingDueToAnimationEvent )
    4.     {
    5.         animator.Play("None", 0, 0);
    6.         animator.Update(float.Epsilon);
    7.         animator.Update(float.Epsilon);
    8.     }
    9. }
    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?
     
    Last edited: Apr 3, 2016
  2. Sycobob

    Sycobob

    Joined:
    Feb 1, 2013
    Posts:
    78
    It would appear the partial workaround I mentioned above has mysteriously stopped working entirely. I'm still curious if there is a reasonable workaround for this that doesn't require hard coding a 'reset to defaults' in the helper script. Any input from a Unity dev would be greatly appreciated.
     
  3. pep_dj

    pep_dj

    Joined:
    Nov 7, 2014
    Posts:
    179
    Hi Sycobob.

    I'm having exactly the same issue you are having. All my animations are working perfectly until I deactivate, and reactivate gameObjects. I think it's a serious bug, but I'm not able to find information about it on Internet, so, how developers are dealing with this issue?
     
  4. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    I may not understand this but in Unity it's commonplace that you need to use an 'OnEnable/OnDisable' pair regarding items that go in and out of being active. This applies in many, many situations from networking to connectivity (analytics and the like) to blends and so on.

    Specifically regarding mecanim animations in the first post, I feel it's kind of "weird" to even turn off (I mean SetActive(false) ) an animating thing. (Unless you're just completely getting rid of it with Destroy.) It is not physical. If you want it to "pause" or something you need to animate it there (with a blend or whatever).

    *"working perfectly until I deactivate, and reactivate gameObjects"* in a word, you are missing an OnEnable/OnDisable pair ?
     
  5. pep_dj

    pep_dj

    Joined:
    Nov 7, 2014
    Posts:
    179
    In my game, I have some game objects (characters, buttons...) that should not be visible, nor interactable, under certain situations. As far I know, I should call SetActive(false), so they are not visible, not interactable, and not consuming cpu.

    Calling SetActive(false), and then SetActive(true), changes game object default values, so animations using "Write Defaults" don't work as expected.
     
  6. pep_dj

    pep_dj

    Joined:
    Nov 7, 2014
    Posts:
    179
    I'm not sure you understand our problem. As the op said, there is a "Write defaults" option in animation states, that is behaving in a different way before the object is deactivated and reactivated, and after it. That's it.

    And it's the same is happening to the op in other thread: http://forum.unity3d.com/threads/write-defaults-confusion-bug.392058

    If I want to hide an object temporarily, I don't need to destroy it and then get a new one from the pool. There is a simple way to do it: SetActive(false), and then SetActive(true). The only problem, is that the object property values that animations are taking as "default", are not the same than before deactivating/reactivating the object.
     
  7. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    You're right, I probably misunderstand you. For me everything works as DavidGeoffroy explains on that linked page. I'm deleting my longer comment to avoid confusion. Good luck to you guys!
     
  8. Sycobob

    Sycobob

    Joined:
    Feb 1, 2013
    Posts:
    78
    I ended up just manually resetting everything to defaults in the helper script OnDisable. It's super lame, but I don't have time to go chasing down yet another Unity bug/quirk/whatever.
     
  9. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    Probably absolutely no comfort, but dats wot I'd do and (to me) it seems natural, since, like we were saying coroutines (or anything similar), you're expected to "do that to" in the same way. sucks all round! :/
     
  10. pep_dj

    pep_dj

    Joined:
    Nov 7, 2014
    Posts:
    179
    Thanks. I think that's the only way to solve it.
     
  11. adamgryu

    adamgryu

    Joined:
    Mar 1, 2014
    Posts:
    187
    Hey, sorry to bump this old thread, but I'm also having troubles with this limitation right now.

    It's too bad your hack stopped working, I might try it again in the current Unity version and get back to you.

    I can manually reset all the states, but I'm wondering if there is a nice automated way of doing it.
    Is there any way we can automatically read all the animated values and then save / restored them?