Search Unity

Current animator state name?

Discussion in 'Animation' started by Rodolfo-Rubens, Jun 9, 2015.

  1. jhughes2112

    jhughes2112

    Joined:
    Nov 20, 2014
    Posts:
    98
    Hey all. Waaaay up near the top was a runtime solution using reflection by @alpha-cast (which is awesome, thanks!). If you want to just collect all the state names, whether full or short, and do so in the editor, here's a quick inspector that shows you how to do it. Just drag the animator controller onto the inspector field and it'll collect all the states in your whole animator. What you do with it from this point on is up to you. (Obviously, change the name of MyObjType to a script you actually have somewhere... doesn't matter what.)

    Hope this helps someone!

    JH

    Code (CSharp):
    1. [CustomEditor(typeof(MyObjType))]
    2. class AnimInspector : Editor
    3. {
    4.     public UnityEditor.Animations.AnimatorController controller;
    5.    
    6.     private HashSet<UnityEditor.Animations.AnimatorStateMachine> statesVisited = new HashSet<UnityEditor.Animations.AnimatorStateMachine>();
    7.     private HashSet<string> stateNames = new HashSet<string>();
    8.  
    9.     private void RecurseStates(UnityEditor.Animations.AnimatorStateMachine asm)
    10.     {
    11.         statesVisited.Add(asm);
    12.  
    13.         foreach (var states in asm.states)  // collect state names, ignore collisions
    14.         {
    15.             stateNames.Add(states.state.name);
    16.         }
    17.         foreach (var childState in asm.stateMachines)
    18.         {
    19.             if (statesVisited.Contains(childState.stateMachine)==false)
    20.                 RecurseStates(childState.stateMachine);
    21.         }
    22.     }
    23.  
    24.     public override void OnInspectorGUI()
    25.     {
    26.         base.OnInspectorGUI();
    27.         UnityEditor.Animations.AnimatorController newController = (UnityEditor.Animations.AnimatorController)EditorGUILayout.ObjectField(new GUIContent("Animator Controller"), controller, typeof(UnityEditor.Animations.AnimatorController), false);
    28.         if (controller != newController)
    29.         {
    30.             controller = newController;
    31.  
    32.             // reset and retraverse all states
    33.             statesVisited.Clear();
    34.             stateNames.Clear();
    35.             if (controller!=null)
    36.             {
    37.                 foreach (var layer in controller.layers)
    38.                 {
    39.                     RecurseStates(layer.stateMachine);
    40.                 }
    41.             }
    42.         }
    43.  
    44.         foreach (string stateName in stateNames)
    45.         {
    46.             EditorGUILayout.LabelField(stateName);
    47.         }
    48.     }
    49.  
    50. }
    51.  
     
    MikeAtOO and andreyefimov2010 like this.
  2. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    2,645
    Nice editor script . However your recommendation to use a reflection based method as though it is the cat's meow is not a good recommendation. Game devs should always have performance in mind and throw out their MSDN practices and work with the engine which marshalls C#..a scripting language.. into C++ at compile time. Reflection is ssslllooowwwww. https://jacksondunstan.com/articles/2972
     
    stonstad and Unplug like this.
  3. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    707
    It's an editor script. Its runtime performance doesn't matter because it doesn't exist at runtime, that's why its called an editor script. And if you actually read the script, it's not even using any reflection.

    It seems like a rather clunky solution to me, but an editor script is exactly the place where inefficient operations like this and reflection should be performed.
     
  4. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    250
    Im not 100% sure what you mean with this comment, but Unity doesn't compile C# into C++ at compile time unless you use IL2CPP (for example for mobile) but a "normal" desktop game is compiled as C# (IL) dll and the engine code itself (which is C++) calls the C# code at runtime.
     
  5. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    2,645
    That is why it was said good editor script but pointing to a runtime solution was not optimal performance wise. I would use this script to gather the stateNames into a string builtin array and creating a stateBool matching bool builtin array. By passing the name of the next state into a loop and seeking the match of either bool or string as is suited to your triggering method you have only one current string stateName which stateNames index matches the index of the bool which is true and the loop turns all others false. Superfast and bulletproof.
     
  6. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    2,645
    Thanks for clarifying the jargon. I admit. I am not a jargon guy. I am a programmer/technical artist/procedural animator. More like the backyard hotrod mechanic whose bondoed jalopy will outperform the factory trained mechanics every time in the quarter mile. .. Fact remains..C# is not the final leg of the journey before display..so whatever is in C# to my sensibilities is still scripting the C++ when all is said and done per frame. The less of this you are pipelining the better the performance.
     
  7. FireGreeks

    FireGreeks

    Joined:
    Jan 14, 2017
    Posts:
    4
    For those still searching for a solution years after this, after some hours of thinking, I came up with this code/script :

    Code (CSharp):
    1. static public T GetInDictionnary<T>(List<T> list, System.Func<T, bool> method)
    2.     {
    3.         T toReturn = default(T);
    4.         foreach (T t in list)
    5.         {
    6.             if (method(t))
    7.                 toReturn = t;
    8.         }
    9.         return toReturn;
    10.     }
    NOTE: This code can also be used for any List or Dictionary with any key type...

    List<T> list
    is a list with all the names of the
    AnimationStates 
    (I know this work but its just copy pasting, takes a couple of minutes). This function is called like this :

    Code (CSharp):
    1. GetInDictionnary<string>(ListofAllStates,(name) => {return animatorStateInfo.IsName(name); });
    Using the lambda expression, you check through every existing state if the name of the state is equal to the current
    AnimationState
    and then returns
    name
    if true.

    This is a pretty good workaround, just needs a bit of copy-pasting... Unity is great but come on, it just takes one line of code to implement this. There must be a reason they didn't do it. But, here you go, you're welcome...
     
  8. wechat_os_Qy04nNLpF6XvuRptWsvltyzug

    wechat_os_Qy04nNLpF6XvuRptWsvltyzug

    Joined:
    Feb 12, 2019
    Posts:
    1
    to 2d project,here is a simple but not universal way:
    animator takes effect by changing the spriterenderer.sprite (on the same game object).the name of sprite is like “xxx_01”,"xxx" is texture name.so if your state names just equal the names of texture,it could be get while game running.
    Code (CSharp):
    1. public string get_anime_name_now()
    2.     {
    3.         string sp=gameObject.GetComponent<SpriteRenderer>().sprite.name;
    4.         string[] subs=sp.Split('_');
    5.         return sp.Substring(0,sp.Length-subs[subs.Length-1].Length-1);
    6.     }
     
  9. leegod

    leegod

    Joined:
    May 5, 2010
    Posts:
    1,825
    How to use this code?

    Missing some specific apply example...

    How to replace [ListofAllStates]?
     
  10. leegod

    leegod

    Joined:
    May 5, 2010
    Posts:
    1,825
    How to use actually this script? I got errors

    The type or namespace name 'HashSet<>' could not be found
     
  11. R1PFake

    R1PFake

    Joined:
    Aug 7, 2015
    Posts:
    250
    You need the proper using, I think it's System.Collections.Generic for HashSet<>.

    Also there is no need to continue the loop if you already found the item, just return inside the if instead of setting a variable, or at least add a break.

    Or you can just add a using for System.Linq and use the FirstOrDefault extension method, it does the same thing.
     
  12. leegod

    leegod

    Joined:
    May 5, 2010
    Posts:
    1,825
    So how to use this script? What the script name should be?
     
  13. leegod

    leegod

    Joined:
    May 5, 2010
    Posts:
    1,825
    So it just shows animator's states name, but when I play game, it disappears. And still how to know what state is currently playing?
     
  14. dibdab

    dibdab

    Joined:
    Jul 5, 2011
    Posts:
    881
    I was under the impression that
    animator.GetCurrentAnimatorStateInfo (0).IsName (_stateN) has an impact on performance just like GetComponent<>()

    made a test with 100 animated characters and I don't see any change in the stats
    if the script is On or Off
    Code (CSharp):
    1. void Update () {
    2.    if (isOn) {
    3.             if (animator.GetCurrentAnimatorStateInfo (0).IsName ("idle")) isIdle = true;
    4. }}
    looks like as if the info is already there and makes no difference if you access it or not.
    is it true?
    (though string compare probably takes some time)
     
    Last edited: Aug 20, 2019
  15. alti

    alti

    Joined:
    Jan 8, 2014
    Posts:
    31
    print(GetCpmponent<Animator>().GetCurrentAnimatorClipInfo(0)[0].clip.name);
     
  16. GuardHei

    GuardHei

    Joined:
    Feb 10, 2018
    Posts:
    55
    It is the name of the animator state, not the clip
     
  17. alti

    alti

    Joined:
    Jan 8, 2014
    Posts:
    31
    It will at least give you the clue you otherwise need. Otherwise you can make life super simple and just name the animation and the clip the same thing.
     
  18. stonstad

    stonstad

    Joined:
    Jan 19, 2018
    Posts:
    118
    Everyone has their own implementation... My requirements are as follows:
    • Be performant. Minimal run-time cost (i.e. no SendMessage, no runtime reflection, no GetSomething() invocations)
    • Allow me to use strings or hashes based on my performance need
    • Tell me if missed declaration of a state during editor playback, but not runtime
    Usage:
    Code (CSharp):
    1. private void Start()
    2. {
    3.    _Animator = GetComponent<Animator>();
    4.    var listeners = Animator.GetBehaviours<StateMachineBehaviorListener>();
    5.    foreach (StateMachineBehaviorListener listener in listeners)
    6.    {
    7.       listener.OnStateEnterEvent += OnStateEnter;
    8.       listener.OnStateExitEvent += OnStateExit;
    9.    }
    10. }
    11.  
    12. private void OnStateEnter(int stateHash, string stateName)
    13. {
    14.    Debug.Log("ENTER " + stateName);
    15. }
    16.  
    17. private void OnStateExit(int stateHash, string stateName)
    18. {
    19.    Debug.Log("EXIT " + stateName);
    20. }
    Configuration: Add StateMachineListener.cs as an animation behavior and expose states by name. A single class implementation may be assigned to multiple states.

    StateMachineListener.cs...
    Code (CSharp):
    1. static StateMachineBehaviorListener()
    2. {
    3.    AddState("Grounded");
    4.    AddState("Pickup");
    5.    AddState("Horizontal Chop");
    6. }

    Gist w/ files: https://gist.github.com/stonstad/7bdd805d52f43d6880cc073a48757aa9

    The reflection code executes to warn if a state is missing only during Editor playback. Accolades to @alpha-cast.

    If you see an optimization or bug let me know and I'll update the gist. I understand there are so many ways to handle this but the best way is for the Unity team to listen and make APIs more developer friendly.

    @Unity dev team -- adding a collection with hashed look-up for a state name is never going to be a deal breaker in terms of memory usage or performance. It's on you to make humane APIs for your developers, and judging by the size of this thread and others, you dropped the ball here. Please fix it.
     
    Last edited: Nov 10, 2019
    andreiagmu and Bezoro like this.
  19. Mathius123

    Mathius123

    Joined:
    May 21, 2018
    Posts:
    6
    Came here simply to post that this asset works like a charm. It places an SO under the animator controller of the game object that contains the Animator you attached the Animator Handler script component to and then you can simply call:

    animator.GetCurrentStateName(layerIndex) and it returns the actual animation state name as a string. Perfect!
     
    andreiagmu likes this.