Before we create turrets, we’ll focus on detecting enemies. We learned how to detect when a character enters or leaves an area in the previous series, so this technique should be familiar to you already.
Areas are one of Godot’s most versatile nodes, and we want to highlight that in this series.
In this lesson, we will create chests that open when we drag gems over them.
We’ll allow the player to drag the gems with the mouse. When a gem gets close to a chest, the chest will open.
We will later use the same technique to have turrets detect targets.
We need three scenes:
Only one scene requires code: the chest, to detect when a gem overlaps with it. We need some code to drag and drop the gems, but we included a helper node to do that in the project files.
Let’s start by creating the gem.
Create a new scene, and in the Scene dock, select Other Node.
Create a Gem.tscn
inside of the
TowerDefense/
directory.
You’ll use the
node for enemies in games when you need them to collide with the environment. While our gems don’t need that, we start with a to match the setup we’ll use in the following lessons.Then, add three child nodes:
GDQuestDebugDraggable
. This is a node we coded for
you for debugging purposes. It allows you to drag and drop physics
bodies and areas with the mouse.You can add nodes by clicking the +
button in the
top-left of the Scene dock, or by pressing
Ctrl+A.
You should end up with the following scene tree.
Note: The GDQuestDebugDraggable
node
will display a warning sign, but you can ignore it.
Let’s give the sprite a texture using a cool trick.
In the FileSystem dock, expand the
TowerDefense/assets/
directory and click and drag
gems_mixed_1.png
onto the node in the Scene dock. A drop-down
menu opens and lets you pick the property to assign the image. Click
Texture
.
Selecting the
and dragging the texture to the Inspector or dragging it directly onto the node in the Scene dock has the same effect.Select the
and assign a new to the Shape property in the Inspector.Then, click and drag on the circle’s resize handle in the viewport until it roughly matches the gem sprite.
The chest should detect when a gem overlaps with it. To do so, we’ll start with an
node.Create a new scene with an
as the first node.Rename the TowerDefense/
directory.
Then add four child nodes:
You should end up with the following scene tree. The sprites’ order is important, as we want the top of the chest to show in front of the bottom part.
Assign the image chest_bottom.png
to the
ChestBottom sprite. Then, assign the image
chest_top.png
to the ChestTop.
Finally, add a
to the and size it to roughly cover the ChestBottom sprite.Your scene should look something like this:
k
Don’t worry about the nodes’ position at this stage. They will be automatically repositioned when we load and play the chest’s open animation.
We prepared the chest’s animations for you. In Godot, you can load animations from files.
Click on the
, and in the bottom panel, click Animation -> Load.Locate the file TowerDefense/assets/chest_open.anim
and
load it. You should see animation keys in the bottom panel. You can play it by pressing
Shift+D.
Note: In Godot, animations use the name of nodes to find which nodes and properties to animate. Our animations expect the chest’s top to be called precisely ChestTop. If the animation doesn’t work, please ensure the nodes are named correctly.
Our scenes are ready! Time to write some code.
We now want to detect when a gem enters or leaves the chest’s area, and we need a script to do that.
Attach a new script to the Chest node.
In the code editor, remove everything apart from the top line:
extends Area2D
.
Our code should:
We will use the body_entered
and
body_exited
signals to do that.
We connect the two signals in the _ready()
function.
func _ready() -> void:
connect("body_entered", self, "_on_body_entered")
connect("body_exited", self, "_on_body_exited")
The functions _on_body_entered()
and
_on_body_exited()
don’t exist yet, so let’s add them. In
these functions, we respectively play the open animation forwards and
backwards by calling AnimationPlayer.play()
and
AnimationPlayer.play_backwards()
.
onready var anim_player := $AnimationPlayer
func _on_body_entered(_body: PhysicsBody2D) -> void:
play("chest_open")
anim_player.
func _on_body_exited(_body: PhysicsBody2D) -> void:
play_backwards("chest_open") anim_player.
Notice how we wrote the _body
parameter in both
functions with a leading underscore.
Adding a leading underscore to a function parameter is a convention to indicate that we do not use the parameter intentionally.
Developers follow this convention in other popular programming languages like C#.
The area_entered
and area_exited
signals
come with a mandatory body
parameter. We can use it to
check and modify the node that entered or left the area.
However, we want any PhysicsBody2D
to trigger
the chest animation, so we don’t use this parameter.
A PhysicsBody2D
is one of the following three nodes:
You will also get a warning if you remove the underscore before the
_body
parameter. You might have noticed warnings already in
Godot’s script editor.
Unlike errors, warnings won’t stop your game’s execution. They point out potential sources of errors or code you forgot to delete.
Warnings help you to ensure that every decision in your code is intentional.
That’s it for the Chest and the Gem scenes! Now we only need to instantiate them in a bigger scene.
Creating the container scene is just a matter of creating a new scene and dragging the gems and the chest on the stage.
Create as many gems and chests as you’d like. We instantiated four of each in our example.
This is the structure you should end up with:
Run the scene! Click and drag gems to move them over a chest and observe how it reacts.
As you’ll see in the following lessons, we can use the same setup to detect when a turret has a target in range.
In the next lesson, you will make a turret that shoots rockets at target within a limited range.
Here’s the complete script for Chest.gd
.
extends Area2D
onready var anim_player := $AnimationPlayer
func _ready() -> void:
connect("body_entered", self, "_on_body_entered")
connect("body_exited", self, "_on_body_exited")
func _on_body_entered(_body: PhysicsBody2D) -> void:
play("chest_open")
anim_player.
func _on_body_exited(_body: PhysicsBody2D) -> void:
play_backwards("chest_open") anim_player.