Search Unity

Question Animations with variable distance

Discussion in 'Animation' started by chusmaverde, Oct 20, 2022.

  1. chusmaverde

    chusmaverde

    Joined:
    Jun 10, 2013
    Posts:
    29
    Hello everyone, I am designing a new climb controller and I got all the logic ready. The only thing I have missing is the animation part. Simplifying to the core issue:
    • I have an animation to start climbing (e.g. I am using Mixamo "Idle to braced Hang").
    • How can I use such animation to reach the end at different heights (e.g. height of 2 or 3 meters)?
      • If I move the character (Lerp) with a script then the animation is not synced to the floor nor edge to grab.
      • If I use the animation motion, then I am stuck with a fixed height.
    The only solutions I have thought of are:
    • Option A) Add events to the animations so i can apply root motion or script movement.
    • Option B) Separate the animation into 3 linked animations:
      1. Static animation until player is in the air.
      2. Jumping controlled by scripting Lerp or similar
      3. Static animation having the player finalize the grabbing.
    It feels a bit overkill to do any of these for every single animation. Is there a better way to achieve this dynamic movement?

    Thank you for your time reading this :)
     
    DragonCoder likes this.
  2. chusmaverde

    chusmaverde

    Joined:
    Jun 10, 2013
    Posts:
    29
    After doing some research on this I will add the possibilities I have found with a bit of detail (for anyone that might find this useful in the future):
    • Animation events: This will allow for creating certain logic in which the movement works differently. For example we can add velocity to the rigidbody to move the character from Event A to Event B. I am not a fan of this as it detaches the logic of the movement to an obscure part of the code that its only referenced as an event in the animation (impossible to track its usage in code).
    • Separate Animations: This solves the problem, but it makes the animation graph much more complex and probably adds more animation parameters.
    • Control movement via script (root motion off): Here the ideal thing is to use lerp + animation curves. Lerp will smoothly transition the movement, while the animation curve will "stop" or move the object however you need.
      • Lerp: To use Lerp properly check this post I found. It has a great understanding on how to do that properly. In the end is to smoothly move from A to B.
      • Animation Curves (Unity reference): they are insanely powerful for this case. They will stop movement/ accelerate forward/ backwards/ etc., depending on your needs. Matching the animation movement to the animation curve is complicated but doable. The major issue is that you will need a specific animation curve depending on the movement you need and tweaking it might be a bit tedious.
    My final approach was to use animation curves via script. It is not perfect but I do not have the perfect animations either, so I am just getting the best approximation I can get without having to create new animations that fit all my needs.
     
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    This is a hard problem!

    You want the movement to come from the animation, because the animator (hopefully!) has a good grasp of how the weight and movement should work. But you can't make a unique animation for every jump in the game.

    Your final approach is a good one. What we've done in order to let the animator control this in some circumstances is to read off the root motion from the animation in order to get a movement curve, transfer that to a script, and then scale that curve to get the correct distance.

    If you have a lot of animations, this will give you better quality for a lot less effort than hand-tuning animation curves.
     
  4. bobadi

    bobadi

    Joined:
    Jan 3, 2019
    Posts:
    670
    also there's targetmatching for rootmotion jumps, which is like lerp but can set bodypart (avatartarget) which should be at the target position (sure you can calculate this with lerp too, but need to know the position offset of the bodypart at target normalized time. and can combine lerp with animation curve too)

    I wouldn't use animation events (and I don't), all you need to know is current state time, which you get with animatorStateInfo
     
  5. chusmaverde

    chusmaverde

    Joined:
    Jun 10, 2013
    Posts:
    29
    Thanks a lot for that input @Baste. I did not think of the possibility to obtain the translation curves directly from the animator. I have tried to do that but I am having some issues. I do manage to obtain the current animation being played, but it is always the previous to the jumping animation I need. I have tried forcing the animator to update but nothing changes. This is the code I am testing (I have simplified it to have only the relevant information):

    Code (CSharp):
    1. //I believe these are all the needed usings:
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using System.Linq;
    6. //...
    7.  
    8. void FixedUpdate
    9. {
    10.   //Do the the lerping once StartClimb has been called
    11.   //(I have removed the logic of this as it is not relevant)
    12. }
    13.  
    14. public void StartClimb()
    15. {
    16.   //Update animator variables:
    17.   _animator.SetTrigger("Climb_Start"); //This will change the state to start climb      
    18.   _animator.applyRootMotion = false;
    19.   _animator.Update(0.0f); //Shouldn't this force the animator to change state?
    20.  
    21.   //Obtain desired curve:
    22.   var animationClipInfo = _animator.GetCurrentAnimatorClipInfo(0);
    23.   var clip = animationClipInfo[0].clip;
    24.   var clipName = animationClipInfo[0].clip.name;
    25.   Debug.Log("Clip name: " + clipName); //This is always the previous animation
    26.  
    27.   _curve = GetAnimationCurve("RootT.x", clip); //member variable to be used in FixedUpdate function.
    28. }
    29.  
    30.  
    31. //This will get the desired animation curve
    32. private AnimationCurve GetAnimationCurve(string bone, AnimationClip clip)
    33. {
    34.   var bindings = AnimationUtility.GetCurveBindings(clip);
    35.   var curveBinding = bindings.Where(cb => cb.propertyName.Contains(bone)).First();
    36.  
    37.  return AnimationUtility.GetEditorCurve(clip, curveBinding);
    38. }
    39.  
    Once the function "StartClimb()" has been called the update function will do the Lerping using the animation curve... or at least that is the idea. So the main question here is:
    • I believe my logic makes sense, but how can I obtain the correct animation from "_animator.GetCurrentAnimatorClipInfo(0)"? Alternatively I can always have a member variable with the desired animation and reference that... but it feels dirty.
     
  6. chusmaverde

    chusmaverde

    Joined:
    Jun 10, 2013
    Posts:
    29
    Thanks for the information! I did not know about this, but from what I could read in the documentation root motion should be enabled, and I have it disabled. I need to investigate this more to see if it might be useful in my case
     
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    So, yeah, you can't get the curves directly from the animator. When we do this we parse the animation at edit time and store the relevant extracted curves in a ScriptableObject.

    For getting the name of the current target animation - ugh. I think what you're looking for is https://docs.unity3d.com/ScriptReference/Animator.GetNextAnimatorClipInfo.html, as that gives you information about the clips you're on the way into. The Animator's API for getting out information is, and has been since day 1, verbose, undocumented, and needlessly confusing.