The idle state is throwing an infinite loop between states

Hi, I was following the 2.5d rpg course, and after I did the Auto Properties class, i started to get an error that everytime that the character enter the “idle” state it start an infinite loop and i do not know why, i even copied and paste all the classes and is still not working, maybe i pressed something in the framework that broke this?, also I am getting this warning in the debbuging but that is all i am getting:
W 0:00:01:0510 _update_caches: AnimationMixer: ‘Idle’, Value Track: ‘Sprite3D:texture’ uses a non-numeric type as key value with UpdateMode.UPDATE_CONTINUOUS. This will not be blended correctly, so it is forced to UpdateMode.UPDATE_DISCRETE.
<C++ Source> scene/animation/animation_mixer.cpp:879 @ _update_caches()
that could be the problem?

Hey Nicolas,
Can you post your statemachine, along with the classes involved to I can better look at it? As well as your heirchy. Thanks in advanced!

Edit : is entirely related to the animator after looking closer at the error

Hi!! sure,

public partial class StateMachine : Node
{
    [Export] private Node currenState;
    [Export] private Node[] states;

    public override void _Ready()
    {
        currenState.Notification(GameConstants.NOTIFICATION_ENTER_STATE);
    }

    public void SwitchState<T>()
    {
        Node newState = null;

        foreach(Node state in states){
            if(state is T){
                newState = state;
            }
        }

        if(newState == null){return;}
        currenState.Notification(GameConstants.NOTIFICATION_ENTER_STATE);
        currenState = newState;
        currenState.Notification(GameConstants.NOTIFICATION_EXIT_STATE);
    }
}
public abstract partial class PlayerState : Node
{
    protected Player characterNode;
    public override void _Ready()
    {        
        characterNode = GetOwner<Player>();
        SetPhysicsProcess(false);
        SetProcessInput(false);
    }
    public override void _Notification(int what)
    {
        base._Notification(what);

        if (what == GameConstants.NOTIFICATION_ENTER_STATE)
        {
            GD.Print("Enter");
            EnterState();
            SetPhysicsProcess(true);
            SetProcessInput(true);
        }
        else if (what == GameConstants.NOTIFICATION_EXIT_STATE)
        {
            GD.Print("Exit");
            SetPhysicsProcess(false);
            SetProcessInput(false);
        }
    }

    protected virtual void EnterState(){}

}
public partial class PlayerMoveState : PlayerState
{
    [Export(PropertyHint.Range, "0,20,0.1")] private float speed = 5;

    public override void _PhysicsProcess(double delta)
    {
        if (characterNode.direction == Vector2.Zero)
        {
            characterNode.StateMachineNode.SwitchState<PlayerIdleState>();
            return;
        }

        characterNode.Velocity = new(
            characterNode.direction.X, 0, characterNode.direction.Y
        );
        characterNode.Velocity *= speed;

        characterNode.MoveAndSlide();

        characterNode.Flip();
    }

    protected override void EnterState()
    {
        characterNode.AnimPlayerNode.Play(GameConstants.ANIM_MOVE);
    }

    public override void _Input(InputEvent @event)
    {
        if (Input.IsActionJustPressed(GameConstants.INPUT_DASH))
        {
            GD.Print("dash state");
            characterNode.StateMachineNode.SwitchState<PlayerDashState>();
        }
    }
}
public partial class PlayerIdleState : PlayerState
{
    public override void _PhysicsProcess(double delta)
    {
        if (characterNode.direction != Vector2.Zero)
        {
            characterNode.StateMachineNode.SwitchState<PlayerMoveState>();
        }
    }

    public override void _Input(InputEvent @event)
    {
        if (Input.IsActionJustPressed(GameConstants.INPUT_DASH))
        {
            GD.Print("dash state");
            characterNode.StateMachineNode.SwitchState<PlayerDashState>();
        }
    }

    protected override void EnterState()
    {
        base.EnterState();
        GD.Print("idle state");
        characterNode.AnimPlayerNode.Play(GameConstants.ANIM_IDLE);
    }
}
public partial class PlayerDashState : PlayerState
{
    [Export] private Timer dashTimerNode;

    [Export(PropertyHint.Range, "0,20,0.1")] private float speed = 10;

    public override void _Ready()
    {
        base._Ready();

        dashTimerNode.Timeout += HandleDashTimeout;
    }

    protected override void EnterState()
    {
        base.EnterState();
        GD.Print("dash state");
        characterNode.AnimPlayerNode.Play(GameConstants.ANIM_DASH);
        characterNode.Velocity = new(
            characterNode.direction.X, 0, characterNode.direction.Y
        );

        if (characterNode.Velocity == Vector3.Zero)
        {
            characterNode.Velocity = characterNode.SpriteNode.FlipH ?
                Vector3.Left :
                Vector3.Right;
        }

        characterNode.Velocity *= speed;
        dashTimerNode.Start();
    }

    public override void _PhysicsProcess(double delta)
    {
        characterNode.MoveAndSlide();
        characterNode.Flip();
    }

    private void HandleDashTimeout()
    {
        GD.Print("idle state");
        characterNode.Velocity = Vector3.Zero;
        characterNode.StateMachineNode.SwitchState<PlayerIdleState>();
    }
}
public partial class Player : CharacterBody3D
{
    [ExportGroup("Required Nodes")]
    [Export] public AnimationPlayer AnimPlayerNode { get; private set; }
    [Export] public Sprite3D SpriteNode { get; private set; }
    [Export] public StateMachine StateMachineNode { get; private set; }

