Lesson 08
The player needs on-screen feedback, not Output logs. We add a 3D-game HUD — a fragment counter and an objective hint — plus a win screen with a restart, using the same Control/CanvasLayer approach as the 2D track.
main.tscn. Add a child CanvasLayer to Main, rename HUD. 2D UI sits on a CanvasLayer even in a 3D game — it renders on top of the 3D viewport.HUD, add a Label named FragmentLabel, anchored top-left, text Fragments: 0 / 0.ObjectiveLabel below it, text Collect all fragments.@onready var fragment_label: Label = $HUD/FragmentLabel
@onready var objective_label: Label = $HUD/ObjectiveLabel
func _refresh_hud() -> void:
fragment_label.text = "Fragments: %d / %d" % [collected_count, total_fragments]
if collected_count >= total_fragments:
objective_label.text = "Reach the exit!"
else:
objective_label.text = "Collect all fragments"Call _refresh_hud() at the end of _ready() and inside _on_fragment_collected():
func _on_fragment_collected(_frag: Area3D) -> void:
collected_count += 1
_refresh_hud()
if collected_count >= total_fragments:
exit.activate()HUD, add a PanelContainer named WinPanel, centered.You escaped the glitch!) and a Button named RestartButton (text Play Again).WinPanel and set its Visible to OFF.@onready var win_panel: PanelContainer = $HUD/WinPanel
@onready var restart_button: Button = $HUD/WinPanel/VBoxContainer/RestartButton
# in _ready(), after other setup:
win_panel.visible = false
restart_button.pressed.connect(_on_restart_pressed)
_refresh_hud()
func _on_exit_reached() -> void:
win_panel.visible = true
# Free the mouse so the player can click Play Again.
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
get_tree().paused = true
func _on_restart_pressed() -> void:
get_tree().paused = false
get_tree().reload_current_scene()Unpause before reloading
We pause the tree on win for a clean freeze. If you reload the scene while still paused, the new scene starts frozen. Always set paused = false before reload_current_scene().
Restart button needs to run while paused
Because we pause on win, the button's node must process while paused. Select HUD (or the button) and set Process → Mode to Always, or the click won't register.
Checkpoint — expected state