Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Having some issues with Animator Controller & Animator Override

Discussion in 'Animation' started by theMasky, Apr 1, 2021.

  1. theMasky

    theMasky

    Joined:
    May 8, 2015
    Posts:
    124
    So, the next story is somewhat long...

    It all started when I wanted to improve my workflow, so I look up on the internet for some tips. At that moment, I was designing a weapon system for a 3D environment. I wanted a reusable, easy to implement solution, one that'd let me create as many weapons as I wanted with just little effort. I think I found a good solution for the weapon itself (shooting, reloading, etc), at least for me. But...there was still something I could not figure it out: the animations. I also wanted a simple method to implement the animations.

    Later I found about Animator Override Controller. I knew they existed but I honestly never use it before.

    So, from what I've read, they're exactly the thing I was looking for. The docs says:
    After watching some tutorials, it seemed that I only needed to create a base controller with some default states (in my case, Idle, Attack, Reload), and the Override Controller. The last one will replace the default animations inside the default states with the weapon ones:

    table.png

    After watching this tutorial, it was clear to me (I guess?) that the function of the Override Controller was to take and use the logic from the base controller to reproduce the required animation clips. At least in the example shown in the video, there wasn't any code involve.

    So, let's take a look:
    chart.png

    Sorry for the bad paint drawing lol, but that's basically what I understood about the Override Controller. It is almost like using interfaces?...I mean, they aren't, but I guess you get the idea.

    So, I made my base controller, and the override one.

    This is how the animator component looks on editor:
    editor.png

    There's also my weapon script. The script takes a reference of the animator (at the moment I manually set it, for testing purposes), and triggers the events according to the current state of the weapon. Since the ScorpionOverridesPlayer is taking the logic from the base controller, I don't need to actually set up the base controller on the scene, isn't it?...

    Now, let's see the Animator set up:
    animator.png

    So...it kinda works. There are some weird things going on.

    First, I'm not sure if I actually need to make a transition back from the Any State node, 'cause I honestly don't remember doing it before, but for some reason whenever it gets to the DEFAULT_ATTACK or DEFAULT_RELOAD state, it never comes back to the DEFAULT_IDLE one. So...yeah.

    There's a trigger to perform the attack, and a boolean to perform the reload state (it makes sense, since the weapon is reloading for some amount of time, until it isn't). Let's ignore the DEFAULT_WALK state, since I haven't set it up yet.

    As I mentioned before, there are some issues. When I do the reload action (press the reload button), the animation starts repeating itself without finishing until it finishes. I know, that sounded weird, but let's take a look to the code:

    Code (CSharp):
    1. IEnumerator IReloadSequence()
    2.     {
    3.         _AnimatorController.SetBool(RELOAD_BOOL, true);
    4.  
    5.         yield return new WaitForSeconds(_AnimatorController.ClipLength("Scorpion_Reload"));
    6.  
    7.         _FX.AnimatorController.SetBool(RELOAD_BOOL, false);
    8.  
    9.         audioSource.PlayOneShot(reload);
    10.         currentAmmo = currentAmmo + maxAmmoRound;
    11.         if (currentAmmo > maxAmmoRound)
    12.         {
    13.             currentAmmo = maxAmmoRound;
    14.         }
    15.         yield break;
    16.     }
    I call this Coroutine though the method:

    public void Reload() { StartCoroutine(IReloadSequence()); }


    The extension method called ClipLength is one that I made to retrieve the current clip length (the animaton clip that's playing at that moment). This is the code (btw based on this post):

    Code (CSharp):
    1. public static float ClipLength(this Animator animator, string animationName) {
    2.         var length = animator.runtimeAnimatorController.animationClips.First(clip => clip.name == animationName).length;
    3.         return length;
    4.     }
    However, once I run the game, it seems that
    yield return new WaitForSeconds(_AnimatorController.ClipLength("Scorpion_Reload"));

    is completely ignoring the length of the animation clip, so it straight up jumps to
    audioSource.PlayOneShot(reload);

    and so on.

    I ended up making that method because
    _AnimatorController.GetCurrentAnimatorStateInfo(0).length;

    was giving me a very similar result, but maybe worse, since the animation wasn't even playing at all.

    So, my first theory is that the GetCurrentAnimatorStateInfo is getting the info from the base controller. If that's the case, the it would make total sense, since the DEFAULT_RELOAD state is technically empty.
    And, if that's the case, then how could I get the current animation clip lenght from the override controller? I guess that one way would be something very similar to the extension method ClipLenght, in which I specified using a string the name of the clip.

    Speaking about that extension method, I didn't want to use the string reference, since it completely ruins my generic implementation, but I couldn't find another way of doing it.

    So, yeah. I'm not sure about what to do now, since I've never been on this situation before.

    Any idea that doesn't involve just telling me "animator bad plz use my asset instead"? Because that's an answer I've seen a lot...
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,503
    That would be because it's the correct answer. The 5 page essay you just wrote about how hard it is to do something so basic is a pretty clear sign that Animator Controllers are a bad system and I don't know what other assets you've seen recommended, but this whole thing would be super easy with Animancer (link in my signature):
    Code (CSharp):
    1. animancer.Play(reloadClip).Events.OnEnd = () =>
    2. {
    3.     audioSource.PlayOneShot(reload);
    4.     currentAmmo = currentAmmo + maxAmmoRound;
    5.     if (currentAmmo > maxAmmoRound)
    6.     {
    7.         currentAmmo = maxAmmoRound;
    8.     }
    9. };
    No messing around with parameters and transition settings. No Animator Controllers. No couroutines. No magic strings. No inefficient Linq. Just simple code that tells it exactly what you want to do.
     
  3. theMasky

    theMasky

    Joined:
    May 8, 2015
    Posts:
    124
    I honestly can't afford your asset right now, so this answer doesn't help me that much. I guess I appreciate?
     
    entropicjoey1 likes this.
  4. FellowPlayer123

    FellowPlayer123

    Joined:
    Dec 23, 2016
    Posts:
    114
    Is there an option to exclude some animations from overriding in Animation Override Controller? It's ridiculous to have that each character had 1000 animations to override, while it only needs override ONLY ONE!!! It's just bad design.
     
  5. mslykhuis

    mslykhuis

    Joined:
    May 16, 2019
    Posts:
    2
    Did you ever figure this out? Having the same issue, and I'd love a simply way to only swap out a few animations at a time.
     
  6. theMasky

    theMasky

    Joined:
    May 8, 2015
    Posts:
    124
    Hi!

    Not @FellowPlayer123, but I would simply put the same animations again on the unused override slots. That is, for example:

    Kick_base -> Kick_override
    Punch_base -> Punch_base (this one remains the same)

    Hacky? Absolutely, but is either that, or completely redesign your animation flow, which I understand can be pretty daunting at some point.
     
  7. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    856
    Yeah, I don't remember the details exactly but I had to write a series of editor scripts a few year back that effectively automated all of this stuff so that I wouldn't have to go through and do it by hand with hundreds of sprites. And my takeaway was... the Animator is terrible.

    That being said there is one gotcha I noticed in your first post. When you are trying to get access to an animation clip that is being overridden you don't access the clip assigned to the state you are interested in. Oh no that would be simple an make too much sense. You have to query an override list of some kind. The details are super fuzzy but I recall need to get the list of overrides from the AnimatorOverrideController.GetOverrides() and then using some special method to query the ACTUAL motion that is used by the AnimationClip. Something like GetEffectiveMotion I think? It gets even more convoluted when using layers. Something like layer.GetOverrideMotion or somesuch nonese. God this brings back some bad memories.
     
  8. FellowPlayer123

    FellowPlayer123

    Joined:
    Dec 23, 2016
    Posts:
    114
    No, I haven't figured it out. I hope someone from Unity created some hot fix to change this design.
     
  9. mslykhuis

    mslykhuis

    Joined:
    May 16, 2019
    Posts:
    2
    Thanks for the reply. I couldn't find anything, so I ended up writing my own Animator Override script that only overrides desired animations. A lot of extra work, but it seems to be working. Weird that this doesn't already exist.
     
  10. FellowPlayer123

    FellowPlayer123

    Joined:
    Dec 23, 2016
    Posts:
    114
    Wanna share that script? That would be helpful.