What's the best way to trigger an animation on diagonal movement?

Godot Version

4.6

Question

I can make my player move with correct animations in 4 directions, but when moving diagonally, the sprite will move, but only with a static frame, no animations. I’ll post the code I have (it’s copy/pasted from the tutorial).

func _process(delta):
	var velocity = Vector2.ZERO # The player's movement vector.
	if Input.is_action_pressed("move_right"):
		velocity.x += 1
	if Input.is_action_pressed("move_left"):
		velocity.x -= 1
	if Input.is_action_pressed("move_down"):
		velocity.y += 1
	if Input.is_action_pressed("move_up"):
		velocity.y -= 1

I was trying to combine if statements as follows:

if Input.is_action_pressed("move_left") and Input.is_action_pressed("move_up"):
	    velocity.y += 1
	

Then I wanted to use the following code to trigger the animation:

if velocity.y < 0:
		$AnimatedSprite2D.animation = "run_down"
	elif velocity.y != 0:
		$AnimatedSprite2D.animation = "run_up"	

Basically, I wanted the run_up animation to trigger anytime the y velocity is greater than zero. I want run_down to trigger anytime the y velocity is less than zero. The run_right and run_left animations would follow the same idea.

The engine was not impressed.

Is there a quick way to do this, or do I have to combine a bunch of elif statements?

So which animation should play when going right-up? Right or up?

The up animation is what I want to trigger.

Any time I’m going up, I want the up animation to trigger.

Any time I’m going down, I want the down animation to trigger.

Then first check if you’re going left or right and start that animation. Then you check if you’re going up or down and trigger a corresponding animation. This way you establish the priority of up-down over left-right, and up-down will always override left-right.

So,

if velocity.x <0
   $AnimatedSprite2D.animation="run_left"
elif velocity.x!=0
   $AnimatedSprite2D.animation="run_right
   elif velocity.y !=0
        $AnimatedSprite2D.animation="run_up"

Is that right?

Edit: No, that can’t be right. I’m doing something wrong.

Just 4 ifs for 4 directions should do it, ordered by the priority so the next one will always override the previous one.

Isn’t that what I have in the first code, though?

That part works.

The 4 directions work fine. It’s when I have 2 buttons pressed for diagonal movement that the animation refuses to trigger.

No it isn’t because you don’t have 4 ifs that test the direction of the velocity.

So what if statement would test the velocity?

You started well with the last snippet you posted. Test for each direction like you’re tesing for -x, assign the corresponding animation and you’re done.

I still don’t understand fully, but thank you for your help.

I was trying to see if I could just create an input map for a combination like W+A and then have the code look for that movement to trigger an animation, but it doesn’t seem like that is possible.

Relying on input is not the best approach because the player may hold up and down at the same time which results in no vertical movement. If you rely only on input state you may miss that.

So a better approach is to first figure out the velocity vector depending on input, which you’re already doing, and then set the animation depending on the velocity.

Right, but that only seems to work for single inputs. When there are two buttons being pressed, the movement will occur, but not the animation.

For instance, here is the code that’s in there now for the animation triggers:

if velocity.x < 0:
		$AnimatedSprite2D.animation = "run_left"
	elif velocity.x != 0:
		$AnimatedSprite2D.animation = "run_right"	
	

	
	if velocity.y<0:
			$AnimatedSprite2D.animation = "run_up"
	elif velocity.y!=0:
			$AnimatedSprite2D.animation = "run_down"

Note: the if statement at the top is indented the same as the other statements in the code. It looks weird in this post.

That works fine in 4 directions, but when moving diagonally at the direction of 2 inputs, the sprite is static. It doesn’t look like it’s walking.

I’ve found other solutions, but they all seem rather bulky and complex. I was just looking for something very simple, like “if you press W and D together, the player moves up and to the right and the run_up animation is triggered”.

Don’t assign the animation directly to the animation property because that will restart the animation. Since you’ll assign twice in the case of diagonal movement it will try to restart the second animation each frame. Instead use a temporary string variable to assign the animation name to in those ifs and then assign that to the animation property in the end.

I wrote this and now none of the animations trigger. lol!

var move_up_left
	velocity.y<0 and velocity.x<0
	$AnimatedSprite2D.animation = "run_up"
	var move_up_right
	velocity.y<0 and velocity.x!=0
	$AnimatedSprite2D.animation = "run_up"
	var move_down_right
	velocity.y!=0 and velocity.x!=0
	$AnimatedSprite2D.animation = "run_down"
	var move_down_left
	velocity.y!=0 and velocity.x<0
	$AnimatedSprite2D.animation = "run_down"

I truly don’t know what I’m doing, obviously. I went through the GDScript online course, but this wasn’t covered. I’ve also gone through the manual. I need to go through it more. It seems like this should be much simpler than what it is.

