03.adding-keyboard-input

Controling the ship with the keyboard

In this video, you’ll add input mappings and code to control your ship with the keyboard.

New concepts

What’s an object?

You’ll hear us talk about objects from time to time in this first part of the course. In Godot, and in programming languages like Python or JavaScript, an object is a structure that bundles data and functions together.

You’ve already encountered several objects: your ship’s Sprite is an object. Arrays, dictionaries, and Input are also objects.

They all bundle data and functions in one package: for example, arrays contain values (data) and give you access to member functions like append().

You’ll be using objects all the time in game development. For instance, every node in Godot is an object.

The concept might be a bit abstract right now, but don’t worry: you’ll learn more about objects later in the course.

Input

The Input object gives you access to all the functions related to user input in Godot. For example, we use it to get the input direction of the player with the get_axis() function.

Many functions of the Input object expect input actions. Input actions are names to which you map keys, mouse buttons, or gamepad buttons. We prepared a recap guide about input actions: Adding input mappings to your game.

Direction vectors

A direction vector is a vector whose maximum length is 1.

We often calculate the velocity of a game character by multiplying a direction vector with a speed value.

In that case, the direction is generally a 2D vector, while the speed is a decimal value. Multiplying a vector by a decimal value makes the vector longer or shorter, preserving its direction.

We prepared a bonus video demo to show you how direction vectors work.

Checking that there is an input direction

In this lesson, we test the player’s input direction in a condition: if direction.

When using this shortcut, Godot tests both the x and y values of the vector. If they’re both equal to zero, then, the condition fails. If the vector has any other values, the condition passes.

Practice: Listening to player input

In the godot-practice project, open the second practice for this project, Listening to player input.

This time, your job is to make the ship listen to input.

Your questions

Why do you sometimes put a decimal after numbers?

We sometimes add a decimal place after numbers, even if it is .0. Doing so tells the computer this value is a float (a decimal number) rather than an int (a whole number).

However, this is often unnecessary in GDScript. While whole numbers and decimal numbers are different for the computer, GDScript will convert numbers for you depending on the context.

For example, when creating a 2D vector, the Vector2() constructor function takes only decimal values.

Even if you write whole numbers, the computer knows you mean to use decimal values and treats them as such.

Why would you ever type .0 after a number, then? It makes your code more precise. It’s especially useful when you need to modify it weeks later and decimals matter.

We generally always write decimal places, even if they’re .0, for that reason. However, they’re a detail that you shouldn’t worry about yet.

Why do we define velocity outside the process function but direction inside it?

Technically, we could define the velocity inside the _process() function as we did for the direction variable. Doing so resets the variable every frame.

However, to make the ship move smoothly later in the series, we will need the velocity to keep its value between frames, which is why we defined it at the top of the script. You will need to do that in most of your character movement scripts.

Why did we define velocity with a value of Vector2.ZERO?

When creating variables, it’s a good practice to give them a value. This allows us to use type inference by writing :=. Also, if you forget to set the initial value of a variable, you can sometimes end up with bugs in your code.

In this case, the starting value is not very important because we recalculate the velocity every frame. We just often initialize 2D vectors with a value of Vector2.ZERO.

The code

Here’s the complete code listing for the ship so far.

extends Sprite

var max_speed := 600.0
var velocity := Vector2.ZERO


func _process(delta: float) -> void:
    var direction := Vector2.ZERO
    direction.x = Input.get_axis("move_left", "move_right")
    direction.y = Input.get_axis("move_up", "move_down")

    # If we don't do this and the player moves diagonally, they will move 40%
    # faster than normal.
    if direction.length() > 1.0:
        direction = direction.normalized()

    velocity = direction * max_speed
    position += velocity * delta

    if direction:
        rotation = velocity.angle()

In the next video, we will use signals to code a boosting mechanic.