Problem with Player Animation Transition Sticking in Unity

Hello everyone,

I’m currently working on a 2D platformer and managing player animations using a custom script. My problem is that the player’s animation gets stuck in the walking animation (WALK_ANIMATION) even when the player is idle or stops moving.

Here’s the logic I’m using in my PlayerAnimationsManager script (full code below):

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

public class PlayerAnimationsManager : MonoBehaviour
{
    [Header("Important-References")]
    private Animator PlayerAnimation;
    private PlayerMovement playerMovementScriptsRef;
    private SpriteRenderer PlayerSprite;

    // Konstanten für Animationsnamen
    private const string IDLE_ANIMATION = "NinjaPlayerIdle";
    private const string WALK_ANIMATION = "NinjaWalkRight";
    private const string NORMAL_ATTACK = "NinjaAttackRight";

    private const float DashEffectDuration = 0.2f;
    private static readonly Color DashColor = Color.red;

    private string currentAnimationState = IDLE_ANIMATION; // Speichert den aktuellen Animationszustand

    void Awake()
    {
        PlayerAnimation = GetComponent<Animator>();
        playerMovementScriptsRef = FindObjectOfType<PlayerMovement>();
        PlayerSprite = GetComponent<SpriteRenderer>();
    }

    void Update()
    {
        // Animationen und andere Aktionen verwalten
        PlayerAnimationManager();
        ChangeSpriteOnDash();
    }

    private void PlayerAnimationManager()
    {
        // Priorisiere Angriffsanimationen
        if (AttackSystemPlayer.PlayerHeavyAttacked && Input.GetMouseButtonDown(0))
        {
            StartCoroutine(PlayFirstAttackAnimationAndWait());
            return; // Andere Animationen ignorieren
        }

        // Blockiere andere Animationen, wenn der Angriff aktiv ist
        if (PlayerAnimation.GetCurrentAnimatorStateInfo(0).IsName(NORMAL_ATTACK))
        {
            return;
        }

        // Spieler steht oder kann nicht laufen
        if (playerMovementScriptsRef.moveDirectionX == 0 || !playerMovementScriptsRef.allowRunning)
        {
            ChangeAnimation(IDLE_ANIMATION);
            return; // Verlasse die Methode, da der Spieler stillsteht
        }

        // Wenn beide Tasten gleichzeitig gedrückt werden, stoppe die Bewegung und gehe in Idle
        if (Input.GetKey(KeyCode.A) && Input.GetKey(KeyCode.D))
        {
            ChangeAnimation(IDLE_ANIMATION);
            playerMovementScriptsRef.moveDirectionX = 0; // Stoppe die Bewegung
            return;
        }

        // Bewegung nach rechts
        if (Input.GetKey(KeyCode.D) && playerMovementScriptsRef.allowRunning && playerMovementScriptsRef.moveDirectionX > 0)
        {
            ChangeAnimation(WALK_ANIMATION);
            PlayerSprite.flipX = false; // Spiegelt den Spieler nach rechts
        }
        // Bewegung nach links
        else if (Input.GetKey(KeyCode.A) && playerMovementScriptsRef.allowRunning && playerMovementScriptsRef.moveDirectionX < 0)
        {
            ChangeAnimation(WALK_ANIMATION);
            PlayerSprite.flipX = true; // Spiegelt den Spieler nach links
        }
    }

    private void ChangeSpriteOnDash()
    {
        if (Input.GetKeyDown(KeyCode.Space) && playerMovementScriptsRef.allowDashing)
        {
            PlayerSprite.color = DashColor;
            Debug.Log("Der Spieler ist nicht mehr zu sehen!");
            StartCoroutine(PlayerSpriteTrue());
        }
    }

    private IEnumerator PlayFirstAttackAnimationAndWait()
    {
        // Starte die Angriffsanimation
        ChangeAnimation(NORMAL_ATTACK);
        Debug.Log("Der Spieler greift an");

        // Warte, bis die Animation vollständig abgespielt ist
        while (PlayerAnimation.GetCurrentAnimatorStateInfo(0).IsName(NORMAL_ATTACK) &&
               PlayerAnimation.GetCurrentAnimatorStateInfo(0).normalizedTime < 1.0f)
        {
            yield return null; // Warten auf den nächsten Frame
        }

        Debug.Log($"Animation {NORMAL_ATTACK} beendet.");
    }

    // Coroutine, die das Sprite nach einer kurzen Verzögerung wieder anzeigt
    private IEnumerator PlayerSpriteTrue()
    {
        yield return new WaitForSeconds(DashEffectDuration);
        PlayerSprite.color = Color.white;
    }

    private void ChangeAnimation(string animationName)
    {
        // Wechsel nur, wenn die Animation eine andere ist
        if (currentAnimationState != animationName)
        {
            Debug.Log($"Wechsle Animation: {currentAnimationState} -> {animationName}");
            PlayerAnimation.Play(animationName);
            currentAnimationState = animationName;
        }
    }
}


  1. If the player is not moving (moveDirectionX == 0), I switch to the idle animation.
  2. If the player moves left or right, the walking animation should play.
  3. I prioritize attack animations so they block other animations until they are completed.

The transitions seem fine, but when the player stops moving, the idle animation (IDLE_ANIMATION) doesn’t consistently trigger. Instead, the player remains in the walking animation.

Here’s the full script for context:

Things I’ve checked:

  • The animations in the Animator Controller are correctly named (NinjaPlayerIdle, NinjaWalkRight, etc.).
  • Debugging shows that the ChangeAnimation function is being called with the correct animation names (IDLE_ANIMATION when stopping, for example).
  • PlayerMovement is sending the correct moveDirectionX values, and allowRunning works as expected.

What I suspect:

  • It might be related to the Animator Controller or transitions overriding the Play() method.
  • Possibly, the PlayerAnimation.GetCurrentAnimatorStateInfo(0) is interfering when switching animations.

I’d appreciate any advice on what might be causing this or if there’s something I’m missing in my logic or Unity’s Animator behavior.

Thanks in advance for your help!

Always nail down the Animator state machine FIRST, get that perfect.

Anything with Animations / Animators / Mechanim:

Only consider the code AFTER you have done this critical step:

Always start with the Animator state machine and prove it works in isolation, no code at all.

Here’s more reading:

Then debug it and find out!! But do the basic animator check above first.

Sounds like you wrote a bug… and that means… time to start debugging!

By debugging you can find out exactly what your program is doing so you can fix it.

Use the above techniques to get the information you need in order to reason about what the problem is.

You can also use Debug.Log(...); statements to find out if any of your code is even running. Don’t assume it is.

Once you understand what the problem is, you may begin to reason about a solution to the problem.

Remember with Unity the code is only a tiny fraction of the problem space. Everything asset- and scene- wise must also be set up correctly to match the associated code and its assumptions.

Not sure since I just skimmed your code, but I would assume you’re just calling Play to switch to the idle state every single frame. In situations like this, I think a lot of your bugs could be cleared up by introducing a parameter called Speed and combining your Idle and Walk states into a 1D blendtree blended on the speed param. Can’t have any transition bugs if there’s no transitions. :wink: