Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Runtime Animator State Properties (speed, mirror, cycleOffset) for 5.1

Discussion in 'Animation' started by Mecanim-Dev, Mar 5, 2015.

  1. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Hi guys,
    I just want to share with you a new feature for 5.1. Enjoy

    Runtime Animator State Properties

    Overview
    This feature, introduced in Unity 5.1, allows users to attach any of their Animator State properties( speed, mirror, cycle offset) as a controller’s parameter to change their value at runtime.

    Properties can either be exposed by using the Animator State Inspector or by script.


    Editor
    The Animator State Inspector has been improved to let you build your own properties setup.





    For each property you can choose to either:

    1. Set a constant value like before.

    2. Check the new checkbox ‘Parameter’ which should change the numeric field to a popup list containing all your controller’s parameters of the same type as the property. Choosing one parameter to drive your property will complete the setup.




    Scripting
    We have also added new properties to allow you to build your setup from script.

    The 3 following new members are used to define which of the controller’s parameters should control this property.

    string AnimatorState.speedParameter;
    string AnimatorState.mirrorParameter;
    string AnimatorState.cycleOffsetParameter;

    The following members are used to define if the controller should use the parameter to animate that property or not.

    bool AnimatorState.speedParameterActive;
    bool AnimatorState.mirrorParameterActive;
    bool AnimatorState.cycleOffsetParameterActive;

    Error Handling
    If the parameter cannot be found in the controller’s parameters list or if the parameter type doesn’t match the type of the property, a console warning should be thrown and the property would fallback to the old behaviour, which is to read the value from the constant.
     
    dyupa, robertTWM, Qlintnet and 5 others like this.
  2. sunnydavis

    sunnydavis

    Joined:
    Aug 2, 2010
    Posts:
    45
    Cool! will the procedurel blendshape creation also be in the 5.1?

    So AnimatorState will be moved from editor to the runtime classes? What else will be moved to runtime?
     
    Last edited: Mar 5, 2015
  3. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    No it not yet finish so it won't be part of 5.1

    No, the AnimatorState class is still not available at runtime. You can define controller's parameter to control those properties that are all baked inside the controller.
     
  4. Jay-Pavlina

    Jay-Pavlina

    Joined:
    Feb 19, 2012
    Posts:
    195
    I have a suggestion for a similar feature. You should allow parameters' values to be set when entering/exiting a state. You could use the same concept. For example, let's say you have bool a parameter named "isShooting" and a state named "Shooting". It would be cool if, from the inspector, you could set isShooting to true when entering the Shooting state, and set isShooting to false when exiting the shooting state.

    I've already written StateMachineBehaviours to do this for me, but it'd be useful if it was built in. It would also be useful to set the value of a parameter to the value of a different parameter and to increment and decrement ints and floats. I feel these are all related to the feature you described here. I bet you could use similar code to implement it.
     
    fahadshafique and RemDust like this.
  5. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    809
    Last edited: Jun 13, 2015
  6. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    809
    On a side note, you mention 6 new scripting members on the AnimatorState class. I see that class exists in Unity, but I have no idea how to access it (the documentation isn't much help either). There is no AnimatorController.GetState() or anything of the sort. How can I access these members, then?
     
  7. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
  8. RickyX

    RickyX

    Joined:
    Jan 16, 2013
    Posts:
    280
    Um can i just ask something. How about transition detection ? I always run into problems, and "if (Animator.IsInTransition(0) == false) " does NOT detect it at freakin all, and i always have to make some functions and invoke them for the amount of time that the anim lasts, and that gives bugs and what not. And through all theese updates, did we ever get to check if the animation is in transition to other, and/or possibly a command that will return not only the info that it's in transition, but also which state is transitioning to which state ? Thanks !
     
  9. DragonSix

    DragonSix

    Joined:
    Jul 2, 2013
    Posts:
    37
    So in short, unlike the thread title might lead to believe, we DO NOT have access to those propeties at runtime (at least through scripting AnimatorState is not).

    When do you think we will be able to? It's actually pretty important for many reasons.
     
    AaronC likes this.
  10. AaronC

    AaronC

    Joined:
    Mar 6, 2006
    Posts:
    3,552

    This sort of documentation is not helpful enough to be useful in practice..


    For anyone interested, I may have found a workaround. In my case I needed to fast forward a clip to the end, in order for props to be positioned in the right place. I sped up the entire statemachine speed, like this:

    myAnimatorCachedUsingGetComponent.speed=100;

    Then when I select that specific animation in the state machine, theres the inspector option to add a behaviour, so I made/attached a C# script with this in it:


    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    animator.speed = 1;
    }

    And I got the control I needed. Not by controlling the state, but the whole state machine unfortunately. I hope thats useful to someone.
     
    Last edited: Jul 20, 2015
  11. silviobadseed

    silviobadseed

    Joined:
    Nov 28, 2013
    Posts:
    32
    I tried to have a state with speed 0 and cycle offset 1 (which I assume is the normalized time of the clip). In this way I thought to watch the state stuck on the final frame of its clip, but it doesn't work.

    So how works that cycle offset number if I don't associate it with a parameter? And does it work only for loop animation?
     
  12. silviobadseed

    silviobadseed

    Joined:
    Nov 28, 2013
    Posts:
    32
    I solved with a workaround!

    The animation was not a loop so I duplicated, I checked the loop time in tab animation and I did the exact same thing. Having said that this is the default state (which I called Boot) where I wanted my character stopped on a precise frame/time, I set again speed 0 and cycleOffset 1 but at play the animator doesn't update the animation state. Ok I understand that speed is 0 but it's the initial state and the fact that the cycleOffset is not 0 doesn't change anything. In any case, even with 0 speed, I thought that the animator controller would have had to update at that fixed frame the animation of the character, but it doesn't work in this way (or is it supposed to work in this way?).

    So I set that animation with just one frame (the final one) and the speed to 0 without any cycleOffset. Now it works but it's an hack so it's a half win and I hope that offset in general will be used for any animation and that in any state the animator goes, even if with 0 speed, it will update at the initial frame (or blended one if it comes in blend way from an other state) of the new state.
     
  13. ZenMicro

    ZenMicro

    Joined:
    Aug 9, 2015
    Posts:
    206
    If I'm not mistaken, Cycle Offset is a frame selection as a starting point for playback, so if it is at 0 then it starts at the beginning, if you change it you are changing the start frame. It's not a normalized parameter it is a frame counter so it can be anything (float) from 0.00 to length of animation clip length. It also does not loop at the end back to this selected frame, if you need to cut out anything before the offset for looping purposes you should create a new animation clip with the time frame you require.

    If you want your animation to freeze on the last frame you need to look at what your connection (return parameters) are in Mecanim, you should be able to easily have the animation stop when it is complete if you have a bool for example that you set via anim.SetBool("exitClause", true) when you are ready to cycle back to idle or what have you.

    And there is a variable Loop which needs to be un-ticked to avoid it looping back to the beginning of the current clip.
     
    Last edited: Nov 30, 2015
  14. SkullKing

    SkullKing

    Joined:
    May 31, 2012
    Posts:
    40
    Is there a way to use this with the variable "transitionduration" in mecanim state transitions?
     
  15. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    not yet, this is on our roadmap, since 5.1 but didn't have time to implement it
     
  16. Bezzy

    Bezzy

    Joined:
    Apr 1, 2009
    Posts:
    75
    Hey there.

    I am trying to make a state which will play where it left off from when re-entered. This has been surprisingly tricky.

    I wanted to set this up so that it's just a quick little state behaviour which our animator can add, but I run into trouble not being able to set the cycleOffset at runtime. I understand that you need to attach it to a Parameter, but that means that for every state I want this, I need a unique parameter. For context, our main character animator already has a 50+ parameter list, and as you can imagine, this starts to become a bit of a management pain especially when it's just a behind the scenes change.

    My alternate solution is to call anim.Play(stateHash, layer, normalizedTimeLastReached); when I receive an OnStateEntered call, and set the normalizedTime there. Unfortunately, this function is called after the animator.Update is enabled, so I'll get one frame of frame Zero of the animation.

    To fix that, i call _animator.Update(0.0f); so that the pose is recalculated with the new normalizedTime.

    That works.

    The problem there is that I might have multiple StateMachineBehaviours which do similar (I have to do this to force an animation to scrub to a particular place in an animator state, which is a very useful tool [and which I wish was a node behaviour in a blend tree]). And as a result I'd be calling _animator.Update multiple times a frame for each _animator which uses this trick.

    So on top of that, I wrote a "DirtyAnimator" script which the above features call to signal for a recalculation will call a one off _animator.Update(0.0f) in its update() after all the other stuff is sorted (using execution order).

    I wonder if there's going to be any "OnPreStateEntered" calls, or ways to access a state's cylceOffset BEFORE the animator is updated, because I currently feel like I'm having to create this big overhead of running an animator twice. I'd be able to skip that overhead if I had more timing specific calls in the animator. It would be really incredibly useful to have a window to be able to set a normalizedTime, or offsetTime just before animators are calculated.

    Or, is there some other way to do this that I a missing?
     
  17. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    There is no Pre state entered callback.
    The only way to do this is to send the Play command from a Monobehaviour.Update which is called before the animation evaluation.
    You would need a StateMachineBehaviour that define OnStateExit(), in this callback register KeyPairValue<state hash, exitTime> in a dictionnary.
    Then from your Monobehaviour.Update when you are about to play a new state, search for your state hash and use the last exit time.

    I don't know what you statemachine look like but if you have any transition between state they will probably mess this system.
     
  18. Straafe

    Straafe

    Joined:
    Oct 15, 2012
    Posts:
    73
    Are there any complete code examples out there yet? For example, all I need to do is access the "speed" parameter of a state so I can play it backwards, and I want to do it in script rather than create a second state with the -1 entered into the inspector. I can't seem to figure out how to access it to make this simple change in script.

    Do I still have to add a new parameter for Speed and access that in script? There is no way to just access "Speed" and change it directly?
     
    Last edited: Mar 13, 2017
  19. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    You need to explicitly add a float controller's parameter and then setup all the animator state that you would like to change the speed of.

    #1 Add a float parameter, set a good default value that make sense in your case as the default value for new parameter is 0.
    floatparam.png

    #2 setup your animator state to read the speed multiplier from your newly added parameter
    stateparam.png

    #3 from script change your speed as you want
    Code (CSharp):
    1.  
    2. Animator animator = GetComponent<Animator>();
    3. float speed = ...
    4. animator.SetFloat(Animator.StringToHash("MySpeed"),  speed);
    5.  
     
    IsDon and Mikael-H like this.
  20. Straafe

    Straafe

    Joined:
    Oct 15, 2012
    Posts:
    73
    Thanks for going over that. I have a unique use case and I was hoping to be able to automate reversing animation clips via script with the only input being a single animation clip / controller. This way you have illustrated here has no advantage over simply copying and pasting the state and changing the new state's speed to -1 to play the same animation backwards, in my specific case.

    Ideally, I would like to just have the developer using my tool to simply create a single animation clip, and then playing it in reverse or forward is automated from that moment forward in code. It looks like instead he/she will have to either duplicate the state and change the speed or add the float parameter.. there isn't any other way around that is there?

    The only other way I can think of is using the time parameter of the Play() function and starting it at specific times, waiting until end of frame, then starting it again at the exact correct amount of time less based on how much time has passed to sort of force it to play backwards... and that would be horrific

    tl;dr:

    Is there really no way to play an Animator Component animation backwards without creating a new state manually or adding parameters?
     
    Last edited: Mar 14, 2017
  21. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    no there is no other way
     
  22. IsDon

    IsDon

    Joined:
    Feb 27, 2015
    Posts:
    35
    It would be great if we could add all this via an editor script, but I can't find any way to attach the parameter to the SpeedMultiplier parameter. Is there some way to do this, or another way I could approach this for setting up a speed multiplier on a series of imported gameobjects with a single animator clip each?
     
  23. mezzostatic

    mezzostatic

    Joined:
    Aug 18, 2016
    Posts:
    13
    I stayed on 5.33 because I didn't want my old codes to become obsolete and my favorite programming language (actionscript) to be made redundant, and on 5.33, I have no way of controlling the anim speed. I just tried an hour in script and in the speed. variable of my anim within unity GUI and anim never changed speed once for me. even when it's speed was set to -1.
     
  24. reuno

    reuno

    Joined:
    Sep 22, 2014
    Posts:
    4,915
    I would also very much like to know how to do this. Having to manually do the binding is not very scalable.
     
  25. max29497696

    max29497696

    Joined:
    Apr 2, 2018
    Posts:
    1
    this is helpful . thank you!!