- Home /
Get length of animator state/transition?
There seems to have been a few queries around this area, such as:
http://forum.unity3d.com/threads/162505-Get-animation-length-in-Mecanim http://answers.unity3d.com/questions/444696/animation-length-mecanim.html
However there seems to be no answers as of yet. The problem I have is that I want to change from the state "walking" to "punch" however I need to check for button presses to see if I need to then go to "punch-2" or back to "idle".
So simplest way to achieve this seems to be change the animation, then find out how long is left in the animation, and give that as the period to listen for button presses, however it seems like the animation info Length property is not accurate, so is there some preferred method or best practice around doing this?
You can use animation events ins$$anonymous$$d. http://docs.unity3d.com/Documentation/Components/animeditor-AnimationEvents.html
@aeroson $$anonymous$$aybe it's just me but the idea of having a system where changing an animation breaks the code seems horrifying. ($$anonymous$$aybe my understanding is broken). State $$anonymous$$achine Behaviours also seem broken because their link to the monobehaviours is similarly tenuous (again maybe just my broken understanding).
Answer by Macro · Sep 04, 2014 at 04:54 PM
If it helps anyone else I ended up re-visiting this problem and the solution I found was to use both the current animation state and the current clip state (or next) to get the details on if the current state was one we cared about, and if so get the normalized time from it, then get the clip state info and get the clip from it, then multiply the clip length by normalized time to give an indication as to when the clip will be over.
var animationState = _animator.GetCurrentAnimatorStateInfo(0);
if (animationState.nameHash != YourAnimationNameHash) { return; }
var animationClips = _animator.GetCurrentAnimationClipState(0);
if (animationClips.Length == 0)
{ throw new Exception("No clips associated with animation"); }
var animationClip = animationClips[0].clip;
var animationTime = animationClip.length*animationState.normalizedTime;
I had a similar issue and I needed to calculate animation time in a loop inside a Coroutine. So my code, using your example had to look something like this:
// The code you posted here...
var animationState = _animator.GetCurrentAnimatorStateInfo(0);
if (animationState.nameHash != YourAnimationNameHash) { return; }
var animationClips = _animator.GetCurrentAnimationClipState(0);
if (animationClips.Length == 0)
{ throw new Exception("No clips associated with animation"); }
// Wait for end of frame to get proper animation length
yield return null;
var animationClip = animationClips[0].clip;
float clipLength = animationClip.length;
for (int i = 0; i < itemsToAnimate.Count; i++) {
// some code... blah blah
// Wait for next frame again
yield return null;
// Normalize animation duration
AnimatorStateInfo animStateInfo = _animator.GetCurrentAnimatorStateInfo(0);
float animDuration = animStateInfo.length - ( clipLength * (animStateInfo.normalizedTime % 1) );
yield return new WaitForSeconds( animDuration );
// Do whatever you want after this animation has looped
}
Important: Note that normalizedTime
could become a value that's not necessarily between 0~1, so that's why I use the module operator with (animStateInfo.normalizedTime % 1)
.
Answer by aeroson · Jan 30, 2014 at 02:01 PM
Seems like AnimationEvents added thru animation viewer are not compatible with mecanim.
But you can add new curves and events in the model's Animations tab. Curves and events added thru here are usable in mecanim.
It's well explained here (especially the mecanim video tutorial): http://answers.unity3d.com/questions/378420/trigger-event-on-specific-frame-of-an-animation.html
So i have added new curve called used starting at 0 ending at 1. Added new float called used in my animator controller. In my code i then did animator.GetFloat("used ") and it works !
The link provided here is very useful, will look into it more and if it solves the problem will give you the answer although it is technically not addressing the animation length problem more giving an alternative way to solve the underlying problem.
I dare to say this is much better solution than being bound to one specific animation. What if you want to change it ? You would have to change all your constants in code. This way your animation guy can easily adjust it to fit the needs.
It is not so much bound, basically in my scenario I have playmaker start an attack animation based upon the players current state, then once that begins the player can chain together combos based upon button presses. The problem I had was telling when an animation had ended so I could start the next one or revert back to idle, however its not quite as simple as that with mecanim as you are transitioning not stopping and starting. So it made more sense to get the animation length, wait for that period and see what buttons (if any) the user presses, then after that time has elapsed start the next animation, however I was not sure which was the best way to get the length. This way it is a generic action which can be applied to any animation and will wait for the elapsed time to expire before actioning anything, and does not need any variables in the animator, as from my point of view game state should be controlled by the game not the animator, so I prefer to not alter variables and things in animations however given the constraints maybe this is a more robust solution even if it mixes concerns and adds a slight level of complexity.
Upon further investigation this appears to be a pro feature, which is good, but I cannot find a way to achieve this without hard-coding in the free version of Unity.