Animation Override Controller [Unity 5.4]

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:

    /// <summary>
    /// Sub in real animations for stubs
    /// </summary>
    /// <param name="animator">Reference to animator</param>
    public void SetCurrentAnimation(Animator animator, string prevAttack)
    {
        RuntimeAnimatorController myController = animator.runtimeAnimatorController;
        AnimatorOverrideController animatorOverride = new AnimatorOverrideController();
        animatorOverride.runtimeAnimatorController = myController;

        animatorOverride[prevAttack] = animClip;
        animator.runtimeAnimatorController = animatorOverride;
    }

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:

    line 7:
    Cannot nest AnimatorOverrideController 'Player' with ''.
    UnityEngine.AnimatorOverrideController:set_runtimeAnimatorController(RuntimeAnimatorController)

    line 9:
    Could not set Runtime Animator Controller. The controller  is an AnimatorOverrideController with no AnimatorController to override.
    UnityEngine.Animator:set_runtimeAnimatorController(RuntimeAnimatorController)

Is this not supported anymore?

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

public void SetCurrentAnimation(Animator animator, string prevAttack)
{
    RuntimeAnimatorController myController = animator.runtimeAnimatorController;

    AnimatorOverrideController myOverrideController = myController as AnimatorOverrideController;
    if(myOverrideController != null)
        myController = myOverrideController.runtimeAnimatorController;

    AnimatorOverrideController animatorOverride = new AnimatorOverrideController();
    animatorOverride.runtimeAnimatorController = myController;
    animatorOverride[prevAttack] = animClip;
    animator.runtimeAnimatorController = animatorOverride;
}
1 Like

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

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

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.

2754411–198690–AnimStuff.cs (2.62 KB)

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

if (myCurrentOverrideController["Stub"] == animClip)

Resolved that by removing

&& 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!

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

Thanks @SirGiv - your answer Change Mechanim motions on the fly? - Questions & Answers - Unity Discussions sorted it for me, i had to add in AnimationClip animClip to the function but works great!

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)?

The code is ready, we are in the process of merging it to 5.6

2 Likes

:slight_smile:
Good news.
Managed to solve my issues with the animator btw. Just needed to take time to figure it out.

Hey Alvarezmd90, could you post your solution here?

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.

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:

Debug.Log("Original controller = " + animator.runtimeAnimatorController);
AnimatorOverrideController overrideController = animator.runtimeAnimatorController as AnimatorOverrideController;
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!

still buggy . This is pretty important part of the Animator…

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?

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.

 public void OverrideAnimationClip(UserEntityView aView)
    {
        Animator animator = aView.Animator;
        AnimatorOverrideController myOriginalController;
        AnimatorOverrideController myCurrentOverrideController = animator.runtimeAnimatorController as AnimatorOverrideController;
        if (myCurrentOverrideController != null)
        {
            myOriginalController = myCurrentOverrideController.runtimeAnimatorController as AnimatorOverrideController;
        }
        else
        {
            myOriginalController = animator.runtimeAnimatorController as AnimatorOverrideController;
        }
// this is a cs class, not a mono or scripbable...
// I know I should move this to an init function, but whatever :smile:
        if (overrideController == null)
        {
            overrideController = new AnimatorOverrideController(animator.runtimeAnimatorController);
            animator.runtimeAnimatorController = overrideController;
        }

        if (clipOverrides == null)
        {
            clipOverrides = new AnimationClipOverrides(overrideController.overridesCount);
            overrideController.GetOverrides(clipOverrides);
        }

        if (_idle != null)
        {
            clipOverrides["Idle"] = _idle;
        }
        else
        {
            if(myCurrentOverrideController["Idle"] != null)
            {
                clipOverrides["Idle"] = myCurrentOverrideController["Idle"];
            }
            else
            {
                clipOverrides["Idle"] = myOriginalController["Idle"];
            }
        }

        if (_run != null)
        {
            clipOverrides["Run"] = _run;
        }
        else
        {
            if (myCurrentOverrideController["Run"] != null)
            {
                clipOverrides["Run"] = myCurrentOverrideController["Run"];
            }
            else
            {
                clipOverrides["Run"] = myOriginalController["Run"];
            }
        }

        if (_auto != null)
        {
            clipOverrides["Auto"] = _auto;
        }
        else
        {
            if (myCurrentOverrideController["Auto"] != null)
            {
                clipOverrides["Auto"] = myCurrentOverrideController["Auto"];
            }
            else
            {
                clipOverrides["Auto"] = myOriginalController["Auto"];
            }
        }

        overrideController.ApplyOverrides(clipOverrides);
    }

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.

@LightStriker_1 this is a know issue that was fixed recently in 2018.2, it should be backported to previous version any time soon

Glad to hear that! Any idea how much it cost now per AnimationClip?