Animator Freezing on First Frame

Hi,

I’m working on a 2D, top-down RPG and I’m trying to use the animator to change animations based on movement… I can’t figure out how to change the character to the “idle” animation when I release the movement keys…

public class player_movement : MonoBehaviour
{
    public float default_speed = 1.0f;
    public Animator anim;

    void Start(){
        anim = GetComponent<Animator>();
    }
     
    void Update () {
        Vector2 pos = transform.position;

        if (Input.GetKey ("w")) {
            anim.SetBool("walk_n", true);
            pos.y += default_speed * Time.deltaTime;
           if (Input.GetKeyUp("w"))
            {
                anim.SetBool("walk_n", false);
            }
        }
        if (Input.GetKey ("s")) {
            anim.SetBool("walk_s", true);
            pos.y -= default_speed * Time.deltaTime;
         }

        if (Input.GetKey ("d")) {
            anim.SetBool("walk_e", true);
            pos.x += default_speed * Time.deltaTime;
        }
        if (Input.GetKey ("a")) {
            anim.SetBool("walk_w", true);
            pos.x -= default_speed * Time.deltaTime;
        }
         
        transform.position = pos;
    }
}

I’ve tried to solve this (GetKeyUp?). When the bool is set to false, it should transition to the “idle_north” animation… What would be a way to accomplish this properly? Thank you.

Try using Trigger instead of Bool for transitions.

Hmm… I switched them all, and made all the correct connections with the animator… Still can’t get the idle animations to trigger…

    void Update () {
        Vector2 pos = transform.position;

        if (Input.GetKeyUp("w"))
        {
            anim.SetTrigger("trig_idle_n");
        }

        if (Input.GetKeyUp("a"))
        {
            anim.SetTrigger("trig_idle_w");
        }

        if (Input.GetKeyUp("s"))
        {
            anim.SetTrigger("trig_idle_s");
        }

        if (Input.GetKeyUp("d"))
        {
            anim.SetTrigger("trig_idle_e");
        }

        if (Input.GetKey ("w")) {
            anim.SetTrigger("trig_walk_n");
            pos.y += default_speed * Time.deltaTime;
        }

        if (Input.GetKey ("s")) {
            anim.SetTrigger("trig_walk_s");
            pos.y -= default_speed * Time.deltaTime;
         }

        if (Input.GetKey ("d")) {
            anim.SetTrigger("trig_walk_e");
            pos.x += default_speed * Time.deltaTime;
        }
        if (Input.GetKey ("a")) {
            anim.SetTrigger("trig_walk_w");
            pos.x -= default_speed * Time.deltaTime;
        }
           
        transform.position = pos;
    }

Do you have return transitions to Idle? All 4 (a,s,d,w) should have transition to them from idle on trigger, and back to idle without conditions?

Oh, just noticed that you have separate Idle for each direction.
But as you set that in code if animator is good it should work. Post screenshot of animator?

You should use Any State as root for that animator. From Any State you have transition to w walk (for example) with anim.SetTrigger(“trig_walk_n”) and from that to w idle with condition anim.SetTrigger(“trig_idle_w”);
No back transitions. Idle animations are loop and so are walk. That way player stays in idle, displays walk on keypress.
I guess that you want player character to stay in idle that corespondents to last walk direction?

1 Like

here is the animator…

yes exactly… hmm

So, Any State is the key difference. Please create new Animator don’t destroy that one since it is rather intricate.

Entry should go to some default Idle animation (with no conditions).
Then from Any State create what I described before.

1 Like

so I used all the proper triggers here, using the same code. still not working…hmm… is this what you meant?

Close but need more work.

  1. delete transition from idle_s to AnyState.
  2. for every walk_w add transition to idle_w with trigger “trig_idle_w”
    Any State transitions only to walk animations.
    Walk animations only transition to corespondent idle animations.
2 Likes

You will need Any State to some death animation for instance.

Something like this.

5012999--490592--animator.png

1 Like

That way character enters idle on start of scene. From that animation with code you provided should go to walk depending on button pressed, and from that to its idle. From that one goes to next walk depending on next button press.

You can have two different States in Animator that play same Animation.

I’m sure that this can be done with Blend Trees also Unity - Manual: Blend Trees but that is something I am not experienced with.

awesome, thank you so much for the help. it is almost there. it is moving in each direction, and it is reaching the idle states properly…however, when it does the “walk” direction, it seems to get stuck on the first frame, and it doesn’t play it out…any idea why?

I changed the code to be

 if (Input.GetKeyDown ("s")) {
            anim.SetTrigger("trig_walk_s");
            pos.y -= default_speed * Time.deltaTime;
         }

GetKeyDown seems to play the animation out, but stops the actual movement??

apparently there was something called “can transition to self” that you could check/uncheck in older versions of unity? would this fix it?

It does that because Update checks every frame what is, in this case, pressed and on every frame that the button is pressed there is trigger and plays animation according to AnyState transition. Now, this can be solved in more than one way. For instance use, yeah I know, Bool on transition to walk:

if (Input.GetKeyDown ("s")&anim.GetBool("walk_s", false))
{
    anim.SetBool("walk_s", true)
    pos.y -= default_speed * Time.deltaTime;
}
if (Input.GetKeyUp("s"))
{
    anim.SetBool("walk_s", false)
    anim.SetTrigger("idle_s");
}

Now, from AnyState transition to walk with Bool walk_s as true. From walk to idle on trigger idle_s.

Maybe, for idle to work you will have to transition to idle from AnyState directly with trigger “idle_s” in this example.
Then, maybe, you need to use Bool for idle also like this:

if (Input.GetKeyDown ("s")&anim.GetBool("walk_s", false))
{
    anim.SetBool("walk_s", true)
    anim.SetBool("idle_s", false);   
    pos.y -= default_speed * Time.deltaTime;
}
if (Input.GetKeyUp("s")&anim.GetBool("idle_s", false))
{
    anim.SetBool("idle_s", true);
    anim.SetBool("walk_s", false)
}

I’m sorry for bringing this back up, but I’ve been trying and trying different things, but this is about as close as I can get. How can I program this without the Update method resetting the animation to the first frame, making it look like it’s frozen when I hold the key down? I tried setting a private bool, etc. I’ve done a lot of research, and apparently there was something called “can transition to self” that seems like it may resolve this issue, but it doesn’t seem to be in newer versions of Unity… Any thoughts here?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;



public class player_movement : MonoBehaviour
{
    public float default_speed = 2.0f;
    public Animator anim;

    void Start(){
        anim = GetComponent<Animator>();
    }
       
    void Update () {
        Vector2 pos = transform.position;
        if (Input.GetKeyUp("w"))
        {
            anim.SetBool("walk_n", false);
        }
     
        if (Input.GetKeyUp("a"))
        {
            anim.SetBool("walk_w", false);
        }

        if (Input.GetKeyUp("s"))
        {
            anim.SetBool("walk_s", false);
        }

        if (Input.GetKeyUp("d"))
        {
            anim.SetBool("walk_e", false);
        }

        if (Input.GetKey("w")){
            anim.SetBool("walk_n", true);
            pos.y += default_speed * Time.deltaTime;
        }
        if (Input.GetKey("s")){
            anim.SetBool("walk_s", true);
            pos.y -= default_speed * Time.deltaTime;
        }

        if (Input.GetKey("d")) {
            anim.SetBool("walk_e", true);
            pos.x += default_speed * Time.deltaTime;
        }
        if (Input.GetKey("a")) {
            anim.SetBool("walk_w", true);
            pos.x -= default_speed * Time.deltaTime;
        }
           
        transform.position = pos;
    }
}