    public Vector2 direction = new();

    public override void _Input(InputEvent @event)
    {
        direction = Input.GetVector(
            GameConstants.INPUT_MOVE_LEFT, GameConstants.INPUT_MOVE_RIGHT,
            GameConstants.INPUT_MOVE_FOWARD, GameConstants.INPUT_MOVE_BACKWARD
        );
    }

    public void Flip()
    {
        bool isNotMovingHorizontally = Velocity.X == 0;

        if (isNotMovingHorizontally) { return; }

        bool isMovingLeft = Velocity.X < 0;
        SpriteNode.FlipH = isMovingLeft;
    }
}

and my heirchy is:
image
image
image

I just created the animationPlayer from scratch again and still i am getting this error where the enter state is getting an infinite loop, i do not know when this is happening and why


as you can see in the screen i am getting enter and exit idle state in an infinite loop (674+) updating

I got help in discord and the problem was here:

        currenState.Notification(GameConstants.NOTIFICATION_ENTER_STATE);
        currenState = newState;
        currenState.Notification(GameConstants.NOTIFICATION_EXIT_STATE);

i changed to this:

        currenState.Notification(GameConstants.NOTIFICATION_EXIT_STATE);
        currenState.Notification(GameConstants.NOTIFICATION_ENTER_STATE);
        currenState = newState;
        

and it worked :smiley:

I’m glad your issue was resolved! Thanks for posting an update (which might in turn help another stuent!) Have a great day and code code code :stuck_out_tongue:

thanks!! but now i am getting another error haha, the problem is that the dash animation is triggering and exiting really fast, and starting again an infinite loop, something is happening with my notifications i think:

public partial class PlayerIdleState : PlayerState
{
    public override void _PhysicsProcess(double delta)
    {
        if (characterNode.direction != Vector2.Zero)
        {
            characterNode.StateMachineNode.SwitchState<PlayerMoveState>();
        }
    }

    public override void _Input(InputEvent @event)
    {
        if (Input.IsActionJustPressed(GameConstants.INPUT_DASH))
        {
            
            characterNode.StateMachineNode.SwitchState<PlayerDashState>();
        }
    }

    protected override void EnterState()
    {
        base.EnterState();
       
        characterNode.AnimPlayerNode.Play(GameConstants.ANIM_IDLE);
    }
}
public partial class PlayerDashState : PlayerState
{
    [Export] private Timer dashTimerNode;

    [Export(PropertyHint.Range, "0,20,0.1")] private float speed = 10;

    public override void _Ready()
    {
        base._Ready();

        dashTimerNode.Timeout += HandleDashTimeout;
    }

    protected override void EnterState()
    {
        base.EnterState();
        
        characterNode.AnimPlayerNode.Play(GameConstants.ANIM_DASH);
        characterNode.Velocity = new(
            characterNode.direction.X, 0, characterNode.direction.Y
        );

        if (characterNode.Velocity == Vector3.Zero)
        {
            characterNode.Velocity = characterNode.SpriteNode.FlipH ?
                Vector3.Left :
                Vector3.Right;
        }

        characterNode.Velocity *= speed;
        dashTimerNode.Start();
    }

    public override void _PhysicsProcess(double delta)
    {
        characterNode.MoveAndSlide();
        characterNode.Flip();
    }

    private void HandleDashTimeout()
    {
        
        characterNode.Velocity = Vector3.Zero;
        characterNode.StateMachineNode.SwitchState<PlayerIdleState>();
    }
}
public abstract partial class PlayerState : Node
{
    protected Player characterNode;
    public override void _Ready()
    {        
        characterNode = GetOwner<Player>();
        SetPhysicsProcess(false);
        SetProcessInput(false);
    }
    public override void _Notification(int what)
    {
        base._Notification(what);

        if (what == GameConstants.NOTIFICATION_ENTER_STATE)
        {
            GD.Print("Enter");
            EnterState();
            SetPhysicsProcess(true);
            SetProcessInput(true);
        }
        else if (what == GameConstants.NOTIFICATION_EXIT_STATE)
        {
            GD.Print("Exit");
            SetPhysicsProcess(false);
            SetProcessInput(false);
        }
    }

    protected virtual void EnterState(){}

}

again!! fixed!! was easy!! hahaha always happen something like this xD

        currenState.Notification(GameConstants.NOTIFICATION_EXIT_STATE);
        
        currenState.Notification(GameConstants.NOTIFICATION_ENTER_STATE);

         currenState = newState;

i had like that and have to change it to this:

        currenState.Notification(GameConstants.NOTIFICATION_EXIT_STATE);
        currenState = newState;
        currenState.Notification(GameConstants.NOTIFICATION_ENTER_STATE);

was so easy hahaha, i was only changing the state after sending the notification, so it never changed xD

Somethign I am noticing now about how your states are handled, you should exit a state, set the new state and then enter the state. That could be causing an issue

1 Like

Ah I was just typing this hahaha good find

1 Like

thanks for all the help Foss :smiley:

1 Like

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Privacy & Terms