In this lesson, we’ll create a new pickup type: the speed boost.
It will make the player run faster until they hit an obstacle or go off course.
With the slow down from earlier, we’ll start having many elements of a real game! Let’s dive in.
Just like you did for the Ghost pickup, right-click the
pickups/Pickup.tscn
file and create a New Inherited
Scene. Rename the root node to PickupSpeed and save the
scene in the pickups/
directory.
Drag and drop the image assets/pickup_lightning.png
onto
the PickupGem sprite node in the Scene dock and assign
it to its Texture
slot to change the pickup’s
appearance.
We now want to assign a new script that extends
Pickup.gd
to the scene’s root node. Right-click on the
PickupSpeed node and select Attach Script.
To code the speed boost, we start in the character’s script. We need to increase its speed when touching the pickup and reset it when colliding with something.
Open the Godot.gd
script. At the top, add a variable
that we will use to know if the godot robot picked a speed up or
not:
var speed_effect_is_active := false
Add the following function and speed constant.
const SPEED_FAST := 1100.0
func apply_speed_effect() -> void:
= true
speed_effect_is_active = SPEED_FAST speed
We will use this function later in the PickupSpeed.gd
script to apply the pickup, just like we did with the ghost effect in
PickupGhost.gd
.
With this, we can change the speed of the character. But we’re not done: we also want to cancel the effect if we bump into anything.
We need to reset the character’s speed
when colliding
with obstacles. We use the
KinematicBody2D.get_slide_count()
function to do that.
This function returns the number of times the physics body collided
in a given frame. If it is greater than 0
, we know that the
character hit an obstacle, like a wall or a rock.
We could write:
if get_slide_count() > 0:
= SPEED_DEFAULT speed
But if we only check get_slide_count()
, the player’s
speed
will also reset if bumping in an obstacle while being
slowed down. We don’t want that. We want to reset
speed
when bumping in an obstacle, but only if the
player has a speed up.
func _physics_process(delta: float) -> void:
# ...
if speed_effect_is_active and get_slide_count() > 0:
= SPEED_DEFAULT speed
All that’s left now is to call our new function from the pickup.
Reopen the PickupSpeed.gd
script and override the
pickup.apply_effect()
function.
extends "Pickup.gd"
func apply_effect(body: Node) -> void:
apply_speed_effect() body.
With that, we can test the speed boost pickup.
Create a new scene file and add the Godot character, a PickupSpeed instance and scatter a few rocks around.
Play the scene to see how the character’s speed increases upon collecting the yellow gem.
The effect is lost when the character collides with a rock.
That’s two pickups done!
Like the turrets in the tower defense series, we saw how to create a base scene that we can reuse to create new pickups quickly.
If you wanted to create a another new pickup, you would inherit the
Pickup scene, extend the Pickup script, and override
the apply_effect()
function.
Sometimes, we need to add more code to the character since we had to create new game mechanics.
We hope you can see how each new pickup requires a small number of code changes.
You could apply the same techniques to consumable items in a role-playing game.
The idea is the same: you want to have the same function on every item to apply their effect. That way, you only need to know one function to call, and it will work with any item.
Here’s the complete code for the character and the pickup we added in this lesson.
Godot.gd
extends KinematicBody2D
const DIRECTION_TO_FRAME := {
0,
Vector2.DOWN: + Vector2.RIGHT: 1,
Vector2.DOWN 2,
Vector2.RIGHT: + Vector2.RIGHT: 3,
Vector2.UP 4,
Vector2.UP:
}
const SPEED_SLOW := 100.0
const SPEED_DEFAULT := 700.0
const SPEED_FAST := 1100.0
# We update the speed variable to use our new constant.
var speed := SPEED_DEFAULT
var drag_factor := 0.13
var velocity := Vector2.ZERO
var start_collision_layer := collision_layer
var start_collision_mask := collision_mask
var speed_effect_is_active := false
onready var sprite := $Sprite
onready var animation_player_ghost := $AnimationPlayerGhost
onready var timer_ghost := $TimerGhost
onready var slowdown_area := $Area2D
func _ready() -> void:
connect("timeout", self, "toggle_ghost_effect", [false])
timer_ghost.connect("area_entered", self, "_on_Area2D_area_entered")
slowdown_area.connect("area_exited", self, "_on_Area2D_area_exited")
slowdown_area.
func _physics_process(delta: float) -> void:
var direction := Input.get_vector("move_left", "move_right", "move_up", "move_down")
var desired_velocity := speed * direction
var steering_vector := desired_velocity - velocity
+= steering_vector * drag_factor
velocity move_and_slide(velocity)
if speed_effect_is_active and get_slide_count() > 0:
= SPEED_DEFAULT
speed
var direction_to_frame_key := direction.round()
= abs(direction_to_frame_key.x)
direction_to_frame_key.x if direction_to_frame_key in DIRECTION_TO_FRAME:
= DIRECTION_TO_FRAME[direction_to_frame_key]
sprite.frame = sign(direction.x) == -1
sprite.flip_h
func apply_speed_effect() -> void:
= true
speed_effect_is_active = SPEED_FAST
speed
func toggle_ghost_effect(is_on: bool) -> void:
if is_on:
start()
timer_ghost.= 0
collision_layer = 0
collision_mask play("ghost")
animation_player_ghost.else:
= start_collision_layer
collision_layer = start_collision_mask
collision_mask stop()
animation_player_ghost.
func _on_Area2D_area_entered(area: Area2D) -> void:
# When entering or leaving the area, we ensure that it has the offcourse
# node group to distinguish it from the push wall's area.
if area.is_in_group("offcourse"):
= SPEED_SLOW
speed
func _on_Area2D_area_exited(area: Area2D) -> void:
if area.is_in_group("offcourse"):
= SPEED_DEFAULT speed
PickupSpeed.gd
extends "Pickup.gd"
func apply_effect(body: Node) -> void:
apply_speed_effect() body.