01.detecting-when-objects-enter-an-area

Detecting when objects enter an area

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.

Preparing the chest and gem scenes

We need three scenes:

  1. The chest.
  2. The gems.
  3. A scene that contains both.

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.

Creating the gem scene

Let’s start by creating the gem.

Create a new scene, and in the Scene dock, select Other Node.

Create a KinematicBody2D node. Then, rename it to Gems. Save the scene as Gem.tscn inside of the TowerDefense/ directory.

You’ll use the KinematicBody2D 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 KinematicBody2D to match the setup we’ll use in the following lessons.

Then, add three child nodes:

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 Sprite node in the Scene dock. A drop-down menu opens and lets you pick the property to assign the image. Click Texture.

Selecting the Sprite and dragging the texture to the Inspector or dragging it directly onto the node in the Scene dock has the same effect.

Select the CollisionShape2D and assign a new CircleShape2D 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.

Creating the chest scene

The chest should detect when a gem overlaps with it. To do so, we’ll start with an Area2D node.

Create a new scene with an Area2D as the first node.

Rename the Area2D to Chest and save the scene in the TowerDefense/ directory.

Then add four child nodes:

  1. A Sprite named ChestBottom.
  2. A second Sprite named ChestTop.
  3. A CollisionShape2D.
  4. An AnimationPlayer.

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 RectangleShape2D to the CollisionShape2D 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 AnimationPlayer, 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 Animation 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.

Detecting when a gem overlaps the chest’s area

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:

  1. Get the AnimationPlayer node so we can use it.
  2. Animate the chest opening when a physics body enters the Area2D’s collision shape.
  3. Animate the chest closing when a physics body exits the Area2D’s collision shape.

We will use the Area2D’s 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:
    anim_player.play("chest_open")

func _on_body_exited(_body: PhysicsBody2D) -> void:
    anim_player.play_backwards("chest_open")

Notice how we wrote the _body parameter in both functions with a leading underscore.

Using a leading underscore when you don’t use a function parameter

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:

  1. KinematicBody2D
  2. RigidBody2D
  3. StaticBody2D

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.

Bringing the chest and the gem together in a test 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.

The code

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:
    anim_player.play("chest_open")


func _on_body_exited(_body: PhysicsBody2D) -> void:
    anim_player.play_backwards("chest_open")