Search Unity

Animation speed of a single Mecanim state.

Discussion in 'Animation' started by Brodal, Apr 17, 2014.

  1. Brodal

    Brodal

    Joined:
    Jan 26, 2013
    Posts:
    8
    I'm trying to set the speed of a single mecanim state through script but can't seem to find a way to do it. What I'm trying to do is hook an animation up to the analogue stick on my Xbox 360 controller. The only solution I've found so far is using a blend tree and having a couple of thresholds where the animation changes speed, this could do the job but is not ideal. This is why I'm trying to find a way that I could set the speed of a single state through script dynamically. Any help would be greatly appreciated.
     
  2. willgoldstone

    willgoldstone

    Unity Technologies

    Joined:
    Oct 2, 2006
    Posts:
    794
    Hi Brodal

    I think what you're missing in this equation is blend trees. I'm gonna start by assuming you have a character driven by root motion - that is - one that uses the motion from the original clip to move it around (you aren't applying forces to a rigidbody or using CharacterController Move functions).

    That assumed, you should keep in mind that your animation's foot placement going forward will define the speed in the basic sense. Usually, the procedure for building up a simple character that goes from idle to walk to run based on an analog input is to use a blend tree. You would create a blend tree and into this place 3 motion clips (for example) - which could be idle, walk and run animations. Then your input from the pad should be tied to a Parameter - the float that you define in the lower left of the Animation window. The blend tree then uses this parameter to define which of those 3 animations should be playing / blending.

    To tie these together, in your script you would have something like
    Code (csharp):
    1.  
    2. void Update(){
    3. GetComponent<Animator>.SetFloat("Speed", v);
    4. }
    Where 'v' is your vertical axis saved as a variable from Input.GetAxis("Vertical"); naturally you would also want to save the Animator into a variable in Awake, instead of using GetComponent in Update.

    Note that the above example is for a basic blend tree and wouldn't be what you want to do in practice - ideally you should create a character that uses animations for running turning left and right too (though you can just add one direction, and duplicate, selecting 'Mirror'). This would be a 2D blend tree and gives you the kind of control you need.

    Take a look at our starter tutorials here -

    http://unity3d.com/learn/tutorials/modules/beginner/animation


    And also check out the Stealth tutorial part that focuses on this to see it in action -

    http://unity3d.com/learn/tutorials/projects/stealth/player-animator-controller


    Good luck!

    Will
     
  3. willgoldstone

    willgoldstone

    Unity Technologies

    Joined:
    Oct 2, 2006
    Posts:
    794
  4. Brodal

    Brodal

    Joined:
    Jan 26, 2013
    Posts:
    8
    I probably should have mentioned this game is using the new 2D stuff. Character uses a 2D rigidbody which is moved by setting the velocity when analogue stick is pressed. What I'm trying to do is reflect the change in speed from the code onto the run animation without affecting the other Mecanim states as many of the animations use different values for the speed.
     
  5. OllyNicholson

    OllyNicholson

    Unity Technologies

    Joined:
    Jun 17, 2011
    Posts:
    142
    Speed is simply another Animation Parameter - any parameters can be exposed to control:

    https://docs.unity3d.com/Documentation/Manual/AnimationParameters.html
    https://docs.unity3d.com/Documentation/Manual/AnimationStates.html

    To set this up you might want to take a look at the sample assets which have fully controllable character setup for Mecanim with speed and other Animation Parameters set up and mapped to a key/controller:

    https://www.assetstore.unity3d.com/#/content/14474
    http://unity3d.com/learn/tutorials/modules/beginner/animation

    For controller setup, please refer to the input documentation:

    http://docs.unity3d.com/Documentation/Manual/Input.html

    Tip for 360 controller on PC:

    Duplicate Mouse Y and Mouse X in the input manager
    Set them to Type: Joystick Axis Axis: 4th Axis (X) and 5th Axis respectively (Y)
     
  6. Brodal

    Brodal

    Joined:
    Jan 26, 2013
    Posts:
    8
    I found part of the solution now. I looked through what you proposed but that just looked like what everyone else proposed, to change the speed of the animator not the single animation state. I however found a way to retrieve the animation states in the animator through UnityEditorInternal.

    Code (csharp):
    1.  
    2.     private static string RUN_FAST = "Base Layer.run_fast";
    3.     private static string RUN_FAST_BEGIN = "Base Layer.run_fast_begin";
    4.     private UnityEditorInternal.State m_runFastState;
    5.     private UnityEditorInternal.State m_runFastBeginState;
    6.  
    7.     // Use this for initialization
    8.     void Start ()
    9.     {
    10.         m_animator = GetComponent<Animator>();
    11.  
    12.         UnityEditorInternal.AnimatorController ac = m_animator.runtimeAnimatorController as UnityEditorInternal.AnimatorController;
    13.         UnityEditorInternal.StateMachine sm = ac.GetLayer(0).stateMachine;
    14.         for (int i = 0; i < sm.stateCount; i++)
    15.         {
    16.             UnityEditorInternal.State state = sm.GetState(i);
    17.             if (state.uniqueName == RUN_FAST)
    18.                 m_runFastState = state;
    19.             else if (state.uniqueName == RUN_FAST_BEGIN)
    20.                 m_runFastBeginState = state;
    21.         }
    22.     }
    Here I can change the speed of a specific state to whatever I want whenever I want. In order to change the speed of the animations i do the following.

    Code (csharp):
    1. // Update is called once per frame
    2.     void FixedUpdate ()
    3.     {
    4.         float h = Input.GetAxis("Horizontal");
    5.         float positiveH = Mathf.Abs(h);
    6.         if (positiveH > 0.1f)
    7.         {
    8.             rigidbody2D.velocity = new Vector3(h * m_maxSpeed, rigidbody2D.velocity.y);
    9.             m_runFastState.speed = 0.4f * positiveH;
    10.             m_runFastBeginState.speed = 0.4f * positiveH;
    11.         }
    12.         else
    13.             rigidbody2D.velocity = new Vector3(rigidbody2D.velocity.x * 0.9f * Time.deltaTime, rigidbody2D.velocity.y);
    14.     }
    Where 0.4f is the max speed I want both of these animations to playback with. This changes the speed just the way i wanted to change the speed. This is used to control the speed of 2D dopesheet animations.
    The problem that I am facing now is that the state resets the playback position to the beginning of the state when the speed is changed, which resets the run animation when the animation speed is changed. What I need is someway to make the state resume playing from the same position it did before the speed changed.¨

    Edit:
    Actually, the whole state machine seems to be resetting when changing the speed of an individual state. Would greatly appreciate any help on how I could counter this problem.
     
    Last edited: Apr 17, 2014
  7. gsus725

    gsus725

    Joined:
    Aug 23, 2010
    Posts:
    250
    I don't know how it is possible for both of you to miss the point so badly,

    Animator.speed will change the playback speed of all animation clips in the animator. We want to be able to change the playback speed of an individual animation state inside the animator, while leaving animator.speed alone.

    In the editor if you click any state in the animator window you can set its speed. We want to be able to do that by scripting as well.

    And if you say the solution is blend trees, then I'm afraid you've missed the point once again
     
    Tethys, guavaman and chelnok like this.
  8. gsus725

    gsus725

    Joined:
    Aug 23, 2010
    Posts:
    250
    Here is an example. I have a run animation, the speed of this run animation is determined by CharacterController.velocity.magnitude.

    On another layer I have a melee attack animation for the upper body only.

    If I set Animator.speed=CharacterController.velocity.magnitude, not only will that speed up my run animation to match the velocity at which my character is moving, it will also make his melee attack animation speed up, which is not something I want.
     
  9. Shin_

    Shin_

    Joined:
    Jul 31, 2014
    Posts:
    1
    Hey gsus, I know exactly what you are trying to achieve, I spent a long time trying to work it out when I first started working with 2d in unity 4.3 as blend tree's always seemed to be a lot more work than was necessary for a sub par result! Why jump between sped up versions of an animation when you could just directly control the speed of it. I have bad news for you, blend trees is going to be the easiest way to get this working for you. But for 2d rather than have it blend between 2 animations, you will need to put one in for every potential speed you want you character to animate at. I went for 3 walk and 3 run in the end and it looks great. You can use the same animation for all 3 and just change the speed on each entry in the blend tree. It's by no means perfect and it's still crazy to me that there is no way of directly altering the animation speed of a single animation, or layer, within the animator from code but don't fight it, blend tree ain't so bad. Hopefully it ends up in a changelog one day! Hope this helps at least a little.
     
    chelnok and OllyNicholson like this.