Search Unity

Get all states in Animation Controller

Discussion in 'Animation' started by h0nka, May 15, 2015.

  1. h0nka

    h0nka

    Joined:
    Apr 7, 2013
    Posts:
    109
    Hi,

    Is i possible to get a list all states in the animator FSM? I found this thread, but the method doesn't seem to be supported any longer. I need to know all states a given state can transition to even if it is not the current or next state of the FSM.

    Thanks!
     
    Last edited: May 15, 2015
  2. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Which version of unity are you using?
     
  3. h0nka

    h0nka

    Joined:
    Apr 7, 2013
    Posts:
    109
    Unity version 5.0.1f1.
     
    siddharthsivaraman likes this.
  4. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
  5. h0nka

    h0nka

    Joined:
    Apr 7, 2013
    Posts:
    109
    Thanks a lot!
     
  6. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    As it uses UnityEditor classes, it won't build :(
     
  7. Kerihobo

    Kerihobo

    Joined:
    Jun 26, 2013
    Posts:
    65
    :'(
     
  8. MaDDoX

    MaDDoX

    Joined:
    Nov 10, 2009
    Posts:
    764
    Yes, probably for performance reasons. If you all you want is to get a hold of the existing states names, just store it in a List<String> or something. If you need an overriden AnimatorController in runtime, your best bet would be exporting the new AnimatorController as a new asset, using a custom editor.
     
  9. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789

    If this isn't possible at runtime, then I have a question:

    I have multiple units in my game. Some of them use an override controller as well.
    My goal is that when I modify the attack-speed of my unit, it should play the attack animation faster as well.

    Now I thought I could get the "Attacking" state from the animator, and then check what "Motion" (AnimationClip) is used, and then check how long it takes. And if it is longer than the time between attacks, I will scale it down.

    To scale it down I have a parameter set and the "speed" as parameter in both my states: "Attacking" and "Attacking_Alternate".

    I can modify how fast the attack is played, but I need to know how long each animation takes at runtime.
    I can't do if beforehand because different units have different animations for attack, so I have to access the motion in the attacking state.

    Is there anything I'm missing?
    How can I scale the attacking animation for each of my units into the given "attack speed" they have?
    For example one attack animation takes 0.7sec, but the unit can attack every 0.5sec, then I calculate 0,7/0,5 => 1.4x increase.

    What can I do? Is there some other way to solve this exact problem?
     
    theANMATOR2b likes this.
  10. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    dadude123 likes this.
  11. Sovogal

    Sovogal

    Joined:
    Oct 15, 2016
    Posts:
    100
    @Mecanim-Dev I'm still not completely clear about how to get the AnimatorController even in an editor script. I have a reference to the Animator component. Can I use this to obtain a reference to the AnimatorController so that I can inspect the state machine?
     
  12. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    If you have an animator instance you can do : animator.runtimeAnimationController;
    then you can cast that to the right type (OverrideAnimationController for example).
     
    Sovogal likes this.
  13. Sovogal

    Sovogal

    Joined:
    Oct 15, 2016
    Posts:
    100
    Ah, yes. I was able to do animator.runtimeAnimatorController as AnimatorController, and that worked.
     
  14. DDNA

    DDNA

    Joined:
    Oct 15, 2013
    Posts:
    116
    I'm running in this problem too... again... and I am about to write my second elaborate system to just simply know how long an animation will take to play. The problem is you don't tell unity to play the "Attack" Clip. You tell Unity to play "Attack State" and you have to somehow know that the "Attack" clip is going to be played.

    The next problem is that the only way to do this is to actually play the animation then poll the system on the next frame. That isn't always practical because usually the point where you play the animation is the point where you want to know what time it should play in, not the next frame. I tried to solve this by pumping the animator. Which seemed to work good for a long time, but I am having problems with it now.

    Here is what I have to do to play an animation

    public void BlendAnimationFixedTime(AnimHash animHash, double fixedBlendTime, double lengthInRealTime)
    {
    Animator.SetFloat(_speedParmID, 1.0f);
    Animator.CrossFadeInFixedTime((int)animHash, (float)fixedBlendTime, AnimLayer);
    Animator.Update(0);

    AnimatorClipInfo info;
    if (Animator.IsInTransition(AnimLayer))
    {
    info = Animator.GetNextAnimatorClipInfo(AnimLayer)[0];
    }
    else
    {
    AnimatorClipInfo[] cinfos = Animator.GetCurrentAnimatorClipInfo(AnimLayer);
    if (cinfos.Length == 0)
    return;
    info = cinfos[0];
    }

    _clipSpeed = info.clip.length / lengthInRealTime;


    if (_speedParmID.HasValue)
    Animator.SetFloat(_speedParmID, (float)(_clipSpeed * Speed));
    AnimationPlayed(CurrentState, animHash);
    }

    What you have to do to do this is ridiculous.

    Here I am temporarily setting the speed, because otherwise the blends will be wrong.

    Then actually changing the animation.

    Then pumping the animator with a Update(0) (this recently as of 5.6 I believe now starts to fire events as well, so I have to deal with that).

    Then I have to try and get the state (That I just set!!!) which occasionally and explicable fails for some reason ( cinfos.Length == 0).

    THEN get the clip and then set the speed, reset the speed to the animator. Just so I can say play this animation in x seconds.

    The problem I have now is that I can no longer tolerate the mysterious (cinfos.Length == 0) errors I get. So I am setting off to build some wild editor plugins that can index the states save the clipdata (and keep them updated) then I can access the data at runtime all so I can say "What clips does the 'Attack' state have in it"

    What can't Animator have a GetAnimatorClipInfo(int stateHash)?!!
     
    DaDarkDan, Romaleks360 and mangax like this.
  15. mangax

    mangax

    Joined:
    Jul 17, 2013
    Posts:
    336
    mecanim is more like tailored for in editor use only.. i really miss the simplicity of animation component... please someone correct me if am wrong or am missing something??

    Most Games contain characters that have different move-sets depending on weapons and gears..being able to swap clips at runtime then edit state exit time and all that stuff ...it adds alot of flexibility at the hands of programmer..

    and makes animator tree in editor much more simpler and more focused on important states (blending etc) then i will only need One attack state!! it would be very awesome if i can edit the exit time depending on the clip length through script, so now i can animate my clips in 3d apps freely without all the tweaking times in editor..
    i find it weird and strange when i see some devs forced to create huge gigantic trees for their characters for every possibility ...

    override controllers don't aid much... still causes pain and tedious to work with! for example... changing a clip from original controller asset in editor causes all other clips references in other override controllers gets lost..

    currently i have many enemies in my games contains two states,, default is flying, then next state is death.. i found out that it is far more convenient to simply add clips in the old animation component and use play("death").. but i lose humanoid features in the process because of this!

    unity devs added behaviors for states, it sounds cool but adding scripts for each state is just wrong and awkward! why adding a script for each state!?

    first of all many of us own their own AI state machine from scripts and personally am happy with it, and i can use any effect at any game event..it's all unified and well managed from code...
    so now animator component does not allow me to update clips or states with ease..
    at same time the new behavior feature encourage me to add behavior scripts which i don't need ... this would cause my code to partitioned...that's why i don't use animator behaviors.


    i really want animator api to be able to do things as simple as this >>>
    PlayState("SuperMoveState", "newClip", 5f ) <--exit after 5 seconds
    then animator handles everything after exit event starts..

    straight forward.. no weird animations or hiccups or interruptions.
     
    Last edited: Oct 5, 2017
    Alverik likes this.
  16. dogzerx2

    dogzerx2

    Joined:
    Dec 27, 2009
    Posts:
    3,971
    There's big change if you're messing around with state machine is you're building editor tools, or at least editor only stuff.
    If that's the case, just put everything that's using UnityEditor class between #if Unity_Editor and #endif

    #if Unity_Editor
    Editor stuff here
    #endif

    Non editor stuff here

    #if Unity_Editor
    More editor stuff here
    #endif

    and so on
     
  17. mangax

    mangax

    Joined:
    Jul 17, 2013
    Posts:
    336

    Sorry guys for the super ranting here, just 2 days later after the replay i found out about the new playables API
    this is the solution we've been all waiting for.. full control over unity animations!
     
    theANMATOR2b likes this.
  18. TheGameLearner

    TheGameLearner

    Joined:
    Feb 10, 2018
    Posts:
    20
    I just wanted to try this idea and create a script to name all Animator States.
    I needed to name them correctly to perform some actions ahead but that is not relevant here.
    I have made a script for Reset() method that calculates all the names and stores their names in a list of string.
    please take a look and I hope it Helps anyone looking for this in future.


    Code (CSharp):
    1. /*
    2. The Script is created by TGL(TheGameLearner)
    3. https://answers.unity.com/users/1044949/thegamelearner.html
    4. https://forum.unity.com/members/thegamelearner.1695462/
    5. */
    6.  
    7. using System.Collections;
    8. using System.Collections.Generic;
    9. using UnityEngine;
    10. using UnityEditor.Animations;
    11.  
    12. public class NameAllAnimStates : MonoBehaviour
    13. {
    14.     public List<string> AnimStateNames;
    15.     AnimatorControllerLayer[] acLayers;
    16.     ChildAnimatorState[] ch_animStates;
    17.  
    18.     Animator animator;
    19.     AnimatorController ac;
    20.     AnimatorStateMachine stateMachine;
    21.     int k = 0;
    22.     private void Reset()
    23.     {
    24.         animator = GetComponent<Animator>();
    25.         ac = animator.runtimeAnimatorController as AnimatorController;
    26.  
    27.         Debug.Log(string.Format("Layer Count: {0}", animator.layerCount));
    28.  
    29.         for (int i = 0; i< animator.layerCount; i++)
    30.         {
    31.             Debug.Log(string.Format("Layer {0}: {1}", i, animator.GetLayerName(i)));
    32.             Debug.Log("---");
    33.         }
    34.         acLayers = ac.layers;
    35.  
    36.         foreach(AnimatorControllerLayer i in acLayers) //for each layer
    37.         {
    38.             Debug.Log("******");
    39.             Debug.Log("Layer : "+i.name);
    40.             Debug.Log("---");
    41.             stateMachine = i.stateMachine;
    42.             ch_animStates = null;
    43.             ch_animStates = stateMachine.states;
    44.             foreach (ChildAnimatorState j in ch_animStates) //for each state
    45.             {
    46.                 AnimStateNames.Add(j.state.name);
    47.                 k++;
    48.                 Debug.Log("Added " + i.name + "." + j.state.name);
    49.             }
    50.         }
    51.         Debug.Log("All " + k + " states in " + animator.layerCount
    52.                         + " layers were added to the string array");
    53.  
    54.        
    55.     }
    56.  
    57.     void Start ()
    58.     {
    59.        
    60.     }
    61.    
    62.     void Update ()
    63.     {
    64.        
    65.     }
    66. }
    67.  
     
    Raikeii, wesleywh, GilberM and 5 others like this.
  19. grobonom

    grobonom

    Joined:
    Jun 23, 2018
    Posts:
    335
    I always wondered why getting allt states names from an animator layer is this S***ty /impossible ^^

    okay i'd be answered RTFM but a 5 lines example would ease things so much ;)

    this is however damn simple but most prefer direct ppl to docz :mad:

    Code (CSharp):
    1.         // now the anims part...... lets grab them all
    2.         // grabbing anims entries...
    3.         anim= GetComponentInChildren<Animator>();
    4.  
    5.       RuntimeAnimatorController rac = anim.runtimeAnimatorController;
    6.  
    7.       AnimationClip[] clips = rac.animationClips;
    8.  
    9.        anim_clip_count = clips.Length;
    10.  
    11.         animation_names = new string[anim_clip_count];
    12.    
    13.       // store names in a table
    14.         int i=0;
    15.         foreach(AnimationClip clip in clips)
    16.         {
    17.             Debug.Log(""+clip.name);
    18.             animation_names[i]=clip.name;
    19.             i++;
    20.         }
    ..... okay.... 20 lines :(
    my bad :/

    Happy unitying :)
     
    Last edited: May 19, 2019
  20. JonPQ

    JonPQ

    Joined:
    Aug 10, 2016
    Posts:
    120
    thanks grobonom, but still doesn't solve runtime problem... Animator requires state names to play them, not clip names. You solution works at runtime and on device, but does not find the state names. TheGameLearner's solution finds the State Names bot only works in Editor. not in a build.

    Is there a way to do it on device/in a real build (not editor) that finds all the state names ?
     
    Andimator likes this.
  21. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    No, there isn't. That's just one of the limitations you have to deal with if you use Animator Controllers.
     
  22. JonPQ

    JonPQ

    Joined:
    Aug 10, 2016
    Posts:
    120
    Thanks for answer. Sad though
    :(
     
    Last edited: Jun 20, 2019
  23. GuruJeya14

    GuruJeya14

    Joined:
    Jun 29, 2017
    Posts:
    9
    Code (CSharp):
    1. [SerializeField] Animator animator;
    2. int NumberOfNonEmptyStates = animator.runtimeAnimatorController.animationClips.Length;
    This returns the number of non-empty states in the specified animator during runtime.
     
    Last edited: Mar 30, 2020
    dogzerx2 likes this.
  24. JonPQ

    JonPQ

    Joined:
    Aug 10, 2016
    Posts:
    120
    TheGameLearner's solution can be used to populate a list at Editor time, that can then be used at run time.
     
  25. Voodoocado

    Voodoocado

    Joined:
    Aug 19, 2018
    Posts:
    11
    This might be helpful for someone - check if a state exists in an animator

    Code (CSharp):
    1.        
    2.  
    3. public bool HasState(string animationName)
    4. {
    5.     AnimatorController animatorController = animator.runtimeAnimatorController as AnimatorController;
    6.  
    7.     foreach (AnimatorControllerLayer layer in animatorController.layers)
    8.         foreach (ChildAnimatorState childAnimatorState in layer.stateMachine.states)
    9.             if (childAnimatorState.state.name == animationName)
    10.                 return true;
    11.  
    12.     return false;
    13. }
    14.  
    15.  
     
    AndersonMarquess and koirat like this.
  26. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    AnimatorController is an Editor-Only class so that won't work in runtime builds and it has already been mentioned earlier in the thread.
     
  27. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,074
    Great.
    I allowed myself to use your code and create useful snipped:

    Put on a script and drop on an object with Animator component.
    I'm using Easy Button but as you can see it is using also context menu.
    The output you are interested in is called "Full Path:" It should appear as a green color:

    Works only in editor.

    Code (csharp):
    1.  
    2. #if UNITY_EDITOR
    3.     //[EasyButtons.Button()]
    4.     [ContextMenu(nameof(GetAnimatorStatesInfo))]
    5.     void GetAnimatorStatesInfo() {
    6.  
    7.         Animator animator = this.GetComponent<Animator>();
    8.         if (animator != null) {
    9.             var animatorController = animator.runtimeAnimatorController as UnityEditor.Animations.AnimatorController;
    10.  
    11.             string animatorInfoText = "<b>Animator:</b>" + animator.name + "\n";
    12.          
    13.             foreach (var animatorLayer in animatorController.layers) {
    14.                 animatorInfoText += "\n<b>Layer:</b>" + animatorLayer.name;
    15.                 foreach (var childAnimatorState in animatorLayer.stateMachine.states) {
    16.                     animatorInfoText += "\n-----------------------";
    17.                     animatorInfoText += "\n\t<b>State:</b>" + childAnimatorState.state.name;
    18.                     animatorInfoText += "\n\t<b>State Name Hash:</b>" + childAnimatorState.state.nameHash;
    19.                     string fullPath = animatorLayer.name + "." + childAnimatorState.state.name;
    20.                     animatorInfoText += "\n\t<b>Full Path:</b>" + "<color=green>" + fullPath + "</color>";
    21.                     animatorInfoText += "\n\t<b>Full Path Hash:</b>"  + Animator.StringToHash(fullPath);
    22.                 }
    23.             }
    24.  
    25.             Debug.Log(animatorInfoText);
    26.  
    27.         }
    28.     }
    29. #endif
    30.  
    Example output in console:
     
    TheGameLearner likes this.
  28. skyemont

    skyemont

    Joined:
    Dec 1, 2020
    Posts:
    3
    I needed to cross reference my states and animation clips at run time, so I came up with a solution that works for me since my animations are fixed at build time (i.e. I don't create animation clips during run time, just consume existing ones). I added a 'public string[] AnimationClipData' member to my custom script. Then let the editor populate that with a list of strings I parse up at run time, which has the cross ref data in it.

    Here is my code (scrubbed and simplified.) It worked fine BEFORE I scrubbed out my custom code, but this should still work :) If you don't know how to add an editor script, search for 'unity editor script' and unity will have the latest way to do that. Change the 'My...' names to what you want.

    Once you create your class and save it, a menu item should appear called 'MyHelperMenu' or whatever you renamed it to... and 'Populate Animator Clip Data' will appear as an option. Just hit that and this will make the data on the assets selected / indicated.

    There are two lines provided to get the selected assets or all prefab assets in a path. It also makes sure the custom script is on the game object. The 'SaveAssets' at the bottom can be a real time killer... expect a long wait if you have a ton of assets you are converting.

    ALWAYS!, ALWAYS!! (did I mention ALWAYS!!!), make a backup of your project BEFORE doing ANY BULK EDITOR changes like this when you first test your code. Once you click that menu, you are TOAST! if you goofed it. (of course I've never had that happen... eh hem...)

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEditor;
    3. using UnityEngine;
    4. using System.Text;
    5. using UnityEditor.Animations;
    6.  
    7. public class MyEditor
    8. {
    9.     [MenuItem("Character/Populate Animator Clip Data")]
    10.     static void CharacterPopulateAnimatorClipData()
    11.     {
    12.         // use this line to get selected assets in the asset window
    13.         string[] assetIDs = Selection.assetGUIDs;
    14.         // use this line to get all assets that are prefabs in an asset path/paths, change path to the folder(s) you want.
    15.         //string[] assetIDs = AssetDatabase.FindAssets("  t:Prefab", new string[] { "Assets/Resources/Prefab/Character" });
    16.         if (assetIDs != null)
    17.         {
    18.             string assetPath;
    19.             bool hadChanges = false;
    20.  
    21.             AssetDatabase.StartAssetEditing();
    22.  
    23.             try
    24.             {
    25.                 foreach (string assetID in assetIDs)
    26.                 {
    27.                     assetPath = AssetDatabase.GUIDToAssetPath(assetID);
    28.                     if (!string.IsNullOrWhiteSpace(assetPath))
    29.                     {
    30.                         GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
    31.                         if (prefab != null)
    32.                         {
    33.                             MyScript myScript = prefab.GetComponent<MyScript>();
    34.                             if (myScript != null)
    35.                             {
    36.                                 Animator animator = prefab.GetComponent<Animator>();
    37.                                 if (animator != null)
    38.                                 {
    39.                                     AnimatorController controller = animator.runtimeAnimatorController as AnimatorController;
    40.                                     if (controller != null)
    41.                                     {
    42.                                         Dictionary<string, string> stateNamesByMotionNames = new Dictionary<string, string>();
    43.                                         List<string> dataItems = new List<string>();
    44.                                         AnimatorState state;
    45.                                         Motion motion;
    46.                                         StringBuilder sbTemp;
    47.                                         string original = null;
    48.                                         string final = null;
    49.  
    50.                                         if (myScript.AnimatorClipData.HasContent())
    51.                                         {
    52.                                             sbTemp = new StringBuilder();
    53.                                             foreach (string data in myScript.AnimatorClipData)
    54.                                             {
    55.                                                 sbTemp.AppendLine(data);
    56.                                             }
    57.                                             original = sbTemp.ToString();
    58.                                         }
    59.  
    60.                                         foreach (AnimatorControllerLayer layer in controller.layers)
    61.                                         {
    62.                                             foreach (ChildAnimatorState childState in layer.stateMachine.states)
    63.                                             {
    64.                                                 state = childState.state;
    65.                                                 if ((state != null) && (state.name != null))
    66.                                                 {
    67.                                                     motion = state.motion;
    68.                                                     if ((motion != null) && (motion.name != null))
    69.                                                     {
    70.                                                         if (!stateNamesByMotionNames.ContainsKey(motion.name))
    71.                                                         {
    72.                                                             stateNamesByMotionNames.Add(motion.name, state.name);
    73.                                                         }
    74.                                                     }
    75.                                                 }
    76.                                             }
    77.                                         }
    78.  
    79.                                         if (stateNamesByMotionNames.Count > 0)
    80.                                         {
    81.                                             string stateName;
    82.                                             string line;
    83.                                             sbTemp = new StringBuilder();
    84.                                             foreach (AnimationClip clip in controller.animationClips)
    85.                                             {
    86.                                                 if ((clip.name != null) && (stateNamesByMotionNames.TryGetValue(clip.name, out stateName)))
    87.                                                 {
    88.                                                     line = stateName + "|" + clip.name + "|" + clip.length;
    89.                                                     dataItems.Add(line);
    90.                                                     sbTemp.AppendLine(line);
    91.                                                 }
    92.                                             }
    93.  
    94.                                             if (sbTemp.Length > 0)
    95.                                             {
    96.                                                 final = sbTemp.ToString();
    97.                                             }
    98.                                         }
    99.  
    100.                                         if (original != final)
    101.                                         {
    102.                                             if (dataItems.Count > 0)
    103.                                             {
    104.                                                 myScript.AnimatorClipData = dataItems.ToArray();
    105.                                             }
    106.                                             else
    107.                                             {
    108.                                                 myScript.AnimatorClipData = null;
    109.                                             }
    110.                                             hadChanges = true;
    111.                                             EditorUtility.SetDirty(prefab);
    112.                                         }
    113.                                     }
    114.                                 }
    115.                             }
    116.                         }
    117.                     }
    118.                 }
    119.             }
    120.             catch
    121.             {
    122.             }
    123.  
    124.             AssetDatabase.StopAssetEditing();
    125.  
    126.             if (hadChanges)
    127.             {
    128.                 AssetDatabase.SaveAssets();
    129.             }
    130.         }
    131.     }
    132. }
    133.  
    134.