godotgodot4

How To Make Animations Play Based on Movement Direction In Godot


So I've got some code in Godot that allows for 8-directional movement for a top-down game in Godot, it works fine, exactly as intended. The part that keeps tripping me up is trying to figure out how to make specific movement animations play depending on the direction the player is moving.

For reference, here is the movement code:

extends CharacterBody2D

var speed = 100

func _physics_process(delta):
    var direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    velocity = direction * speed

    move_and_slide()

I tried to make the animations play depending on which inputs are being pressed (i.e: if Up and Right are being pressed the animation will be the Up Diagonal animation facing to the right), but with 8 different animations to play it became such a large slab of code that seemed to be not very efficient and that while it technically functioned, it looked very janky and stuttered a lot when in use.


Solution

  • KidsCanCode's recipe "8-Directional Movement/Animation" is what you're looking for, using keyboard input instead of the mouse.

    First, you get the angle of the current movement direction (in radians):

    if not direction.is_zero_approx():
        var angle = direction.angle()
    

    Then, you divide it by π/4 to transform the angles into offsets in the [-3, 4] interval:

    var offset = angle / (PI/4)
    

    Next, you wrap the offset to be in the [0, 7] interval:

    var index = wrapi(int(offset), 0, 8)
    

    The index is a numerical equivalent of cardinal directions, and you can use it to access the appropriate animation, by means of a name suffix:

    var anim_name = "Idle"
    if not direction.is_zero_approx():
        ...
        anim_name = "Run"
    # Works with both AnimatedSprite2D and AnimationPlayer
    $Animation.play(anim_name + str(index))  # "Idle0"/"Run0, "Idle1"/"Run1"...
    

    If you don't like numerical suffixes, replace them with cardinal directions:

    const idx_to_dir: Array[String] = ["E", "SE", "S", "SW", "W", "NW", "N", "NE"]
    $Animation.play(anim_name + idx_to_dir[index])  # "IdleE"/"RunE", "IdleSE"/"RunSE"...
    

    Here's the complete code snippet using numerical indices (adapted from your code and the original source), tweak it as you need:

    extends CharacterBody2D
    
    var speed = 100
    
    func _physics_process(delta):
        var anim_name = "Idle"
        var direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
        if not direction.is_zero_approx():
            var angle = direction.angle()
            var offset = angle / (PI/4)
            var index = wrapi(int(offset), 0, 8)
            anim_name = "Run"
        velocity = direction * speed
        move_and_slide()
        $Animation.play(anim_name + str(index))
    

    And here's a handy conversion table for reference:

    Angle 0 45° 90° 135° 180° 225° 270° 315°
    Offset -3 -2 -1 0 1 2 3 4
    Index 0 1 2 3 4 5 6 7
    Direction E SE S SW W NW N NE