05.changing-the-character-sprite

Changing the character’s sprite based on its direction

In this part, we’ll add code to change the character’s sprite depending on the player’s input direction.

We’ll use a dictionary lookup to achieve this. This will show you a creative way to use dictionaries.

The character sprite sheet

The character’s Sprite node uses a multi-frame texture. We call that a sprite sheet: a texture that packs multiple sprites into one image.

Each sprite fits in a grid, cutting the texture into equal chunks. This sprite sheet has two rows and three columns.

If you set up your node’s Hframes and Vframes correctly, as we did earlier, only one sprite will show up at a time.

You can control the displayed sprite by changing the Animation -> Frame property in the Inspector.

Please notice how frame 0 is the character looking down, 1 is looking down and right, and so on. We’ll use these values in our code.

Every property you see in the Inspector has a code equivalent. You can see it bold and underlined if you hover over the property name with the mouse.

For example, writing frame = 0 in GDScript will make the character sprite look down.

Flipping the sprite

Before moving on to code, please note the Offset -> Flip H and Offset -> Flip V properties.

Turning the properties on flips the image horizontally and vertically.

Our texture has five sprites: one looking down, one looking up, and three looking to the right.

We will use the Flip H property from the code to mirror the sprite horizontally when the character moves left.

We can hover over the property name in the Inspector again to learn how to access it in code.

Coding the frame lookup

Let’s head back to the script.

As mentioned in the intro, we’ll use a dictionary lookup. We start by defining a Dictionary that maps a Vector2 value to a frame number.

const DIRECTION_TO_FRAME := {
    Vector2.DOWN: 0,
    Vector2.DOWN + Vector2.RIGHT: 1,
    Vector2.RIGHT: 2,
    Vector2.UP + Vector2.RIGHT: 3,
    Vector2.UP: 4,
}

We use Godot’s predefined Vector2 constants to make our code more readable. The constant Vector2.DOWN is equal to Vector2(0, 1).

For diagonal directions, we add two vectors. For example, to represent the southeast direction, we add Vector2.DOWN and Vector2.RIGHT. This produces a value equal to Vector2(1, 1).

We map that to frame number 1, which is the character going southeast.

Please note how the X and Y components of our Vector2 are always equal to 0 or 1. We need numbers we can reproduce reliably in our code to provide our Dictionary with a valid key.

Calculating the right dictionary keys

In the _physics_process() function, we make calculations to turn our direction vector into a key in DIRECTION_TO_FRAME.

We first call Vector2.round() to get a vector with the X and Y components rounded to 0 or 1.

We also use the abs() function to ensure that direction_to_frame_key has a positive X component, as the dictionary has no keys with negative X components.

func _physics_process(delta: float) -> void:
    # ...
    # The Vector2.round() function returns a new Vector2 with both the `x` and
    # `y` rounded out.
    var direction_key := direction.round()
    # The abs() function makes negative numbers positive.
    direction_key.x = abs(direction_key.x)

Then, we get the corresponding frame if the calculated key exists in the DIRECTION_TO_FRAME dictionary.

Also, we set the Sprite’s flip_h property to true when the player goes to the left.

# We need to get the Sprite node to change its frame and flip it horizontally.
onready var sprite := $Godot

func _physics_process(delta: float) -> void:
    # ...
    if direction_key in DIRECTION_TO_FRAME:
        sprite.frame = DIRECTION_TO_FRAME[direction_key]
        # Notice how we directly assign the result of a comparison to flip_h.
        # The computer converts comparisons to either true or false, which is
        # compatible with boolean variables like flip_h.
        sprite.flip_h = sign(direction.x) == -1

The sign() function returns -1 for negative numbers, 0 for 0, and 1 for positive numbers.

If the input vector points even a bit to the left, the sprite will flip horizontally.

We now have a moving character whose textures align with its movement direction.

Note: the code above works for both keyboard and joystick input by default, but it will have a subtle bug if you lower the default joystick’s deadzone. By default, input actions will not trigger unless you push the joystick at least 50% towards its edges. That’s what we call the deadzone.

The default value works great with our code, but if you lower it in the input map, you’ll be able to move the character without changing the sprite’s texture when barely pushing the joystick.

In the next lesson, you’ll learn how to restrict the character’s movement to four directions.

Practice: Changing sprite frames

Open the practice Changing sprite frames.

Practice: Flipping sprites

Open the practice Flipping sprites.

Practice: Flipping when going left

Open the practice Flipping when going left.

Practice: Choose the right frame

Open the practice Choose the right frame.

The code

Here’s the complete code listing for EightWayMovement.gd.

extends KinematicBody2D

# Notice how we capitalize the constant's name. We do this to distinguish them
# from variable names.
const SPEED := 700.0
const DIRECTION_TO_FRAME := {
    Vector2.DOWN: 0,
    Vector2.DOWN + Vector2.RIGHT: 1,
    Vector2.RIGHT: 2,
    Vector2.UP + Vector2.RIGHT: 3,
    Vector2.UP: 4,
}

# We need to get the Sprite node to change its frame and flip it horizontally.
onready var sprite := $Godot


func _physics_process(delta: float) -> void:
    var direction := Input.get_vector("move_left", "move_right", "move_up", "move_down")
    var velocity := direction * SPEED
    move_and_slide(velocity)

    # The Vector2.round() function returns a new Vector2 with both the `x` and
    # `y` rounded out.
    var direction_key := direction.round()
    # The abs() function makes negative numbers positive.
    direction_key.x = abs(direction_key.x)
    if direction_key in DIRECTION_TO_FRAME:
        sprite.frame = DIRECTION_TO_FRAME[direction_key]
        # Notice how we directly assign the result of a comparison to flip_h.
        # The computer converts comparisons to either true or false, which is
        # compatible with boolean variables like flip_h.
        sprite.flip_h = sign(direction.x) == -1