05.creating-the-ghost-pickup

Creating the ghost pickup

In this lesson, we’ll extend the Pickup scene to create and test our ghost pickup.

Coding the pickup’s base script

Like before, we will use inheritance and have all pickups extend a base scene and script.

Unlike the turret, this script will be tiny. It will only do one thing: when it detects the player, it will remove itself, and apply its effect to the player.

This script will guarantee that all pickups work the same code-wise.

Reopen the Pickup scene and attach a new script named Pickup.gd to it.

We know every pickup type will have an effect, but we don’t know all the effects we’ll need yet. We define a function named apply_effect() with an empty body. We’ll override it in each pickup to apply the pickup’s effect to the character.

# Virtual function. Applies this pickup's effect on the body node.
func apply_effect(body: Node) -> void:
    pass

This is something we call a virtual function. It’s a function that does nothing until you override it. That’s why we call it “virtual.”

It ensures that the apply_effect() function exists in all pickups and prevents errors.

You have already used some virtual functions from Godot, such as _ready() and _process(). Until you override them in your scripts, these functions exist in the engine but do nothing.

All pickups share a common functionality: when the player touches it, we should destroy the pickup and apply its effect on the player.

onready var animation_player := $AnimationPlayer

func _ready() -> void:
    connect("body_entered", self, "_on_body_entered")

func _on_body_entered(body: Node) -> void:
    animation_player.play("destroy")
    apply_effect(body)

The destroy animation calls the queue_free() function at the end, so playing it will remove the pickup from the level.

At this stage, we already have a working pickup. If we place it in a level and the player touches it, the pickup will get destroyed and call its apply_effect() function.

But since we added the ghost code to the Godot script, we might as well create the ghost pickup before testing.

Creating the ghost pickup inherited scene

To create our ghost pickup, we start by creating an inherited scene.

In the FileSystem dock, right-click on the ObstacleCourse_Part2/pickup/Pickup.tscn file and select New Inherited Scene.

Rename the scene’s root node to PickupGhost and save it in the pickups/ folder.

Then, right-click on the PickupGhost node and click Attach Script to attach a new script to it.

Coding the specific ghost effect

First, we need to extend the Pickup.gd script we just coded.

extends "Pickup.gd"

Next, we override the apply_effect() function from the Pickup base script by declaring a function with the same name and parameters.

This function override takes priority over the virtual function in Pickup.gd.

func apply_effect(body: Node) -> void:
    # We turn on the ghost effect on the character when it touches the pickup.
    body.toggle_ghost_effect(true)

That’s it! All we need to do is call body.toggle_ghost_effect() on the player.

Testing the ghost effect

We can make a temporary 2D scene with a YSort root node in the ObstacleCourse2/ directory to test that the ghost effect gets applied to the player.

Create a new 2D scene. In the FileSystem dock, select the rocks, the Godot character, and the PickupGhost scene files.

You can find the rocks in the obstacles/rocks/ sub-directory.

Drag and drop them into the viewport to instantiate them. Then, spread them around to test the ghost effect.

Play the scene and try to run into the rocks. They should block the character.

Then, walk over the pickup and then over the rocks. The characters should now walk through them.

In the next lesson, we’ll create our second pickup type, the speed boost pickup.

Practice: Increasing the gem counter

Open the practice Increasing the gem counter.

This practice will bring you back to detecting collisions and interactions with the Area2D node.

The code

Here’s the complete code for the files we coded in this lesson.

Pickup.gd

extends Area2D

onready var animation_player := $AnimationPlayer

func _ready() -> void:
    connect("body_entered", self, "_on_body_entered")

func _on_body_entered(body: Node) -> void:
    animation_player.play("destroy")
    apply_effect(body)


# Virtual function. Applies this pickup's effect on the body node.
func apply_effect(body: Node) -> void:
    pass

PickupGhost.gd

extends "Pickup.gd"


func apply_effect(body: Node) -> void:
    # We turn on the ghost effect on the character when it touches the pickup.
    body.toggle_ghost_effect(true)