Unser Spiel bietet bereits Bewegung, Kollisionen, Animationen, Soundeffekte und Partikel. Nun integrieren wir ein Heads-Up Display (HUD), um unseren Spieler:innen den aktuellen Punktestand anzuzeigen. Wir erstellen eine eigene Scene für das UI. Das ist Best Practice in Godot für modulare und übersichtliche Projekte. Auf diese Weise können wir die UI für mehrere Levels einfach wiederverwenden.
In diesem Artikel wirst du:
- Eine neue, separate Scene für unser UI erstellen.
- Einen
CanvasLayer
Node als Root dieser UI-Scene verwenden. - Einen
Label
Node für die Punktezahl hinzufügen und konfigurieren. - Ein Signal in unserem
Player
-Script deklarieren, das den neuen Punktestand aussendet. - Ein neues Script für die HUD-Scene erstellen, das auf dieses Signal hört.
- Die UI-Scene in unsere
World
-Scene instanziieren und dort die Verbindung zwischen dem Player-Signal und dem HUD-Script herstellen.
Schritt 1: Eine neue Scene für das UI erstellen (hud.tscn
)
Für UI-Elemente verwendet man in Godot Nodes, die von der Basisklasse Control
erben. Um diese UI-Elemente über der Spielwelt (funktioniert sowohl in 2D als auch in 3D) anzuzeigen und sicherzustellen, dass sie sich nicht mit der Spielkamera mitbewegen, verwenden wir üblicherweise einen CanvasLayer
. Ein CanvasLayer
ist wie eine separate Zeichenebene, die immer über der Standard-Ebene liegt.
- Erstelle eine neue Scene: Gehe im Menü auf Scene -> New Scene (Strg + N).
- Wähle CanvasLayer als Root Node: Klicke auf „Other Node“, suche und wähle
CanvasLayer
. Klicke „Create“. Benenne diesen Root Node im Scene Dock inHUD
um. - Füge einen Label Node hinzu: Wähle im Scene Dock den
HUD
(CanvasLayer
) Node aus. Klicke auf den „+“ Button (Child Node hinzufügen). Suche und wähleLabel
. Klicke „Create“. Benenne diesenLabel
inScoreLabel
um. - Konfiguriere den ScoreLabel:
- Wähle den
ScoreLabel
Node im Scene Dock aus. - Gehe in den Inspector.
- Suche die Eigenschaft „Text“ und gib einen Startwert ein, z.B.:
Score: 0
. - Im Abschnitt „Control“:
- Finde die Eigenschaft „Layout“ und klappe sie auf. Wähle im Bereich Anchors Preset die Option „Top Left“ (oben links).
- Finde den Abschnitt „Theme Overrides“ und darin „Font Sizes“. Setze die
font_size
auf etwas Größeres. Probiere ein wenig aus, bis du mit der Größe zufrieden bist (z.B. 24). Das Ergebnis dieser Einstellung kannst du direkt im Viewport sehen.
- Wähle den
- Speichere die UI-Scene: Gehe im Menü auf Scene -> Save Scene (Strg + S) im Ordner scenes. Nenne sie
hud.tscn
und speichere sie am besten im Projekt-Root oder in einem dediziertenui
-Ordner.
Schritt 2: Script für die HUD-Scene erstellen (hud.gd
)
Damit unser HUD auf die Punktestandsänderungen reagieren kann, geben wir ihm ein eigenes Script. Dieses Script ist für die Aktualisierung des ScoreLabel
verantwortlich.
- Script an HUD-Scene anhängen:
- Öffne deine Szene
hud.tscn
. - Wähle im Scene Dock den Root Node
HUD
(CanvasLayer
) aus. - Klicke auf den „Attach Script“ Button (Pergamentrolle mit Plus) oberhalb des Scene Trees.
- Als „Base Type“ sollte
CanvasLayer
stehen, und der Pfadhud.gd
vorschlagen. Klicke „Create“. - Speichere das
hud.gd
Script (Strg + S).
- Öffne deine Szene
- Referenz auf ScoreLabel und Aktualisierungsfunktion: In diesem Script referenzieren wir unser
ScoreLabel
und erstellen eine Funktion, die den Text aktualisiert.
1 2 3 4 5 6 7 8 9 10 11 |
class_name HUD extends CanvasLayer @onready var score_label = $ScoreLabel # Referenz auf den ScoreLabel-Node # Initialisiere den Score-Text, wenn die Szene bereit ist func _ready(): update_score_display(0) # Zeigt zu Beginn "Score: 0" an # Diese Funktion wird von außen aufgerufen, um den Score zu aktualisieren func update_score_display(new_score: int): score_label.text = "Score: " + str(new_score) |
Schritt 3: Signal im Player-Script deklarieren und aussenden
Unser Player wird den Punktestand verwalten und ein Signal aussenden, wann immer sich dieser ändert. Das HUD wird dann auf dieses Signal hören.
- Öffne dein
player.gd
Script. - Ganz oben im Script, unter
class_name Player extends CharacterBody2D
, deklarieren wir ein neues Signal namensscore_updated
. Dieses Signal wird einen Integer-Wert (den neuen Punktestand) übergeben.
1 2 3 4 5 6 7 8 9 10 |
class_name Player extends CharacterBody2D signal score_updated(new_score: int) # Signal für den aktualisierten Score @onready var animated_sprite_2d = $AnimatedSprite2D @onready var collect_animation = $CollectAnimation var speed = 200 # Einheit: Pixel pro Sekunde var score = 0 # Punktestand # ... restlicher Code |
3. Gehe zur Funktion collect_coin()
. Hier erhöhen wir den Punktestand und senden dann das
score_updated
-Signal mit dem neuen Wert aus. Das alte print
zur Score-Ausgabe ist nun
optional, da das UI dies übernimmt.
1 2 3 4 5 6 7 8 9 10 11 12 |
func collect_coin() -> void: # ... (Deine bestehende Logik für Sound- und Animationseffekte) ... score += 10 # Erhöhe den Punktestand # Sende das Signal mit dem aktualisierten Score aus emit_signal("score_updated", score) # Ausgabe zur Überprüfung, die ist optional, da das UI den Score anzeigt print(str("Current score: ", score)) # ... (deine bestehende Animationslogik für die Collect-Animation) ... |
Schritt 4: Die UI-Scene in die World-Scene instanziieren und Signale verbinden
Jetzt fügen wir unsere neu erstellte hud.tscn
Scene in unsere world.tscn
Scene ein und stellen die Verbindung zwischen dem Player-Signal und dem HUD her.
- Öffne die Scene
world.tscn
. - Instanziiere die HUD-Scene:
- Wähle im Scene Dock den Root-Node
World
aus. - Ziehe die Datei
hud.tscn
aus dem Filesystem Dock (links unten) auf denWorld
Node im Scene Dock. - Du siehst nun
HUD
als Child vonWorld
im Scene Dock.
- Wähle im Scene Dock den Root-Node
- Verbinde das Signal im
world.gd
Script: Da dieWorld
-Scene sowohl den Player als auch das HUD kennt, ist sie der perfekte Ort, um die Signalverbindung herzustellen. Öffne deinworld.gd
Script. Wir passen die_ready()
-Funktion an, um die Referenzen zu holen und die Verbindung herzustellen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class_name World extends Node func _ready(): var player_node = null var hud_node = null # Referenzen auf Player und HUD holen for child in get_children(): if child is Player: player_node = child if child is HUD: # Prüfen auf HUD-Node (dank class_name HUD) hud_node = child if player_node == null: print("Fehler: Player-Node in der Szene nicht gefunden!") return if hud_node == null: print("Fehler: HUD-Node in der Szene nicht gefunden!") return # Verbinde das 'score_updated'-Signal des Players mit der 'update_score_display'-Funktion des HUD # player_node ist der Emitter, hud_node ist der Receiver player_node.score_updated.connect(hud_node.update_score_display) print("Player-Score-Signal mit HUD verbunden.") # ... (Die Schleife zum Verbinden der Münzen bleibt unverändert) ... |
Darauf achten: Signal-Verbindungen
Die Signal-Verbindung player_node.score_updated.connect(hud_node.update_score_display)
ist ein Paradebeispiel für lose Kopplung. Der Player
weiß nichts über das HUD
und das HUD
weiß nichts über den Player
. Sie kommunizieren über ein abstraktes Signal, das von der World
-Scene als Vermittler verbunden wird. Nur die World
kennt beide. Das macht unseren Code robuster und leichter wartbar.
Schritt 5: Speichern und Ausführen
- Speichere alles (Strg + Alt + Shift + S).
- Führe das Projekt aus (F5).
Ergebnis:
Du siehst nun die Score-Anzeige in der oberen linken Ecke. Wenn du Münzen einsammelst, erhöht sich die Zahl! Die Aktualisierung erfolgt nun elegant über das Signalsystem.
Dein nächster Schritt
Sehr gut! Du hast gelernt, wie man ein grundlegendes User Interface (UI) in Godot erstellt, indem man eine separate, wiederverwendbare Scene dafür nutzt. Du hast das Konzept von CanvasLayer
und Label
verstanden und erfolgreich das Signalsystem von Godot verwendet, um den Punktestand zwischen dem Player und dem UI zu kommunizieren. Dies ist ein entscheidender Schritt für ein sauberes und erweiterbares Spiel-Design.
Als Nächstes werden wir unser Spiel um ein Hauptmenü erweitern und lernen, wie man zwischen verschiedenen Szenen wechselt. Dies ist ein essenzielles Konzept, um ein vollständiges Spielerlebnis zu schaffen.