Search Unity

Scaling animation clip in time by speed

Discussion in 'Animation' started by Azonai, Apr 21, 2019.

  1. Azonai

    Azonai

    Joined:
    Jun 22, 2018
    Posts:
    1
    Hi. A little problem here. I have several spells with different casting times. (1 sec, 1.5 sec, etc. for example).
    And some casting spells animation clips with different length (2.7 sec etc.).
    Animated clips are controlled by an Animator (several layers such as "Walk", "Cast", "Idle").
    To scale clips in time, I plan to use the animator "speed" property. But I need to know the length of the clip.
    And this is a problem.
    In Update function, I change the weight of the layers depending on the state of the character:

    Code (CSharp):
    1.  public void HandleLayers()
    2.     {
    3.         if (isMoving)
    4.         {
    5.             ActivateLayer("WalkLayer");
    6.             StopAttack();
    7.         }
    8.         else if (isAttacking)
    9.         {
    10.             ActivateLayer("AttackLayer");
    11.         }
    12.         else
    13.         {
    14.             ActivateLayer("IdleLayer");
    15.         }
    16.     }
    Pressing a key causes a coroutine, like this:
    Code (CSharp):
    1.     private IEnumerator SpellCasting()
    2.     {
    3.         //Attack began
    4.         isAttacking = true;
    5.         //Set needed not looped clip
    6.         animator.SetBool("cast", isAttacking);
    7.         //Set animation speed scaled by MyCastTime and clip length
    8.         animator.speed = neededSpeed;
    9.         //Wait
    10.         yield return new WaitForSeconds(MyCastTime);
    11.        //Reset animation speed
    12.        animator.speed = 1.0f;
    13.         //StopCoroutine, set isAttacking to false etc.
    14.         StopAttack();
    15.     }
    But in this case, "animator.SetBool("cast", isAttacking);" does not guarantee that the clip will start playing immediately. Therefore, getting the length of the clip looks something like this:
    Code (CSharp):
    1. while (animator.GetCurrentAnimatorClipInfo(2).Length == 0)
    2.       {
    3.           yield return null;
    4.       }
    5. AnimatorClipInfo[] myAnimatorClip =  animator.GetCurrentAnimatorClipInfo(2);
    6. neededSpeed = MyCastTime / myAnimatorClip[0].clip.length;
    Another way is to add to each spell the "Animation" field with a link to the clip that is needed for this spell. And from there to get the length of the clip. But if I have different characters with different animations for the same spells ...
    Both ways seem wrong. Maybe someone know is a better way.
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The Animator.speed property usually isn't a very good way of doing things because it affects all animations. Specifically, it affects both the current one and the next one you are transitioning towards so during a transition you can either be at the correct speed for the previous one or the next one, but not both.

    It is possible to control the speed of individual states by creating a float parameter for each of them, selecting the state, and assigning that parameter to control its speed. Then Animator.SetFloat("ParameterName", value) will control the speed of a particular state.

    In my opinion, it's a terrible system. It requires so many tedious extra steps for something so straightforward and the end result is an inflexible mess of parameters and states that makes it hard to debug or refactor anything.

    Fortunately I don't have to deal with that system anymore and you don't either if you use my Animancer plugin (link in my signature). It lets you write code like this:

    Code (CSharp):
    1. var state = _AnimancerController.Play(AnimationClip);
    2. state.Speed = ...
    3. state.Time = ...
    4. state.NormalizedTime = ...
    5. state.OnEnd = ...
    6. // Store the state in a field to access it later.
    7. // Enable/disable IK.
    8. // Etc.
    No spaghetti AnimatorControllers where you have to define all your states and transitions in a huge mess which can't be changed at runtime. No magic strings that require you to just hope you spell everything correctly and guess what will be affected whenever you change anything. No extra steps to set things up to be controlled. Just a simple script that says "I have an AnimationClip, play the damn clip with these parameters".
     
    Azonai likes this.