Lesson 05
Time to give GlitchFall a goal. We build a Fragment scene that spins and glows, detects the player with an Area3D trigger, and reports itself collected. This is the signal-driven pickup pattern.
Fragment. Save as fragment.tscn.Area3D = trigger, not a wall
An Area3D detects overlaps without blocking movement — perfect for pickups, damage zones, and goals. A StaticBody3D would stop the player; an Area3D lets them pass through while firing a signal.
Attach fragment.gd to the root Fragment:
extends Area3D
# Emitted when the player collects this fragment.
signal collected(fragment: Area3D)
const SPIN_SPEED: float = 1.5 # radians per second
func _ready() -> void:
# React when a body enters our area.
body_entered.connect(_on_body_entered)
func _process(delta: float) -> void:
# A little idle spin so fragments read as collectible.
rotate_y(SPIN_SPEED * delta)
func _on_body_entered(body: Node3D) -> void:
# Only the player should trigger collection.
if body.is_in_group("player"):
collected.emit(self)
queue_free()Groups
body.is_in_group("player") checks a tag instead of a node name or type — flexible and rename-proof. We'll add the player to the player group in the next step. Groups are Godot's lightweight way to categorize nodes.
player.tscn, select the root Player.player.body_entered needs monitoring on
Area3D detects bodies only if its Monitoring property is on (it is by default). If pickups silently never fire, check that Monitoring is enabled and that the player is a physics body (it is — a CharacterBody3D).
main.tscn. Instance fragment.tscn a few times under Main and scatter them around the floor (vary X and Z, keep Y ≈ 0.5).fragments.Attach main.gd to Main to track collection:
extends Node3D
var total_fragments: int = 0
var collected_count: int = 0
func _ready() -> void:
# Find every fragment and listen for its collected signal.
var frags: Array = get_tree().get_nodes_in_group("fragments")
total_fragments = frags.size()
for frag in frags:
frag.collected.connect(_on_fragment_collected)
print("Fragments to collect: %d" % total_fragments)
func _on_fragment_collected(_frag: Area3D) -> void:
collected_count += 1
print("Collected %d / %d" % [collected_count, total_fragments])
if collected_count >= total_fragments:
print("All fragments collected! Exit is now active.")Checkpoint — expected state
fragment.tscn Area3D that spins, glows, and detects the player.collected signal and free themselves.player group; fragments in the fragments group.