As a final building block, we will learn to make a door that opens with a pressure plate!
There are multiple ways to implement this.
In the obstacle course series, you placed a door as a child of a pressure plate.
In this lesson, we will explore another valid approach: connecting each pressure plate to a door through
.Using a child node is more straightforward, while a node path gives you more flexibility regarding where you place the nodes in your scenes.
You’ve seen node paths many times before.
onready var collision_shape := $Area2D/CollisionShape2D
In the code above, Area2D/CollisionShape2D
is a node
path relative to the node to which we attached the script.
By combining this with the
value type, we can export a variable that lets us connect nodes in the editor, as you will see.The $
character is a shortcut for the
get_node()
function. We can write the above as:
onready var collision_shape := get_node("Area2D/CollisionShape2D")
Note: No matter the notation, always use
onready
for acquiring nodes. Otherwise, the nodes might not
have been created by Godot yet, and your variable will be
null
.
In our project, we will give each pressure plate a path to a door.
We will:
PhysicsBody2D
enters it, it
triggers an attached door specified by a .You should be familiar with the process by now so we’ll go a bit faster through the scene creation steps.
Let’s start!
Create a new scene with a
at its root and name the node Door.Add three nodes:
We will use the
to animate the door opening and closing.Find the asset door.png
and assign it to the ’s Texture.
Give the
a shape and make it roughly the size of the door. We use it to block the player.Finally, save the scene and attach a script to the Door node. Your scene should look like this.
Let’s edit the door’s script now.
We will write a single function that opens the door if it is closed and closes it if it is open.
To animate the door, we will use the
node.Let’s first reference the node we need:
extends StaticBody2D
onready var tween := $Tween
To open and close the door, we’ll animate its
position
.
When doing so, we lose the node’s position at the start of the game. So, we store the initial position in a variable to restore when closing the door.
onready var initial_position := position
We also add a variable to track the door’s status.
var is_open := false
We can start our function. We will not call it “open” or “close” because the function will do both.
Let’s call it activate()
. This function will open the
door if it is closed and close it if it is open.
We start by inverting the value of is_open
to track the
door’s state.
func activate() -> void:
= not is_open is_open
If is_open
is true
, it will become
false
. If is_open
is false
, it
will become true
.
Now, we set the target height. We first set the target position to
the initial position. If the door is open, we offset the target position
by the height of the door, 260
pixels.
func activate() -> void:
# ...
var target_position := initial_position
if is_open:
+= 260.0 target_position.y
Finally, we use the
to animate the door to the desired target position.func activate() -> void:
# ...
# If we toggle the door while it is animating, we must first erase and stop
# the existing animation. Tween.remove_all() does both for us.
remove_all()
tween.interpolate_property(
tween.self,
"position",
position,
target_position,0.8,
Tween.TRANS_EXPO,
Tween.EASE_OUT
)start() tween.
Now let’s move on to the pressure plate.
For the pressure plate up we need to detect when a physics body overlaps with it and if so, open the door.
Which node do we use when we need to detect an overlap?
An
!Create a new scene with an
at the root and call it Switch (it’s a switch for the door).Add:
The
has the same setup as the PlayerDetector we made before.We set Monitorable to Off because no other physics object needs to detect it. We also don’t need it to be on any collision layer.
However, we set the collision mask to all of 1
,
2
, and 3
because we want all physics bodies to
press it down.
Drag the pressure-plate.png
image to the , add a to the , and move its handles so it more or
less covers the sprite.
Finally, save the scene and attach a script to the Switch node.
We’re done! Let’s write the script.
In this project, the door will not be a child of the switch. Instead, the switch will receive a path to a door node using the Inspector.
Then, when a body enters or leaves the area, the Switch will
call the door’s activate()
function.
Let’s first observe this line:
extends Area2D
export var door_path: NodePath
This exports a variable. In the editor, it will show as a widget to select a node.
When clicking on it, it will open a dialog allowing us to choose a node in the scene.
We can then load the corresponding node.
onready var door := get_node(door_path)
The node path allows us to get a node whose path we do not know
in advance. We capture it in the door_path
variable
and use it later.
That way, we can connect any switch to any door, regardless of their position in the level’s scene tree.
We finally connect both body_entered
and
body_exited
to a function that calls the door’s
activate()
function.
func _ready() -> void:
connect("body_entered", self, "_activate_door")
connect("body_exited", self, "_activate_door")
func _activate_door(body: PhysicsBody2D) -> void:
activate() door.
You can now open the Level scene and drag a Switch and a Door in it.
Then, select the Switch, and click on the Assign button next to the Door Path property in the Inspector.
In the popup that opens, select the Door.
Your node tree might be different from ours, depending on what you added to the level.
And test the level! The door should open and close when you walk over a pressure plate.
Here’s the whole script for Door.gd
.
extends StaticBody2D
onready var tween := $Tween
onready var initial_position := position
var is_open := false
# Animates the door to position. If the door is open or opening, closes it.
# if the door is closed or closing, opens it.
func activate() -> void:
= not is_open
is_open var target_position := initial_position
if is_open:
+= 260.0
target_position.y # If we toggle the door while it is animating, we must first erase and stop
# the existing animation. Tween.remove_all() does both for us.
remove_all()
tween.interpolate_property(
tween.self,
"position",
position,
target_position,0.8,
Tween.TRANS_EXPO,
Tween.EASE_OUT
)start() tween.
And Switch.gd
.
extends Area2D
export var door_path: NodePath
onready var door := get_node(door_path)
func _ready() -> void:
connect("body_entered", self, "_activate_door")
connect("body_exited", self, "_activate_door")
func _activate_door(body: PhysicsBody2D) -> void:
activate() door.