I suppose I could simply make new input map keys for diagonals and put them in and assign animations to them. That would probably work.

Make an @onready var for your animated sprite 2d

@onready var sprite : AnimatedSprite2D = # insert path to your node here

Also throw in two boolean variables too

var upflag : bool = false
var downflag : bool = false

You can create if statements to play animations based on these flags.

Next, instead of writing $AnimatedSprite2D.animation = "run_up", try sprite.play(run_up)

Under this example it should look something like this

@onready var sprite : AnimatedSprite2D = # drag and drop your node here, if you hold ctrl when dropping, godot automatically fills in this entire line of code (minus this comment, of course)

var upflag : bool = false
var downflag : bool = false

func physics_process(delta: float) -> void:

	if upflag == true:
		sprite.play(run_up)
		downflag = false
	elif downflag == true: # elif statements make sure that these conditions must be mutually exclusive, prevents animation overlap
		sprite.play(run_down)
		upflag = false

From here, you will include your left and right animations according to velocity

	if velocity.x < 0: # if you run into any troubles, include "and upflag == false and downflag == false" as part of these if statements
		sprite.play(run_left)
	elif velocity.x > 0:
		sprite.play(run_right)

Lastly, throw in some contingency code to make sure your flags are set based on y velo

	if velocity.y < 0:
		upflag = true
	elif velocity.y > 0:
		downflag = true
	else: # velocity y == 0
		upflag = false
		downflag = false

Try that out and see if it’s helpful.

1 Like

Thank you. The only issue was that it popped an error code saying that (run_up) and (run_down) were undefined in the scope. I might have put them in the wrong area.

var anim = "idle" # if you have it
if velocity.x < 0:
	anim = "run_left"
if velocity.x > 0:
	anim = "run_right"
if velocity.y < 0:
	anim = "run_up"
if velocity.y > 0:
	anim = "run_down"
$AnimatedSprite2D.animation = anim

This really hasn’t much to do with GDScript per se. It’s pure first order logic, unrelated to any specific language or framework.

First order logic is a fundamental thinking tool in programming, and well, in life as a whole. You need to learn to wield this tool with confidence, otherwise you’ll have very hard time programming anything.

1 Like

Ok, thank you.

I guess if I haven’t figured out first order logic by now, I never will.

However, a lot of my issues are with understanding how some things are explained. For instance: your attached code is very good, I’m sure.

However, where is it put in the code string? Are variables always in the extends portion of the script? That’s never really something that was clear to me. It seems like it wouldn’t be the case, because each variable is attached to an individual logic string.

Does the layout always progress extends - func ready - func process, or can that be moved around? If I need to use func physics_process, does that replace func process, or is it put before or after physics_process?

A lot of the tutorials explain the function of code, but less so the order. Additionally, the vocabulary used to describe the code functions is somewhat specific to a process that is otherwise unfamiliar. The word might make sense. But how it fits into the overall meaning and context of the code can be somewhat opaque.

It’s a bit like saying “What does the French word ‘vent’ mean?” and getting the reply “That’s the same as ‘viento’ in Spanish.”

“What’s ‘viento’ in English?”

“That means ‘wind.’”

“So this sentence is about wind?”

“No, it’s about sand.”

I thought it’s obvious from the context that the code I posted goes where your if velocity.y < 0:... part of code is.

No. Variables have what is called “scope”. Variable’s scope determines the availability and lifetime of the variable within the code. Keyword var creates a variable so to speak, we call this “declaring a variable”. Depending on where the variable is declared its scope will be different.

If you declare the variable in the “extends portion”, that is outside of any function, its scope is the whole script file. Every piece of code will know the name of the variable and can access it. Since the script file represent a class, those variables are considered class-wide properties.

On the other hand, if you declare a variable inside a function body, the variable will be known only inside that function. You can go to even smaller scope and declare a variable inside an if block or a loop body. In that case it will only live inside that block of code. As soon as the execution exits the block, such variables will vanish into thin air. Those variables are called “local variables” as they are local to the scope they’re declared in.

So the placement of var determines the scope of the variable.

You can order functions however you see fit. The only mandatory thing is to have class_name (optional) and extends at the top of the file. You can even declare class properties in between functions or at the end of file, although a widely accepted convention is to declare them at the top. So something like the following would be technically correct but stylistically strange:

class_name MyScript extends Node

func _process(delta):
	var foo # foo exist only inside this function

func _ready():
	var bar # bar exists only inside this function

var my_property # my_property exist everywhere in this script, every function can access it

Note that for variables declared inside functions, variable’s declaration needs to appear before the variable is otherwise used, but class property declarations can appear afterwards.

No, they both will be automatically executed by the engine on a perpetual basis. _process() is executed at every rendered frame, typically 60 times per second, and _physics_process() is executed at every step (aka tick) of the physics simulation. This will also be 60 times per second by default.

1 Like