06.four-way-movement

Four-way character movement

In this bonus lesson, you’ll learn to restrict the player’s movement to four directions, whether they use a gamepad or the keyboard.

Duplicating the character scene and script

We don’t want to change our character directly here, as we’ll reuse the eight-way movement later.

We want a separate copy of the character scene and script, so we don’t lose any work.

In the FileSystem dock, right-click on the EightWayMovement.tscn file and select Duplicate. Name the new file FourWayMovement.tscn.

Please do the same for the EightWayMovement.gd script file.

You should see the following two files in the TopDownMovement/ directory.

As we duplicated the EightWayMovement scene, the new scene still has the EightWayMovement.gd script attached. We need to replace it.

Double-click the FourWayMovement.tscn file to open the scene, and click and drag FourWayMovement.gd onto the KinematicBody2D node.

You can see if the node has the correct script by hovering over the script icon with the mouse.

Restricting the movement to four directions

Click the script icon to open FourWayMovement.gd.

We mostly need to change how we calculate the player’s input direction.

Instead of using Input.get_vector() to calculate the character’s movement direction, we use simpler condition blocks.

Replace the line starting with var direction with this new code.

func _physics_process(delta: float) -> void:
    var direction := Vector2.ZERO
    if Input.is_action_pressed("move_right"):
        direction.x = 1.0
    elif Input.is_action_pressed("move_left"):
        direction.x = -1.0
    elif Input.is_action_pressed("move_up"):
        direction.y = -1.0
    elif Input.is_action_pressed("move_down"):
        direction.y = 1.0

We initialize the direction variable to a Vector2 of length 0. That way, if the player isn’t pressing any keys, the character won’t move.

We call Input.is_action_pressed() with the corresponding input action for each of the four input directions.

If the input is pressed, we set the corresponding vector member variable to 1.0 or -1.0.

An x value of 1.0 will make the character move right, while -1.0 will make it move left. A y value of 1.0 will make the character move down, and -1.0 will move it up.

Notice how we use a series of elif blocks. This is how we ensure the player can only move in one of the four directions at a time.

The order of the condition blocks also gives some inputs priority over others. If the player presses both the right and left arrow keys, the character will move to the right because the first conditional block tests for the move_right input.

And that’s all we need to restrict the character’s movement to four directions.

Cleaning up the code

As the character cannot move diagonally, you can remove the extra mappings from the DIRECTION_TO_FRAME dictionary.

We won’t need the diagonal textures anymore, so we remove two entries from the dictionary to keep the code relevant.

Your dictionary should look like the following.

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

It’s a good practice to remove bits of code you don’t need anymore.

Before moving on, we have two exercises for you.

Practice: Eight-way movement

Open the practice Eight-way movement.

Practice: Four-way movement

Open the practice Four-way movement.

The code

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

extends KinematicBody2D

const SPEED := 700.0
const DIRECTION_TO_FRAME := {
    Vector2.DOWN: 0,
    Vector2.RIGHT: 2,
    Vector2.UP: 4,
}

onready var sprite := $Godot

func _physics_process(delta: float) -> void:
    var direction := Vector2.ZERO
    if Input.is_action_pressed("move_right"):
        direction.x = 1.0
    elif Input.is_action_pressed("move_left"):
        direction.x = -1.0
    elif Input.is_action_pressed("move_up"):
        direction.y = -1.0
    elif Input.is_action_pressed("move_down"):
        direction.y = 1.0
    move_and_slide(SPEED * direction)

    var direction_key := direction.round()
    direction_key.x = abs(direction_key.x)
    if direction_key in DIRECTION_TO_FRAME:
        sprite.frame = DIRECTION_TO_FRAME[direction_key]
        sprite.flip_h = sign(direction.x) == -1