In this lesson, we will make our ship move much smoother using steering behaviors.
Steering behaviors are calculations that allow you to make characters and AI’s move smoothly with generally little code. They lead to a natural motion and smooth curves.
Currently, in every frame, we instantly change the velocity. The ship moves at maximum speed when the player presses a direction key. If the player releases the key, the ship stops.
We want the ship to accelerate and decelerate smoothly instead. To do so, we will use steering behaviors.
Steering behaviors are a category of algorithms we use to make objects move smoothly. Steering behaviors use vector calculations to make objects steer towards a target position.
The first steering algorithm we’ll see is the simplest: it only takes two or three lines of code and can improve movement immensely.
var desired_velocity := speed * direction
var steering_vector := desired_velocity - velocity
+= steering_vector * drag_factor velocity
We’ll break it down below.
This steering behavior gradually changes the ship’s velocity to move towards a target.
It works by calculating what we call the “desired velocity.” That’s the ship going instantly at the max speed in the input direction. We “compare” that with the current velocity.
We can represent the current and desired velocity vectors like so.
Imagine the ship is going towards the left, and the player presses the right key. How do we make the ship steer towards the right?
We start by calculating the difference between the two velocities. That’s what we call the steering vector.
To make the ship turn smoothly, instead of moving at the desired velocity, every frame, we add a small portion of the steering vector to the current velocity. Doing so makes the ship gradually steer towards the target velocity.
So far, we calculated the velocity with a single line of code.
func _process(delta: float) -> void:
# ...
= direction * speed velocity
Note: When we write #...
in code listings, it
represents the lines of code that came before or after the lines we are
highlighting. Otherwise, if we included every line of code every time,
important or new lines of code would become difficult to spot.
To get smooth movement, we need to replace the line that updates the
ship’s velocity with three lines of code. We also add a new variable,
the drag_factor
:
var drag_factor := 0.1
func _process(delta: float) -> void:
# ...
var desired_velocity := max_speed * direction
var steering_vector := desired_velocity - velocity
+= steering_vector * drag_factor
velocity
# ...
We first calculate the desired velocity: the ship moving at the maximum speed in the input direction.
var desired_velocity := max_speed * direction
We then calculate the difference between the desired velocity and the current one.
var steering_vector := desired_velocity - velocity
Note that the order in which you put the vectors matters a lot as vectors have a direction. The ship will move away from the target if you reverse the two terms.
+= steering_vector * drag_factor velocity
We add a portion of this steering vector to the current
velocity on the third line. It changes the velocity
gradually every frame, making our ship accelerate and decelerate
depending on our input.
Without the drag_factor
, we would instantly set our
velocity
to desired_velocity
, making the ship
always move at the maximum speed or not move at all.
To add only a portion of the steering vector every frame, you
multiply the value steering_vector
by a small number.
Anywhere between 0.01
and 0.3
should work
well.
The lower the value, the more inertia the ship will have: it will
turn, accelerate, and decelerate much slower. Here is the ship with a
drag_factor
of 0.05
.
The higher the value, the more reactive the ship will be: it will
turn sharply, get to its maximum speed quickly, and stop in a split
second. Here’s the ship moving with a drag_factor
of
0.3
.
Still, the motion will always feel much smoother than always moving at max speed with this steering algorithm.
In the Godot practices project, open the Making the ship steer.
These steering equations are widely used in professional games. This lesson shows only the basic principle, but you will see in the course that we can push steering behaviors further to code impressive AI movement.
Here’s the complete code for the steering ship, including the boost mechanic you coded before.
extends Sprite
var boost_speed := 1500.0
var normal_speed := 600.0
var max_speed := normal_speed
var velocity := Vector2.ZERO
var drag_factor := 0.1
func _process(delta: float) -> void:
var direction := Vector2.ZERO
= Input.get_axis("move_left", "move_right")
direction.x = Input.get_axis("move_up", "move_down")
direction.y
if direction.length() > 1.0:
= direction.normalized()
direction
if Input.is_action_just_pressed("boost"):
= boost_speed
max_speed get_node("Timer").start()
var desired_velocity := max_speed * direction
var steering_vector := desired_velocity - velocity
+= steering_vector * drag_factor
velocity += velocity * delta
position = velocity.angle()
rotation
func _on_Timer_timeout() -> void:
= normal_speed max_speed