Search Unity

Write Defaults Confusion / Bug

Discussion in 'Animation' started by TitanUnity, Mar 16, 2016.

  1. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    It's a bug, it should be deterministic and if the default value are not saved then it not deterministic.
    Could you log a bug with your project showing the issue please?

    This is high priority for us and should be fixed asap
     
    TextusGames and mcroswell like this.
  2. adamgryu

    adamgryu

    Joined:
    Mar 1, 2014
    Posts:
    188
    Hey, thanks for looking into this. This will be a really useful feature once it's fixed.

    I just made an example project and submitted a bug.

    To help clarify the problem, here's a GIF I made showing the bug in action.
    GIF.gif

    The ENTER button toggles between a spinning animation (the default) and a moving animation.

    The cube on the left is marked Animator.keepAnimatorControllerStateOnDisable = true.
    Notice that in spite of this, it forgets it's original position when the GameObject is disabled and re-enabled.

    Edit: Here's the bug report - https://fogbugz.unity3d.com/default.asp?1076483_ivcpr3om28aq3tqs
     
    Last edited: Aug 30, 2018
  3. OndrejP

    OndrejP

    Joined:
    Jul 19, 2017
    Posts:
    304
    Hi,
    I'm also affected by this issue.

    I'd like to suggest simple solution:
    Instead of "remembering default values" which might not be easy, it would be completely sufficient to write default values just before the animator is disabled.

    Simply add checkbox to animator: Restore defaults on disable
    - this can be used with keepAnimatorControllerStateOnDisable to continue in fully deterministic manner
    - can be used without keepAnimatorControllerStateOnDisable to safely "reset" the object to "pre-animation" state when animator is disabled

    Now I have to scan object for animators before deactivation and call "WriteDefaultValues" manually (I can't even do it in OnDisable, because in OnDisable, WriteDefaultValues does nothing).
    The problem with my workaround is apparent when multiple different objects gets deactivated in different ways...e.g. when using object pools

    When looking for solution or workaround to this issue, I've found that a lot of people have similar problem.
    This could be huge help. Thank you!
     
    mcroswell, ModLunar and ihgyug like this.
  4. ihgyug

    ihgyug

    Joined:
    Aug 5, 2017
    Posts:
    194
    Manually using Animator.WriteDefaultValues() before turning the gameobject off seems to work to avoid this bug. I hope there will be a built-in way to do it tho.
     
    mcroswell, krzys_h, ModLunar and 2 others like this.
  5. happysunshineandthepoweroffriendship

    happysunshineandthepoweroffriendship

    Joined:
    Mar 20, 2018
    Posts:
    9
    This also solves the problem of bugged default positions that happens when using the Apply Anim Overrides method as described here

    https://docs.unity3d.com/ScriptReference/AnimatorOverrideController.html
     
  6. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    I too, have suffered with this bug. However, I found some news on it. I tried all sorts of magic and trickery with Playables, AnimationStreams, and nothing helped 100%. However.. the answer was simpler than I thought... I think this is only available in Unity 2018.2+ or so. So what I found is:


    SOLUTION:

    Two parts: (you need to do both)
    Solution Part 1:
    Set animator.keepAnimatorControllerStateOnDisable to true. This does 2 things:
    1.1. When the Animator is re-enabled again, the state machine will resume where it last left off.
    1.2. HOWEVER, IT ALSO "FIXES" THE DEFAULT VALUES!! It keeps them the same default values that they were in the VERY beginning of the game.

    Therefore, set animator.keepAnimatorControllerStateOnDisable = true as early as possible. During Awake() worked for me.

    Solution Part 2: Due to the side-effect of animator.keepAnimatorControllerStateOnDisable resuming the state machine where it last left off, there is the only last potential issue remaining: The object may resume in an unwanted state (animation-wise).

    To fix this, you can create 0-duration transition in the Unity editor from Any State to your chosen default state that you would like the object to return back to upon re-enabling, with a trigger parameter condition.
    Then in your MonoBehaviour, call animator.SetTrigger("Reset") or animator.SetTrigger(ResetId) during OnEnable() to immediately set the state machine back to the initial animation state.

    --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---

    I was also wondering:
    Does this work with Root Motion?
    ANSWER: Yes.
    When Root Motion is enabled, the root object's position and rotation's default values are changed to be updated with the object's new position and rotation. On the root of the hierarchy ONLY.
    The scale is not counted in this, and keeps its original default value.

    So, If you want the object to respawn at its original position/rotation, you'll have to use Awake and OnEnable again for that, but the values are not locked, since the Animator will only be moving the root using deltas from its current position/rotation.

    --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---

    TLDR; Do both of the following:
    Solution Part 1: animator.keepAnimatorControllerStateOnDisable = true during Awake().
    Solution Part 2: animator.SetTrigger("Reset") during OnEnable(). Requires a 0-duration transition from Any State to your chosen default state w/ trigger parameter "Reset".

    Code (CSharp):
    1.  
    2.        public void Awake() {
    3.            animator = GetComponent<Animator>();
    4.            animator.keepAnimatorControllerStateOnDisable = true; //!!! (Solution Part 1)
    5.        }
    6.  
    7.        public void OnEnable() {
    8.            animator.SetTrigger(ResetId); //!! (Solution Part 2)
    9.        }
    10.  
    EDIT: For your information, I was using Unity 2019.2.5f1 when I successfully got this to work!
    EDIT 2: It seems this solution is not enough if you are mixing custom PlayableGraphs with the Animator in certain cases. My guess is it seems the graph will need to be cautiously constructed and maintained in order to avoid the AnimatorControllerPlayable from possibly resetting the default values.
     
    Last edited: Jul 10, 2023
  7. flyingspringrol

    flyingspringrol

    Joined:
    Apr 4, 2015
    Posts:
    2
    Alright ran into this problem today. Super rough bug to work with to be honest, almost had to reanimate about 20 hours of work which would have sucked.
    Essentially my workaround was to have a default state of "alloff": which has a keyframe for every object in my hierarchy turning it off. My code runs like this.
    Code (CSharp):
    1.             anim.Play("off", 0, 0.0f);
    2.             anim.Update(0.0f);
    3.             curr_anim_idx++;
    4.             PlayCurrentAnimation();
    For every animation, I then keyframe all the objects I need to 'turn on' for the scene (generally 10-20).
    This solves the problem.

    I don't know what I'd do if complex states/rotations were being messed with: I think having some serialization method of running through and saving each scene with it's necessary default values would be the way I'd do it.
    Curious to see how unity devs fix this in the future: it's a tricky problem, but it really does seem like you could fix it with code that identified objects as 'animatable', and then prevents them from being dumped from memory and having their default values removed.
    Essentially in the animator, when you 'toggle off' an object, underneath the hood, in the animator code, it just turns off all it's renderers/scripts. Shouldn't be impossible to fix, but of course I don't work with the unity code base. Does seem like the mecanim system needs a refactor anyway to support more than just rigged animations :).
     
    ModLunar likes this.
  8. MrMagister

    MrMagister

    Joined:
    Jan 8, 2019
    Posts:
    9
    This topic most useful and live for discussion about Write Defaults. Thx @ModLunar for your solution about reseting animator when gameobject was disabled. But I have another problem/question.

    Imagine, that you have 4 states:

    #1 - Default empty with write defaults
    #2 - Change color for first image (with write defaults)
    #3 - Change color for second image (with write defaults)
    #4 - Change color for third image (without write defaults)

    And I start this states in next sequence:

    - from #1 -> #2 (change color for first image)
    - from #2 -> #3 (change color for second image, first image color reset)
    - from #3 -> #1 (reset colors for both images)
    - from #1 -> #4 (change color for third image AND happens "magic" for me: colors for first 2 images return back)

    So, as I understand, when you go to state without write defaults, it's restore properties from ALL last frames of all animations, that you called before. Is it okay? I thought, that if you go to state without write defaults, it just not reseting to defaults and just keep values for properties, that this animation not using, AND NOT restoring ALL OTHER properties from ALL called states. Is it possible to fix somehow?

    Gif illustrate my example.

     
    Last edited: Oct 23, 2019
  9. MrMagister

    MrMagister

    Joined:
    Jan 8, 2019
    Posts:
    9
    Nobody can help?
     
  10. Moe_Baker

    Moe_Baker

    Joined:
    Oct 22, 2017
    Posts:
    36
    Came across this problem and this seems like the best solution, can you perhaps share some guidelines for that code or maybe share it? That would be very awesome.
     
  11. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    From my experience in Unity 2020.1.15f1, this bug is still here.

    @Mecanim-Dev Any update on the bug report or bug itself?
     
    mcroswell likes this.
  12. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Yet another surpize...
     
  13. mcroswell

    mcroswell

    Joined:
    Jan 6, 2010
    Posts:
    79
    I've been trying to animate some Text Mesh Pro (TMP) settings and simply want to control the attributes after the animation finishes. That is, it (the Animator) transitions into another animation which doesn't use those attributes. I was hoping to use Write Defaults flag so then I could use code (normal MonoBehaviour) to modify those attributes.
     
    Last edited: Jan 15, 2021
    ModLunar likes this.
  14. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    In unity 2019 Lts:

    private void Awake
    {
    GetComponent<Animator>().keepAnimatorControllerStateOnDisable = true;
    }

    keeps original default values.
     
    Whatever560 likes this.
  15. Metall_East

    Metall_East

    Joined:
    Feb 6, 2019
    Posts:
    2
    Write defaults still don't work in 2020.1.10f1 or am i doing something wrong?
     
  16. ASymShade

    ASymShade

    Joined:
    May 18, 2018
    Posts:
    6
    Using keepAnimatorControllerStateOnDisable with an entry state with no animation and Write Defaults set to true seems to be able to prevent this issue in recent Unity versions (previously, we would make other animations forcibly reset these changes if necessary). However, it is rather strange that this setting is exposed in the inspector in Debug Mode only (https://forum.unity.com/threads/ani...ndisable-should-be-shown-in-inspector.875872/). Is there some risk involved with using it that means it isn't usually intended to be set by animators in the way that the base Write Defaults is?
     
  17. GlassHaven

    GlassHaven

    Joined:
    Oct 21, 2021
    Posts:
    1
    had a similar issue... fixed it by turning WD on then off again while in playmode... fixed it instantly. why does unity do this to me...

    Unity 2019.4.31f1
     
  18. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    224
    I tried a few of the suggested solutions/workarounds. Note that I'm using an Animator with a single int parameter "AnimationID" which leads to various states with their own animations. I'm also using an animator override controller and not all animations are overridden, leaving many states playing the base animations, which are mostly empty (hence the importance of writing defaults).

    A. Setting Animator.keepAnimatorControllerStateOnDisable = true on Awake (or in Inspector in Debug mode, as it's not visible in Normal mode) didn't work for me (Unity 2022) (I kept having the bad defaults stored from animation played at deactivation time).

    B. CrossFade didn't work for me in:

    Code (CSharp):
    1.     anim.CrossFade("Default State", 0f);
    2.     anim.Update(0f);
    3.     anim.Update(0f);
    4.     gameObject.SetActive(false);
    However, since I'm working with parameters instead, I replaced CrossFade with code setting my default parameter value:

    Code (CSharp):
    1.  
    2. // Animator-specific values
    3. private static readonly int AnimationIdHash = Animator.StringToHash("AnimationID");
    4. public const int DefaultAnimationID = 0;
    5. // In method that deactivates game object
    6.     m_Animator.SetInteger(AnimationIdHash, DefaultAnimationID);
    7.     anim.Update(0f);
    8.     anim.Update(0f);
    9.     gameObject.SetActive(false);
    and it worked. As they say, the two Updates were needed, and a single Update(0.1f) was not enough.

    However, as Sycobob said, even if Unity dev team prefers to clear animator defaults when disabled for memory reasons, having an option to keep the defaults in memory seems a reasonable compromise. I'm not sure what Animator.keepAnimatorControllerStateOnDisable is really intended for (it seems to be about preserving the last animator state, but from experience Animator.enabled = false as always paused animations preserving states, so I'm not sure what it's about, and it's not exposed in Inspector Normal mode, but we could have an Animator.keepAnimationDefaultsOnDisable exposed in Inspector Normal mode with a tooltip explaining it will keep the original defaults since first Awake/OnEnable, and should be used in combination with Animation asset Write Defaults to revert all modified properties to their Entry values.

    I personally think this should be default, but if you insist on memory performance and want to preserve old behavior for compatibility with existing projects, then an option should be enough. People stumbling on this issue will find this thread, then find the new property's name once you've implemented it, and can even define an Animator Preset to always set it to true in their projects.
     
    Whatever560 and OndrejP like this.
  19. OndrejP

    OndrejP

    Joined:
    Jul 19, 2017
    Posts:
    304
    Last edited: Mar 21, 2023
    Whatever560 and pep_dj like this.
  20. Whatever560

    Whatever560

    Joined:
    Jan 5, 2016
    Posts:
    519
    Although I'm not sure to fully follow what it does when enabled or not.
    Basically, by default when enabling/disabling a GO everything resets. If I enable this, do I have "no consequences" when enabling/disabling GO with an animator. So I can disable an object rendering and enable it without loosing it states/parameteres/initial DefaultValues at all ?
     
  21. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    I now have an opposite problem. If I disable WriteDefaults flag in state A, and have other state B which animate some property. What property is reseted to default in state A ((.
    Seems like that flag works as it is always true. Unity 2021.3.14f1
     
  22. drazuerg

    drazuerg

    Joined:
    Jan 31, 2014
    Posts:
    62
    Hello !

    I have had the same issue as well. Interstingly, I've used Unity 2021.3.17 for several months and never had an issue with these default values when disabling / renabling again.
    But then I had to switch to a newer version of Unity (2021.3.32 currently), and the issues appeared ...
    After a lot of investigations, it seems that what I found as "normal" was actually considered a "bug" running from 2021.3.16 to 2021.3.20. It was "fixed" in 2021.3.20 ( Editor: Fixed a regression that changed the default behaviour of animators on disabled. (UUM-27229) ) and now I had to redo tons of animations, adding "default" keyframes all over the place, which is both time consuming and very hard to debug. I recently opened a thread before discovering this one, with no answer, hoping I would get more luch here.

    I'm not sure I fully grasped all workarounds descibed in this thread, but short questions :
    • Is this now supposed to be fixed for good ?
    • If so, how is "Write default" really supposed to work ?