Lesson 05

Collectible fragments with Area3D

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.

▸ By the end of this lesson: Spinning fragments that disappear when you touch them and tell Main they're collected

5.1Build the Fragment scene

  1. New Scene → "Other Node" → Area3D as root. Rename Fragment. Save as fragment.tscn.
  2. Add a child MeshInstance3D → New PrismMesh or BoxMesh (a small crystal-ish shape). Scale it down to ~0.5.
  3. Give it a StandardMaterial3D with a bright Albedo and set Emission → Enabled with a matching color so it glows.
  4. Add a child CollisionShape3D → New SphereShape3D, radius ~0.6, so the pickup zone is a bit generous.

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.

5.2The fragment script: spin + detect

Attach fragment.gd to the root Fragment:

fragment.gdgdscript
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.

5.3Tag the player

  1. Open player.tscn, select the root Player.
  2. In the Inspector, open the Node tab (next to Inspector) → Groups → add a group named 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).

5.4Place fragments and count them in Main

  1. Open main.tscn. Instance fragment.tscn a few times under Main and scatter them around the floor (vary X and Z, keep Y ≈ 0.5).
  2. Group them for easy counting: select all fragments, Node tab → Groups → add fragments.

Attach main.gd to Main to track collection:

main.gdgdscript
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.")
  1. Run with F6. Walk into a fragment — it vanishes and the Output shows the running count.
  2. Collect them all; Output announces the exit is active. (We build the actual exit next lesson.)

Checkpoint — expected state