Search Unity

Animation Override Controller [Unity 5.4]

Discussion in 'Animation' started by SirGive, Aug 11, 2016.

  1. SirGive

    SirGive

    Joined:
    Sep 27, 2010
    Posts:
    384
    I just upgraded to 5.4. I am having some problems with my animation override controller code. In 5.3, I was able to do manual animation controller overrides with this code:

    Code (CSharp):
    1.     /// <summary>
    2.     /// Sub in real animations for stubs
    3.     /// </summary>
    4.     /// <param name="animator">Reference to animator</param>
    5.     public void SetCurrentAnimation(Animator animator, string prevAttack)
    6.     {
    7.         RuntimeAnimatorController myController = animator.runtimeAnimatorController;
    8.         AnimatorOverrideController animatorOverride = new AnimatorOverrideController();
    9.         animatorOverride.runtimeAnimatorController = myController;
    10.  
    11.         animatorOverride[prevAttack] = animClip;
    12.         animator.runtimeAnimatorController = animatorOverride;
    13.     }
    I have an override controller attached to the object because some animations use the above method and some use mechanim ui. Right after upgrading, I now get this:

    Code (CSharp):
    1.     line 7:
    2.     Cannot nest AnimatorOverrideController 'Player' with ''.
    3.     UnityEngine.AnimatorOverrideController:set_runtimeAnimatorController(RuntimeAnimatorController)
    4.  
    5.     line 9:
    6.     Could not set Runtime Animator Controller. The controller  is an AnimatorOverrideController with no AnimatorController to override.
    7.     UnityEngine.Animator:set_runtimeAnimatorController(RuntimeAnimatorController)
    Is this not supported anymore?
     
    Last edited: Aug 11, 2016
  2. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    yes this is still supported, but you cannot nest anymore AnimatorOverrideController. We had a few performance bug when users were deeply nesting OverrideController like you did but weren't aware.

    So my guess is that when you call RuntimeAnimatorController myController = animator.runtimeAnimatorController;
    there is already a AnimatorOverrideController in animator.runtimeAnimatorController.
    You need to support this case like this

    Code (CSharp):
    1. public void SetCurrentAnimation(Animator animator, string prevAttack)
    2. {
    3.     RuntimeAnimatorController myController = animator.runtimeAnimatorController;
    4.  
    5.     AnimatorOverrideController myOverrideController = myController as AnimatorOverrideController;
    6.     if(myOverrideController != null)
    7.         myController = myOverrideController.runtimeAnimatorController;
    8.  
    9.     AnimatorOverrideController animatorOverride = new AnimatorOverrideController();
    10.     animatorOverride.runtimeAnimatorController = myController;
    11.     animatorOverride[prevAttack] = animClip;
    12.     animator.runtimeAnimatorController = animatorOverride;
    13. }
     
    tomjycUnity likes this.
  3. SirGive

    SirGive

    Joined:
    Sep 27, 2010
    Posts:
    384
    Unfortunately that change doesn't solve my problem. The exceptions are gone, but the animations are not subbed in. I don't know if it makes a difference, but that was how I was injecting animation clips that were not in mechanim. Is there a better way to do that?

    Edit:
    Rolling back to 5.3.6 to keep productive. I kept the same change as above, and even then the animations are not subbed beyond the very first one
     
    Last edited: Aug 12, 2016
  4. SirGive

    SirGive

    Joined:
    Sep 27, 2010
    Posts:
    384
    I'm still really confused. The sample you posted casts the runtime animator controller to an override controller and then assigns is to the same controller that is overwritten. Does that RuntimeAnimtorController property differ from the AnimatorOverrideController one?

    Even if this is the case, the animations are not overriding. This is preventing us from upgrading to 5.4. Is there a bug in your sample or am I doing something wrong?

    Edit: Submitted a bug. Case number is 823450
     
    Last edited: Aug 16, 2016
  5. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Hi SirGive

    So I did take a look at your case
    Here an updated version of your script with a workaround

    Like I said you were nesting your controller which is not supported anymore for performance reason.
    on the first frame that you press either 1,2 or 3 you are creating a new overridecontroller which you set in
    animator.runtimeAnimatorController, and then on the next frame that you press 1,2 or 3 you fetch the same overridecontroler from animator.runtimeAnimatorController and override it with another override.
    After a few thousand frame if the user did switch a lot from walk to run to jump you endup with a many nested override which can cause performance regression because of this nesting

    You absolutly need to get the orignal controller and create a new override from this controller, so either keep a reference to the orignal controller in your script or fetch it like I did.

    We would like to fix this issue in 5.4. You shouldn't need to recreate a new override each time you should be able to reuse the same one and only swap the clip that you want.
     

    Attached Files:

  6. SirGive

    SirGive

    Joined:
    Sep 27, 2010
    Posts:
    384
    Man, that did the trick. Was pulling my hair out trying to figure it out. My last issue was not overriding when already overridden:

    Code (CSharp):
    1. if (myCurrentOverrideController["Stub"] == animClip)
    Resolved that by removing

    Code (CSharp):
    1. && animator.GetCurrentAnimatorStateInfo(0).IsName("Move")
    This condition wasn't need for me. I'll update my answer on unity answers to not mislead anyone as well.

    Thanks, I really appreciate it!
     
    Last edited: Aug 17, 2016
  7. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Yeah, it a little bit confusing I agree, GetCurrentAnimatorStateInfo(0).IsName("Move") work with State name but the override controller work with animation clip name.

    Like I said there is a few hiccup with the overidecontroller but we are a looking at this to fix all of them for 5.4
     
  8. Gwom

    Gwom

    Joined:
    Aug 5, 2015
    Posts:
    21
  9. Patrascu

    Patrascu

    Joined:
    Jan 20, 2016
    Posts:
    59
    Any more news on swapping animation clips on the AnimatorOverrideController? Is it possible to do without resetting the Animator state machine (active state, paramaters etc)?
     
  10. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    The code is ready, we are in the process of merging it to 5.6
     
    Patrascu and Alvarezmd90 like this.
  11. Alvarezmd90

    Alvarezmd90

    Joined:
    Jul 21, 2016
    Posts:
    151
    :)
    Good news.
    Managed to solve my issues with the animator btw. Just needed to take time to figure it out.
     
  12. Patrascu

    Patrascu

    Joined:
    Jan 20, 2016
    Posts:
    59
    Hey Alvarezmd90, could you post your solution here?
     
  13. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    So all the new stuff for animator overide controller is in 5.6.0b2, it should no longer reset the statemachine when you change a clip.

    there is a few API change:
    • Animation: Deprecated AnimatorOverrideController.clips.
    • Animation: Added AnimatorOverrideController.GetOverrides and AnimatorOverrideController.ApplyOverrides.
    If you are using AnimatorOverrideController.clips I would suggest you to update your script to the new API GetOverrides/ApplyOverrides as it allocation free and ApplyOverrides send only one notification to the animator, the old API .clips was sending one notification for each changed clips.
     
  14. Helipork

    Helipork

    Joined:
    Mar 19, 2015
    Posts:
    2
    Hello guys,

    I've been having issues with animation overriding for some time when trying to switch to Unity's most recent versions (was using 5.3.4 up until now, where those nested overrides were still allowed).

    Now, whenever I try to apply the solutions found for SirGive to my case, I'm faced with the same issue: casting my animator's RuntimeAnimatorController as an AnimatorOverrideController gives me Null.

    For example:

    Code (CSharp):
    1. Debug.Log("Original controller = " + animator.runtimeAnimatorController);
    2. AnimatorOverrideController overrideController = animator.runtimeAnimatorController as AnimatorOverrideController;
    3. Debug.Log("Override controller = " + overrideController);
    This code first displays "Original controller = Character (UnityEngine.AnimatorController)", and then "Override controller = " in the console.

    I am now testing 5.6.0b4 (tested on 5.4 and 5.5 before that), and I still can't manage to properly get rid of the nesting and still have my overridings work.

    It would be great if someone could help me find a solution for that...

    Thanks for your time!
     
  15. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    still buggy . This is pretty important part of the Animator..
     
  16. Bunzaga

    Bunzaga

    Joined:
    Jan 9, 2009
    Posts:
    202
    Is there an example of the 5.6.x working with layers? Or does the example in the new documentation for this take layers into consideration?

    overrideController.ApplyOverrides(clipOverrides);

    For example, if I have a Left Hand override vs Right Hand, or full body, etc. Do I need to somehow specify which layer I want to apply it to?
     
  17. Bunzaga

    Bunzaga

    Joined:
    Jan 9, 2009
    Posts:
    202
    Hey, I got this working for my situation, not sure about layers yet though... basically, I have two WeaponInstances: Left and Right, but not sure if I'll have one, both, or neither, etc. So I had to figure out a way to see if there was a 'Left' animation, if not, then just keep the 'Right' animation, but if there is no 'Right' animation, or 'Left', then just keep the base.

    Here's what I came up with, using the AnimationClipOverrides class from the 5.6.x documentation.

    Code (CSharp):
    1.  public void OverrideAnimationClip(UserEntityView aView)
    2.     {
    3.         Animator animator = aView.Animator;
    4.         AnimatorOverrideController myOriginalController;
    5.         AnimatorOverrideController myCurrentOverrideController = animator.runtimeAnimatorController as AnimatorOverrideController;
    6.         if (myCurrentOverrideController != null)
    7.         {
    8.             myOriginalController = myCurrentOverrideController.runtimeAnimatorController as AnimatorOverrideController;
    9.         }
    10.         else
    11.         {
    12.             myOriginalController = animator.runtimeAnimatorController as AnimatorOverrideController;
    13.         }
    14. // this is a cs class, not a mono or scripbable...
    15. // I know I should move this to an init function, but whatever :D
    16.         if (overrideController == null)
    17.         {
    18.             overrideController = new AnimatorOverrideController(animator.runtimeAnimatorController);
    19.             animator.runtimeAnimatorController = overrideController;
    20.         }
    21.  
    22.         if (clipOverrides == null)
    23.         {
    24.             clipOverrides = new AnimationClipOverrides(overrideController.overridesCount);
    25.             overrideController.GetOverrides(clipOverrides);
    26.         }
    27.  
    28.         if (_idle != null)
    29.         {
    30.             clipOverrides["Idle"] = _idle;
    31.         }
    32.         else
    33.         {
    34.             if(myCurrentOverrideController["Idle"] != null)
    35.             {
    36.                 clipOverrides["Idle"] = myCurrentOverrideController["Idle"];
    37.             }
    38.             else
    39.             {
    40.                 clipOverrides["Idle"] = myOriginalController["Idle"];
    41.             }
    42.         }
    43.  
    44.         if (_run != null)
    45.         {
    46.             clipOverrides["Run"] = _run;
    47.         }
    48.         else
    49.         {
    50.             if (myCurrentOverrideController["Run"] != null)
    51.             {
    52.                 clipOverrides["Run"] = myCurrentOverrideController["Run"];
    53.             }
    54.             else
    55.             {
    56.                 clipOverrides["Run"] = myOriginalController["Run"];
    57.             }
    58.         }
    59.  
    60.         if (_auto != null)
    61.         {
    62.             clipOverrides["Auto"] = _auto;
    63.         }
    64.         else
    65.         {
    66.             if (myCurrentOverrideController["Auto"] != null)
    67.             {
    68.                 clipOverrides["Auto"] = myCurrentOverrideController["Auto"];
    69.             }
    70.             else
    71.             {
    72.                 clipOverrides["Auto"] = myOriginalController["Auto"];
    73.             }
    74.         }
    75.  
    76.         overrideController.ApplyOverrides(clipOverrides);
    77.     }
     
  18. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I've moved to the ApplyOverrides method, but I found the performance to be terrible, in the 100ms range range for a single call, with an extra 40ms on Animator.SetupControllerDataSet (called once for every animation?). For reference, our Animator has ~300 animations.
     
  19. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    @LightStriker this is a know issue that was fixed recently in 2018.2, it should be backported to previous version any time soon
     
  20. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Glad to hear that! Any idea how much it cost now per AnimationClip?