Search Unity

Non-Linear Animation Transition

Discussion in 'Animation' started by launzone, Feb 13, 2020.

  1. launzone

    launzone

    Joined:
    Dec 2, 2015
    Posts:
    57
    Is it possible to make a non-linear Animation Transition? (like using an ease function to set the current point of the transition).

    If you want to read on, I will just list a couple of ways that I tried already:

    I know that I can create "animator.CrossFade" at runtime and set the transition duration through code, but as far as I know it still doesn't allow me to set the current time.
    Another option that I was trying was using a BlendTree. I can adjust the blend value through code so it works fine to ease between 2 animations but I can hardly make this work for a character with a lot of different poses to blend between. I would need to create blend trees and blend parameters for every possible combination and that just doesn't seem practical at all.
    Also, I saw that with "AnimatorTransitionInfo.normalizedTime" you can GET the time of the transition but you can't SET it. I think this would actually be the value that I would need to access to achieve what I want.

    Any ideas would be highly appreciated! Thank You!
     
    DragonCoder likes this.
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Check out Animancer (link in my signature). It lets you get and set whatever animation details you want so you could update the weights over time to use whatever easing function you want. Implementing an easier way of setting up transition curves is on my list of things to consider for the next major version, but that won't be any time soon since I only just released v4.0.
     
    launzone likes this.
  3. launzone

    launzone

    Joined:
    Dec 2, 2015
    Posts:
    57
    Thank you, I just took a look at your Plugin and it looks promising! I would still be interested in a way to do this without 3rd party solutions if anyone has an idea.
     
  4. mcmount

    mcmount

    Joined:
    Nov 15, 2015
    Posts:
    83
    I think this should be a built in feature. It's bit annoying to create extra animations to cover the linear transition behavior or mask it with layers.
     
    GGsparta and launzone like this.
  5. launzone

    launzone

    Joined:
    Dec 2, 2015
    Posts:
    57
    hi, i actually got animancer pro finally. it seems great and the documentation is very detailed! i am currently working on porting our old animation system to use animancer. now back to my old question, how do i set the weights in a transition from 1 animation to another?
     
  6. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You can access any states via various methods in animancerComponent.States and you can simply set their Weight property.
     
    launzone likes this.
  7. launzone

    launzone

    Joined:
    Dec 2, 2015
    Posts:
    57
    Thank you very much! So i understand it basically, I don't want to take too much of your time but I would have another question : What would be the best way to handle adjusting the weights of the current state that i am in and the state that i want to switch to? (or if it's better: the last state that I was in and the new state) Right now i set it up in a way where i have a lot of Animationclips referenced in a script and i am passing it to the HybridAnimancerComponent by playing the referenced clips. This works but I still don't know a good way to then change the weights of the right states once I start playing an Animation. As far as i know i can't get the state based on a clip that it is using? Would I use transitions for what I want to achieve?
     
  8. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    animancer.States[clip] should give you the state playing that clip if it exists.

    Making a custom transition class might be a convenient way to save the curve you want beside the other details (clip, speed, start time, etc.). The Root Motion example shows how you can do that.

    The easiest way of keeping track of the states might be to just make your own Play method. animancer.States.Current is the state you most recently told it to play so whenever you want to do a curved fade you could store the current state in a list (in case you start another fade before the previous one ends) before telling it to play the new one. Every update you change the weight of the current state based on the curve and fade out everything in the list inversely. Then just clear the list if the fade completes or if you do a regular fade.
     
    launzone likes this.
  9. launzone

    launzone

    Joined:
    Dec 2, 2015
    Posts:
    57
    awesome, that helps me a lot! thank you!
     
  10. launzone

    launzone

    Joined:
    Dec 2, 2015
    Posts:
    57
    okay, thank you again Kybernetik for the tool you provided and your help, I managed to create an EasedTransition function that I use to switch between poses. If anyone ends up here looking for something similar I am just going to post my solution:

    Code (CSharp):
    1. public void EaseTransition (AnimationClip clip, float duration) {   // function to start transition coroutine
    2.         if (transCR!=null) {
    3.             StopCoroutine(transCR);
    4.         }
    5.  
    6.         oldStates_StartWeights.Clear();
    7.         oldStates_CurrentWeights.Clear();
    8.  
    9.          // old states:
    10.      
    11.         AnimancerState oldState = animancer.States.Current;
    12.         oldStates.Add(oldState);    // list of old states is necessary in case old transition is not finished yet when new one starts
    13.         for (int i = 0; i < oldStates.Count; i ++) {
    14.             oldStates_StartWeights.Add(oldStates[i].Weight);
    15.             oldStates_CurrentWeights.Add(oldStates_StartWeights[i]);
    16.         }
    17.  
    18.         animancer.Play(clip);
    19.         AnimancerState newState = animancer.States[clip];
    20.  
    21.         transCR = StartCoroutine(EaseTransitionCR(duration, newState));
    22.     }
    23.  
    24.     public IEnumerator EaseTransitionCR(float duration, AnimancerState newState) {   // actual transition coroutine
    25.         float timeElapsed_ = 0f;
    26.  
    27.         // new state:
    28.  
    29.         float newState_StartWeight = 0f;
    30.  
    31.         for (int i = 0; i < oldStates.Count; i ++) {
    32.             if (oldStates[i] == newState) {
    33.                 newState_StartWeight = oldStates_CurrentWeights[i];
    34.  
    35.                 Debug.Log("REMOVED : : : " + oldStates[i] + " WEIGHT : : : " + oldStates_CurrentWeights[i]);
    36.  
    37.                 oldStates_StartWeights.RemoveAt(i);
    38.                 oldStates_CurrentWeights.RemoveAt(i);
    39.                
    40.                 oldStates.RemoveAt(i);
    41.                 break;
    42.             }
    43.         }
    44.  
    45.         float newState_CurrentWeight = newState_StartWeight;
    46.        
    47.         easeFunction = EasingEquations.Easing.GetFunctionWithTypeEnum (easeType_PoseTrans);
    48.  
    49.         float oldWeightsSum = 0f;
    50.  
    51.         for (int i = 0; i < oldStates.Count; i ++ ) {
    52.             oldWeightsSum+= oldStates_StartWeights[i];
    53.         }
    54.  
    55.         while (timeElapsed_ <= duration) {
    56.             timeElapsed_ += Time.deltaTime;
    57.            
    58.             float easedProgress = easeFunction (timeElapsed_ / duration);
    59.  
    60.             newState_CurrentWeight = Mathf.Lerp(newState_StartWeight, 1f, easedProgress);
    61.  
    62.             float oldWeightLeft = 1f - newState_CurrentWeight;
    63.  
    64.             newState.Weight = newState_CurrentWeight;
    65.  
    66.             for (int i = 0; i < oldStates.Count; i ++) {
    67.                
    68.                 oldStates_CurrentWeights[i] = oldWeightLeft * oldStates_StartWeights[i]/ oldWeightsSum;
    69.  
    70.                 oldStates[i].Weight = oldStates_CurrentWeights[i];
    71.             }
    72.  
    73.             yield return 0;
    74.         }
    75.  
    76.         for (int i = 0 ; i < oldStates.Count; i ++) {
    77.              oldStates[i].Weight = 0f;
    78.         }
    79.  
    80.         oldStates.Clear();
    81.         oldStates_StartWeights.Clear();
    82.         oldStates_CurrentWeights.Clear();
    83.         yield return null;
    84.     }
     
    Kybernetik likes this.
  11. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    still no way to do this in Unity be default? Seems pretty standard...
     
  12. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    @launzone In case you weren't aware, Animancer v5.0 includes a Custom Fade system that makes this sort of thing very easy.
     
    launzone likes this.
  13. launzone

    launzone

    Joined:
    Dec 2, 2015
    Posts:
    57
    just saw this, thank you kybernetik! ill see if i can find the time to refactor the transitions in our project.
     
  14. GGsparta

    GGsparta

    Joined:
    Aug 25, 2017
    Posts:
    14
    Anything new for a built-in feature? I don't feel like it is much to create our own curve for animation transitions...

    @Kybernetik thank's for your work, I will give it a try... Do you plan to setup a "Package Manager friendly" access to your Animancer?
     
  15. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I'm not sure if that's possible for assets on the Asset Store and maintaining two different folder structures at once would be annoying. If you know of a way to do it for Asset Store uploads I'll take a look at it though.
     
  16. GGsparta

    GGsparta

    Joined:
    Aug 25, 2017
    Posts:
    14
    @Kybernetik I don't know about the Asset Store, but I noticed you have a GitHub link for bug reports. The Package Manager allows you to add git projects as a package, which is really useful for managing dependencies, optimizing compilation, and sorting content for their usage in a built-in way. Using git, you can also manage versions by dedicating a branch to a version... Then you can share your package just by a link people can add to their manifest, like: https://github.com/KybernetikGames/animancer.git#7.0.0

    How to set up the package: https://docs.unity3d.com/Manual/CustomPackages.html
    The structure of the GIT project: https://docs.unity3d.com/Manual/cus-layout.html

    However, it takes some time to set up everything...
     
  17. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    That git repo just hosts the documentation, not the actual package.

    And I can't see anything there about the Asset Store so I'm assuming it has no way to handle paid assets. There wouldn't be any point in doing it just for Animancer Lite if you need to use the old Plugins folder when updating to Animancer Pro.
     
    GGsparta likes this.
  18. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    You can also use unity animations and manually control the time from script. that is the best we have been able to do.
     
  19. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Fiddling with the animation time can't help with creating non-linear transitions. You need to fiddle with the blend weights, which Animator Controllers don't allow you to do unless you put your animations in a Manual Blend Tree.
     
  20. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    ah lol it's been a while since I read the whole thread and just commented from what I remembered
     
  21. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    651
    Ok but are extrapolations supported? Because weight is a value between 0 and 1 ... and the overgrowth demo wouldn't be possible without extrapolating animation (pose) transitions.

    I already bought your product (for multiple reasons), now I'd like to create something similiar to the Overgrowth Animation System -> (video timestamp 7:37)

     
  22. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The Playables API will extrapolate positive weight values, but negatives get clamped to 0.
     
  23. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    813
    Did anyone figure a way to do this without Kybernetik's Animancer plugin?

    I have nothing bad to say about that particular plugin (haven't tried it), but we already have a full animation pipeline in place with animator controllers already setup, and it wouldn't be worth the effort to import a third-party solution just to get non-linear animator transitions.
     
  24. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
  25. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,701
    Am assuming it is still status quo that Unity does not have this built in? :(
    It would make a significant difference. Cannot throw all the tutorials on Mecanim into the wind and use the 3rd party plugin just for this.
     
  26. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,799
    Well for the animator to get new features they need someone working on the animator and they are working on new animation tools instead for years now.

    And in the meantime, low hanging fruit like this feature, remain unimplemented.