13.opening-doors-with-the-rocks

Opening doors with the rocks

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 NodePath.

Using a child node is more straightforward, while a node path gives you more flexibility regarding where you place the nodes in your scenes.

What’s a NodePath?

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 NodePath 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:

You should be familiar with the process by now so we’ll go a bit faster through the scene creation steps.

Let’s start!

Creating the door’s scene

Create a new scene with a StaticBody2D at its root and name the node Door.

Add three nodes:

We will use the Tween to animate the door opening and closing.

Find the asset door.png and assign it to the Sprite’s Texture.

Give the CollisionShape2D a RectangleShape2D 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.

The door’s script

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 Tween 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:
    is_open = not 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:
        target_position.y += 260.0

Finally, we use the Tween 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.
    tween.remove_all()
    tween.interpolate_property(
        self,
        "position",
        position,
        target_position,
        0.8,
        Tween.TRANS_EXPO,
        Tween.EASE_OUT
    )
    tween.start()

Now let’s move on to the pressure plate.

Creating the pressure plate’s scene

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 Area2D!

Create a new scene with an Area2D at the root and call it Switch (it’s a switch for the door).

Add:

The Area2D 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 Sprite, add a RectangleShape2D to the CollisionShape2D, 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.

The switch’s 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:
    door.activate()

Testing it out

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.

The code

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:
    is_open = not is_open
    var target_position := initial_position
    if is_open:
        target_position.y += 260.0
    # 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.
    tween.remove_all()
    tween.interpolate_property(
        self,
        "position",
        position,
        target_position,
        0.8,
        Tween.TRANS_EXPO,
        Tween.EASE_OUT
    )
    tween.start()

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:
    door.activate()