Search Unity

Animation transition duration seems to fail (should be 1s - looks like 0s)

Discussion in 'Animation' started by Tech-Labs, Jun 14, 2018.

  1. Tech-Labs

    Tech-Labs

    Joined:
    Feb 5, 2014
    Posts:
    105
    Hi,
    I've got this very simple Animator Controller set up.
    I've got a 'Body Idle' state and a 'Body Talking' state. Both are loaded with clips during run time using the AnimatorOverrideController. That works fine.
    I'm using a simple boolean parameter to switch between the states. If 'true' then start the talking animation, if 'false' then start the idling animation.
    Both transitions are exactly the same (apart from the bool being true in one and false in the other). Not Exit time, Fixed duration of 1s. All default settings.
    When I set the Bool to 'true' the transition looks fine.
    When I then set the Bool (back) to 'false' there is no transition! The idle animation just starts within 1 frame making the transition look extremely abrupt.
    Any ideas why this could happen?

    Cheers!

    Marco

    [Flow]
    1 load idle animation clip in the Idle State at start using AnimatorOverrideController
    2 start the idle animation by setting the bool 'start' to 'true'
    3 load the talking animation clip in the Talk State at some point using AnimatorOverrideController
    4 set the bool 'startTalkin' to 'true' to start the talking animation using the 1 second fixed duration transition
    5 wait until the audio (started at 4) is done
    6 load idle animation clip using AnimatorOverrideController into the Idle State
    7 set the bool 'startTalking' to 'false' to start the idle animation using the 1 second fixed duration transition
     

    Attached Files:

  2. Tech-Labs

    Tech-Labs

    Joined:
    Feb 5, 2014
    Posts:
    105
    Addition:
    When I just hard set two animation clips in the Animator states, the transitions work fine. Both the 'to-talking' and the 'to-idle' nicely blend over 1s.
    So it seem to do with the animator override.
    However, like I said I use the exact same code to load the two clips for the talking animation as for the idling animation. I use the same BOOL parameter to switch between the states.

    The code that calls the override method and loads the talking animations (head+body):
    Code (CSharp):
    1. LoadAnimations(false, bodyanim, theAgent[agentReaction].agent_headanim);//Body layer with name
    2. agentAnimator.SetBool("startBody", true);
    3. agentAnimator.SetBool("startHead", true);
    The code that call the override method and loads the idling animations:
    Code (CSharp):
    1. LoadAnimations(true, type_idle + agression, type_idle + agression); //load the correct idle animations
    2. agentAnimator.SetBool("startBody", false); //Play the idles
    3. agentAnimator.SetBool("startHead", false);
    The Animator Override code:
    Note: I preload the animation clips into clp at the start using Resources.LoadAll().
    Code (CSharp):
    1. void LoadAnimations(bool idle, string bodyAnimName, string headAnimName) //Replace head and body animations
    2.     {
    3.         RuntimeAnimatorController myController = agentAnimator.runtimeAnimatorController; //Copy the current animator controller
    4.         AnimatorStateInfo[] layerInfo = new AnimatorStateInfo[agentAnimator.layerCount]; //Create animation layer array
    5.         AnimatorOverrideController animatorOverride = new AnimatorOverrideController(myController); //This overrider will hold the changes in clips
    6.         for (int i = 0; i < agentAnimator.layerCount; i++)
    7.         {
    8.             layerInfo[i] = agentAnimator.GetCurrentAnimatorStateInfo(i); //Store each layer animation state
    9.         }
    10.         if (animatorOverride!= null)
    11.         {
    12.             myController = animatorOverride.runtimeAnimatorController; //Push the overrider in the controller copy
    13.         }
    14.         if (idle)
    15.         {
    16.             foreach (AnimationClip clp in bodyIdleClips) //Search for the correct clip in memory
    17.             {
    18.                 if (clp.name == bodyAnimName)
    19.                 {
    20.                     animatorOverride["body_idle_empty"] = clp; //Override it
    21.                     break;
    22.                 }
    23.             }
    24.             foreach (AnimationClip clp in headIdleClips)
    25.             {
    26.                 if (clp.name == headAnimName)
    27.                 {
    28.                     animatorOverride["head_idle_empty"] = clp;
    29.                     break;
    30.                 }
    31.             }
    32.         }
    33.         else
    34.         {
    35.             foreach (AnimationClip clp in bodyTalkClips)
    36.             {
    37.                 if (clp.name == bodyAnimName)
    38.                 {
    39.                     animatorOverride["body_talk_empty"] = clp;
    40.                     break;
    41.                 }
    42.             }
    43.             foreach(AnimationClip clp in headTalkClips)
    44.             {
    45.                 if (clp.name == headAnimName)
    46.                 {
    47.                     animatorOverride["head_talk_empty"] = clp;
    48.                     break;
    49.                 }
    50.             }
    51.         }
    52.  
    53.         agentAnimator.runtimeAnimatorController = animatorOverride; //Push the override into the current Animator controller
    54.         for (int i = 0; i < agentAnimator.layerCount; i++)
    55.         {
    56.             agentAnimator.Play(layerInfo[i].fullPathHash, i, layerInfo[i].normalizedTime); // Push back layer states
    57.         }
    58.         animatorOverride = null;
    59.     }
    So why would it work one way, but not the other (back) way?

    Marco
     
  3. Tech-Labs

    Tech-Labs

    Joined:
    Feb 5, 2014
    Posts:
    105
    SOLVED
    Okay, I solved this myself :)
    I was using a perfectly working Animator Override method that I got from one of the earlier threads here on the forum. It works fine on an other project, but apparently not in this one (for no clear reason to me).
    So I decided to use the default code from the manual for updating multiple animation clips in one frame.

    Code (CSharp):
    1.     protected AnimatorOverrideController myOverride;
    2.     protected AnimationClipOverrides clipOverrides;
    3.         myOverride = new AnimatorOverrideController(agentAnimator.runtimeAnimatorController);
    4.         agentAnimator.runtimeAnimatorController = myOverride;
    5.         clipOverrides = new AnimationClipOverrides(myOverride.overridesCount);
    6.         myOverride.GetOverrides(clipOverrides);
    7. if (idle)
    8.         {
    9.             foreach (AnimationClip clp in bodyIdleClips) //Search for the correct clip in memory
    10.             {
    11.                 if (clp.name == bodyAnimName)
    12.                 {
    13.                     clipOverrides["body_idle_empty"] = clp; //Override it
    14.                     break;
    15.                 }
    16.             }
    17.             foreach (AnimationClip clp in headIdleClips)
    18.             {
    19.                 if (clp.name == headAnimName)
    20.                 {
    21.                     clipOverrides["head_idle_empty"] = clp;
    22.                     break;
    23.                 }
    24.             }
    25.         }
    26.         else
    27.         {
    28.             foreach (AnimationClip clp in bodyTalkClips)
    29.             {
    30.                 if (clp.name == bodyAnimName)
    31.                 {
    32.                     clipOverrides["body_talk_empty"] = clp;
    33.                     break;
    34.                 }
    35.             }
    36.             foreach (AnimationClip clp in headTalkClips)
    37.             {
    38.                 if (clp.name == headAnimName)
    39.                 {
    40.                     clipOverrides["head_talk_empty"] = clp;
    41.                     break;
    42.                 }
    43.             }
    44.         }
    45.         myOverride.ApplyOverrides(clipOverrides);
    Now it works fine!
     
    radiantboy likes this.
  4. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    Awesome, thanks, I needed the exact same. The only problem is I still see a jerk when the animations change..

    I have 10 new idles and i swap a random one in when the current idle ends (using an animation event to tell me when its ended) then I do the above code to swap the new one. Which works but as I say there's a jerk, is there a way to smoothly go from one to another?