Search Unity

[Open Source] Animator Access - Generate State & Parameter Hash IDs

Discussion in 'Assets and Asset Store' started by kayy, Jun 13, 2014.

  1. kayy

    kayy

    Joined:
    Jul 26, 2011
    Posts:
    110
    Just released my newest open source scripting asset - Animator Access Generator

    The video below gives a quick overview of Animator Access Generator, a C# code generator that makes handling of Animator states and parameters easier:
    • You don't have to enter state or parameter names manually any longer as they will be generated automatically.
    • Using it will let the compiler detect most possible problems in your code, saving you time consuming bug analysis because of wrong hash IDs.


    Note: Switch to HD Quality manually or use this link to youtube.com


    Most important features:

    • Defines public member variables to hold the hash IDs of all Animator states and parameters and initialises them in Awake () with the correct name string constants
    • Query Animator states by using explicit methods e.g.
      bool IsIdle () for checking a state named Idle
    • Provides dedicated methods for accessing every parameter e.g.
      void SetSpeed (float newValue) and float GetSpeed () for setting a float parameter speed
    • Easy to use through custom editor for AnimatorAccess components
    • Supports smooth updates on name changes by analysing the existing class's code and marks outdated members as obsolete first. They will not be removed before the code is generated one more time
    • Customise the way how variable and method names are built
    Resources
    Animator Access Generator is an open source project hosted at GitHub see:
    Requirements
    Unity 4.3

    Code Sample

    Excerpt of generated code (see ExamplePlayerAnimatorAccess.cs for full code):
    Code (CSharp):
    1. public class ExamplePlayerAnimatorAccess : BaseAnimatorAccess
    2. {
    3.     Animator animator;
    4.  
    5.     /// <summary>
    6.     /// Hash of Animator state Base Layer.Idle
    7.     /// </summary>
    8.     public int stateIdIdle;
    9.  
    10.     // ...
    11.  
    12.     public int paramIdSpeed;
    13.  
    14.     public int paramIdJumpTrigger;
    15.  
    16.     // ...
    17.  
    18.     public void Awake () {
    19.         animator = GetComponent<Animator> ();
    20.         stateIdIdle = Animator.StringToHash ("Base Layer.Idle");
    21.         // ...
    22.         paramIdSpeed = Animator.StringToHash ("Speed");
    23.         paramIdJumpTrigger = Animator.StringToHash ("JumpTrigger");
    24.         // ...
    25.     }
    26.  
    27.     public bool IsIdle (int nameHash) {
    28.         return nameHash == stateIdIdle;
    29.     }
    30.  
    31.     // ...
    32.  
    33.     public void SetSpeed (float newValue, float dampTime, float deltaTime) {
    34.         animator.SetFloat (paramIdSpeed, newValue, dampTime, deltaTime);
    35.     }
    36.  
    37.     public void SetSpeed (float newValue) {
    38.         animator.SetFloat (paramIdSpeed, newValue);
    39.     }
    40.     public float GetSpeed () {
    41.         return animator.GetFloat (paramIdSpeed);
    42.     }
    43.  
    44.     public void SetJumpTrigger () {
    45.         animator.SetTrigger (paramIdJumpTrigger);
    46.     }
    47.  
    48.     // ...
    49.  
    50. }

    Using generated code (see Player.cs for full code):
    Code (CSharp):
    1. public class Player : MonoBehaviour
    2. {
    3.     ExamplePlayerAnimatorAccess anim;
    4.  
    5.     Animator animator;
    6.     int currentState0;
    7.  
    8.     public float speed;
    9.     public float maxSpeed = 5f;
    10.     float horizontalInput;
    11.     bool jumpKeyPressed = false;
    12.  
    13.     void Awake () {
    14.         animator = GetComponent<Animator> ();
    15.         anim = GetComponent<ExamplePlayerAnimatorAccess> ();
    16.     }
    17.  
    18.     void Update () {
    19.         horizontalInput = Input.GetAxis ("Horizontal");
    20.         jumpKeyPressed = Input.GetKeyDown (KeyCode.UpArrow);
    21.     }
    22.  
    23.     void FixedUpdate () {
    24.         currentState0 = animator.GetCurrentAnimatorStateInfo (0).nameHash;
    25.         if (anim.IsIdle (currentState0)) {
    26.             speed = 0f;
    27.             return;
    28.         } else if (anim.IsJumping (currentState0)) {
    29.             return;
    30.         }
    31.         if (jumpKeyPressed) {
    32.             anim.SetJumpTrigger ();
    33.         }
    34.         speed = horizontalInput * maxSpeed;
    35.         anim.SetSpeed (Mathf.Abs (speed));
    36.         // alternative (not recommended) way of accessing hash IDs directly:
    37.         // animator.SetFloat (anim.paramIdSpeed, Mathf.Abs (speed));
    38.     }
    39. }

    I appreciate your feedback, feature requests, ... :)
     
    Last edited: Jun 17, 2014
    Stiefo.o likes this.
  2. kayy

    kayy

    Joined:
    Jul 26, 2011
    Posts:
    110
    Version 0.7 just released! It now has support for event callbacks on Animator state changes. To register a callback to be notified whenever a state changes, you just write:

    Code (CSharp):
    1. void OnEnable () {
    2.     anim.OnStateChange += OnStateChange;
    3. }
    4. void OnDisable () {
    5.     anim.OnStateChange -= OnStateChange;
    6. }
    7. void OnStateChange (int layer, int newState, int previousState) {
    8.     if (anim.IsJumping (newState)) {
    9.         Debug.Log ("OnStateChange: Jump, previous state was " + anim.IdToName (previousState));
    10.         }
    11.     }
    12.  
    The state checking code can be configured in Settings to run in FixedUpdate, Update or by invoking the appropriate method manually. At the moment this is a global setting for all generated classes. Individual i.e. per class configuration will come in version 0.9, the beta version when all planned features are implemented.

    The next version 0.8 will support transition events like OnTransitionStarted and hash ID to clear text name conversion for transition hash IDs - meant to be used for debugging purposes only.

    I currently only have tested it on Mac OS. So I appreciate your feedback especially on Linux or Windows :)
     
  3. im

    im

    Joined:
    Jan 17, 2013
    Posts:
    1,408
    very nice asset thanks!
     
    kayy likes this.
  4. kayy

    kayy

    Joined:
    Jul 26, 2011
    Posts:
    110
    Another Update to stable version 0.8.2. The most important changes:
    • Events listeners for a couple of Animator state changes like any state change in layer X, a specific event's OnEnter, OnExit, OnStay, OnActive
    • Events on transitions, e.g. when any transition to a specified target state starts ...
    • Overloaded check methods for states. Now you can just write anim.IsIdle () and IsIdle knows the right layer
    • Extended information about all states and transitions at runtime like name, speed, ... or name, duration, source, destination, ... Especially useful for debugging
    The new event handling let you design your code in a much more flexible and elegant way. if-else-chains for state checking are not needed any longer. Instead you can just register an appropriate event listener in OnEnable. A few examples:

    Code (CSharp):
    1. void OnEnable () {
    2.         anim.State (anim.stateIdIdle).OnActive += OnIdle;
    3.         anim.TransitionTo (anim.stateIdJumping).OnStarted += OnStartedTransitionToJumping;
    4. }
    5. void OnIdle (StateInfo info, LayerStatus status) {
    6.         // called in every FixedUpdate cycle as long the player is in state 'Idle'
    7. }
    8. void OnStartedTransitionToJumping (TransitionInfo info, LayerStatus status) {
    9.         //  called once when an arbitrary transition towards 'Jumping' started
    10. }
    11.  
    Especially when dealing with complex player objects Loose Coupling can be realised much easier using an event-based approach. Functionality can be delegated to single scripts that doesn't know anything about each other. Thus a PlayerAnimationSoundController for example just plays its Jumping audio clip when a transition towards status Jumping is started without having references to controller parent objects or vice versa.
    Everything is highly optimised for best performance i.e. code is only executed and events are sent, if there are any listeners subscribed for.

    As event based state handling is a really cool idea, Unity just announced to support a similar feature in version 5.0. They took a slightly different way but basically the same goal. Well, a bit pity for me but so what.

    If you want to use event based state handling right now take a closer look at it
     
  5. vToMy

    vToMy

    Joined:
    Apr 19, 2013
    Posts:
    22
    I must say WOW.
    I was looking for something like this. I really liked the fine details design (i.e. having obsolete methods so I could refactor).
    Definitely going to make my code a lot more manageable.
    THANK YOU!
     
    kayy likes this.
  6. Tyri0n

    Tyri0n

    Joined:
    Nov 28, 2012
    Posts:
    2
    Thanks for this, but just this func had a problem.

    Changes in bold below:


    public AnyTransitionHandler AnyTransition (int layer = -1) {
    if (layer > LayerCount) {
    Debug.LogWarning ("The specified layer " + layer + " exceeds layer count (" + LayerCount + ")! Seems like the AnimatorAccess component needs to be updated.");
    }
    AnyTransitionHandler handler = new AnyTransitionHandler (layer);
    int id = handler.GetHashCode ();
    if (!TransitionHandlers.ContainsKey (id))
    {
    TransitionHandlers [id] = handler; // changed to key to id from 0
    }
    return (AnyTransitionHandler)TransitionHandlers [id]; // changed to key to id from 0
    }
     
    kayy likes this.
  7. kayy

    kayy

    Joined:
    Jul 26, 2011
    Posts:
    110
    Thanks, you are right. Additionally AnyTransitionHandler needed to override the GetKeyString () method so that the layer is considered in GetHashCode (). Otherwise the first call instantiates a handler that will be used for all subsequent calls regardless which layer is specified.

    I fixed this and built the new release 0.8.4
     
  8. im

    im

    Joined:
    Jan 17, 2013
    Posts:
    1,408
    thanks :)
     
  9. Tyri0n

    Tyri0n

    Joined:
    Nov 28, 2012
    Posts:
    2
    Cool, cheers.

    Another idea for you...

    I've changed my version to output the state Ids as const ints, so I could use them in a switch block.
     
  10. kayy

    kayy

    Joined:
    Jul 26, 2011
    Posts:
    110
    Sure this would increase flexibility. I just tried it out (just field.Const = true; in line 176 of AnimatorCodeElementsBuilder.cs) but it breaks the example scene (CS0176: static members cannot be accessed with an instance reference). Thus existing users will run into compatibility issues.

    Basically a good idea, I will bear this in mind. Maybe const integers should be generated in parallel.
     
  11. bug5532

    bug5532

    Joined:
    Aug 16, 2011
    Posts:
    307
    Hi, this plugin looks like it could be awesome.
    I'm having trouble getting it to work on one of my animators though. Can it handle substates and blend trees?
    heres the error:
    Cheers
     
  12. kayy

    kayy

    Joined:
    Jul 26, 2011
    Posts:
    110
    No I am sorry, at the moment there is no support for substates and blend trees. Originally both features were planned for version 0.9. But then when I releases version 0.8 Unity announced that version 5 will contain a pretty similar feature. So chances are are high that further developement will be obvious at the end of the year.
     
  13. bug5532

    bug5532

    Joined:
    Aug 16, 2011
    Posts:
    307
    Ah, thats a shame, looked like a really useful tool. Is there an easy way to make it just ignore the blendtrees/substates? Would be good to be able to use it just for the variables!
     
  14. makoto_snkw

    makoto_snkw

    Joined:
    Aug 14, 2013
    Posts:
    340
    Can this handle mask animations?

    Like, in this situation.

    A singer character is standing playing a guitar with animation swaying left and right...

    Then using your method to call a mask animation of her left hand and to pose playing different chords at different timing (my script).
    Then your method again, to call a mask animation for her right hand for strumming...
     
  15. ahmidou

    ahmidou

    Joined:
    Sep 17, 2012
    Posts:
    87
    Hi,
    This very cool package is not working with the lasts versions of Unity, are there other packages close to this one around?
    Thanks
    -A
     
  16. kayy

    kayy

    Joined:
    Jul 26, 2011
    Posts:
    110
    Like stated above there is no support any longer because Unity introduced a very similar system starting with version 5.0