{"id":644,"date":"2025-06-16T09:00:00","date_gmt":"2025-06-16T07:00:00","guid":{"rendered":"https:\/\/staratnight.de\/blog\/?p=644"},"modified":"2026-03-08T23:03:16","modified_gmt":"2026-03-08T21:03:16","slug":"hud-und-punktanzeige","status":"publish","type":"post","link":"https:\/\/staratnight.de\/blog\/hud-und-punktanzeige\/","title":{"rendered":"HUD und Punktanzeige"},"content":{"rendered":"\n<p>Unser Spiel bietet bereits Bewegung, Kollisionen, Animationen, Soundeffekte und Partikel. Nun integrieren wir ein <strong>Heads-Up Display (HUD)<\/strong>, um unseren Spieler:innen den aktuellen Punktestand anzuzeigen. Wir erstellen eine <strong>eigene Scene<\/strong> f\u00fcr das UI. Das ist Best Practice in Godot f\u00fcr modulare und \u00fcbersichtliche Projekte. Auf diese Weise k\u00f6nnen wir die UI f\u00fcr mehrere Levels einfach wiederverwenden.<\/p>\n\n\n\n<p>In diesem Artikel wirst du:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Eine neue, separate Scene f\u00fcr unser UI erstellen. <\/li>\n\n\n\n<li>Einen <strong><code>CanvasLayer<\/code><\/strong> Node als Root dieser UI-Scene verwenden. <\/li>\n\n\n\n<li>Einen <strong><code>Label<\/code><\/strong> Node f\u00fcr die Punktezahl hinzuf\u00fcgen und konfigurieren. <\/li>\n\n\n\n<li>Ein <strong>Signal<\/strong> in unserem <code>Player<\/code>-Script deklarieren, das den neuen Punktestand aussendet. <\/li>\n\n\n\n<li>Ein neues Script f\u00fcr die HUD-Scene erstellen, das auf dieses Signal h\u00f6rt. <\/li>\n\n\n\n<li>Die UI-Scene in unsere <code>World<\/code>-Scene instanziieren und dort die Verbindung zwischen dem Player-Signal und dem HUD-Script herstellen.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Schritt 1: Eine neue Scene f\u00fcr das UI erstellen (<code>hud.tscn<\/code>)<\/h3>\n\n\n\n<p>F\u00fcr UI-Elemente verwendet man in Godot Nodes, die von der Basisklasse <strong><code>Control<\/code><\/strong> erben. Um diese UI-Elemente \u00fcber der Spielwelt (funktioniert sowohl in 2D als auch in 3D) anzuzeigen und sicherzustellen, dass sie sich nicht mit der Spielkamera mitbewegen, verwenden wir \u00fcblicherweise einen <strong><code>CanvasLayer<\/code><\/strong>. Ein <code>CanvasLayer<\/code> ist wie eine separate Zeichenebene, die immer \u00fcber der Standard-Ebene liegt.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Erstelle eine neue Scene:<\/strong> Gehe im Men\u00fc auf <strong>Scene<\/strong> -&gt; <strong>New Scene<\/strong> (Strg + N).<\/li>\n\n\n\n<li><strong>W\u00e4hle CanvasLayer als Root Node:<\/strong> Klicke auf <strong>&#8222;Other Node&#8220;<\/strong>, suche und w\u00e4hle <strong><code>CanvasLayer<\/code><\/strong>. Klicke <strong>&#8222;Create&#8220;<\/strong>. Benenne diesen Root Node im Scene Dock in <strong><code>HUD<\/code><\/strong> um.<\/li>\n\n\n\n<li><strong>F\u00fcge einen Label Node hinzu:<\/strong> W\u00e4hle im Scene Dock den <strong><code>HUD<\/code><\/strong> (<code>CanvasLayer<\/code>) Node aus. Klicke auf den <strong>&#8222;+&#8220;<\/strong> Button (Child Node hinzuf\u00fcgen). Suche und w\u00e4hle <strong><code>Label<\/code><\/strong>. Klicke <strong>&#8222;Create&#8220;<\/strong>. Benenne diesen <code>Label<\/code> in <strong><code>ScoreLabel<\/code><\/strong> um.<\/li>\n\n\n\n<li><strong>Konfiguriere den ScoreLabel:<\/strong>\n<ul class=\"wp-block-list\">\n<li>W\u00e4hle den <strong><code>ScoreLabel<\/code><\/strong> Node im Scene Dock aus.<\/li>\n\n\n\n<li>Gehe in den Inspector.<\/li>\n\n\n\n<li>Suche die Eigenschaft <strong>&#8222;Text&#8220;<\/strong> und gib einen Startwert ein, z.B.: <code>Score: 0<\/code>.<\/li>\n\n\n\n<li>Im Abschnitt <strong>&#8222;Control&#8220;<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Finde die Eigenschaft <strong>&#8222;Layout&#8220;<\/strong> und klappe sie auf. W\u00e4hle im Bereich <strong>Anchors Preset<\/strong> die Option <strong>&#8222;Top Left&#8220;<\/strong> (oben links).<\/li>\n\n\n\n<li>Finde den Abschnitt <strong>&#8222;Theme Overrides&#8220;<\/strong> und darin <strong>&#8222;Font Sizes&#8220;<\/strong>. Setze die <strong><code>font_size<\/code><\/strong> auf etwas Gr\u00f6\u00dferes. Probiere ein wenig aus, bis du mit der Gr\u00f6\u00dfe zufrieden bist (z.B. 24). Das Ergebnis dieser Einstellung kannst du direkt im <strong>Viewport <\/strong>sehen.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Speichere die UI-Scene:<\/strong> Gehe im Men\u00fc auf <strong>Scene<\/strong> -> <strong>Save Scene<\/strong> (Strg + S) im Ordner <strong>scenes<\/strong>. Nenne sie <strong><code>hud.tscn<\/code><\/strong> und speichere sie am besten im Projekt-Root oder in einem dedizierten <code>ui<\/code>-Ordner.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Schritt 2: Script f\u00fcr die HUD-Scene erstellen (<code>hud.gd<\/code>)<\/h3>\n\n\n\n<p>Damit unser HUD auf die Punktestands\u00e4nderungen reagieren kann, geben wir ihm ein eigenes Script. Dieses Script ist f\u00fcr die Aktualisierung des <code>ScoreLabel<\/code> verantwortlich.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Script an HUD-Scene anh\u00e4ngen:<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u00d6ffne deine Szene <strong><code>hud.tscn<\/code><\/strong>.<\/li>\n\n\n\n<li>W\u00e4hle im Scene Dock den Root Node <strong><code>HUD<\/code><\/strong> (<code>CanvasLayer<\/code>) aus.<\/li>\n\n\n\n<li>Klicke auf den <strong>&#8222;Attach Script&#8220;<\/strong> Button (Pergamentrolle mit Plus) oberhalb des Scene Trees.<\/li>\n\n\n\n<li>Als <strong>&#8222;Base Type&#8220;<\/strong> sollte <code>CanvasLayer<\/code> stehen, und der Pfad <code>hud.gd<\/code> vorschlagen. Klicke <strong>&#8222;Create&#8220;<\/strong>.<\/li>\n\n\n\n<li>Speichere das <code>hud.gd<\/code> Script (Strg + S).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Referenz auf ScoreLabel und Aktualisierungsfunktion:<\/strong> In diesem Script referenzieren wir unser <code>ScoreLabel<\/code> und erstellen eine Funktion, die den Text aktualisiert.<\/li>\n<\/ol>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:python decode:true \" >class_name HUD extends CanvasLayer\n\n@onready var score_label = $ScoreLabel # Referenz auf den ScoreLabel-Node\n\n# Initialisiere den Score-Text, wenn die Szene bereit ist\nfunc _ready():\n    update_score_display(0) # Zeigt zu Beginn \"Score: 0\" an\n\n# Diese Funktion wird von au\u00dfen aufgerufen, um den Score zu aktualisieren\nfunc update_score_display(new_score: int):\n    score_label.text = \"Score: \" + str(new_score)\n<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Schritt 3: Signal im Player-Script deklarieren und aussenden<\/h3>\n\n\n\n<p>Unser Player wird den Punktestand verwalten und ein Signal aussenden, wann immer sich dieser \u00e4ndert. Das HUD wird dann auf dieses Signal h\u00f6ren.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u00d6ffne dein <code>player.gd<\/code> Script.<\/li>\n\n\n\n<li>Ganz oben im Script, unter <code>class_name Player extends CharacterBody2D<\/code>, deklarieren wir ein neues Signal namens <code>score_updated<\/code>. Dieses Signal wird einen Integer-Wert (den neuen Punktestand) \u00fcbergeben.<\/li>\n<\/ol>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:python decode:true \" >class_name Player extends CharacterBody2D\n\nsignal score_updated(new_score: int) # Signal f\u00fcr den aktualisierten Score\n\n@onready var animated_sprite_2d = $AnimatedSprite2D\n@onready var collect_animation = $CollectAnimation\n\nvar speed = 200 # Einheit: Pixel pro Sekunde\nvar score = 0 # Punktestand\n# ... restlicher Code<\/pre><\/div>\n\n\n\n<p>     3. Gehe zur Funktion <code>collect_coin()<\/code>. Hier erh\u00f6hen wir den Punktestand und senden dann das <br>         <code>score_updated<\/code>-Signal mit dem neuen Wert aus. Das alte <code>print<\/code> zur Score-Ausgabe ist nun<br>         optional, da das UI dies \u00fcbernimmt.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:python decode:true \" >func collect_coin() -&gt; void:\n    # ... (Deine bestehende Logik f\u00fcr Sound- und Animationseffekte) ...\n\n    score += 10 # Erh\u00f6he den Punktestand\n\n    # Sende das Signal mit dem aktualisierten Score aus\n    emit_signal(\"score_updated\", score)\n\n    # Ausgabe zur \u00dcberpr\u00fcfung, die ist optional, da das UI den Score anzeigt\n    print(str(\"Current score: \", score)) \n\n    # ... (deine bestehende Animationslogik f\u00fcr die Collect-Animation) ...<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Schritt 4: Die UI-Scene in die World-Scene instanziieren und Signale verbinden<\/h3>\n\n\n\n<p>Jetzt f\u00fcgen wir unsere neu erstellte <code>hud.tscn<\/code> Scene in unsere <code>world.tscn<\/code> Scene ein und stellen die Verbindung zwischen dem Player-Signal und dem HUD her.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u00d6ffne die Scene <strong><code>world.tscn<\/code><\/strong>.<\/li>\n\n\n\n<li><strong>Instanziiere die HUD-Scene:<\/strong>\n<ul class=\"wp-block-list\">\n<li>W\u00e4hle im Scene Dock den Root-Node <strong><code>World<\/code><\/strong> aus.<\/li>\n\n\n\n<li>Ziehe die Datei <strong><code>hud.tscn<\/code><\/strong> aus dem Filesystem Dock (links unten) auf den <strong><code>World<\/code><\/strong> Node im Scene Dock.<\/li>\n\n\n\n<li>Du siehst nun <strong><code>HUD<\/code><\/strong> als Child von <strong><code>World<\/code><\/strong> im Scene Dock.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Verbinde das Signal im <code>world.gd<\/code> Script:<\/strong> Da die <code>World<\/code>-Scene sowohl den Player als auch das HUD kennt, ist sie der perfekte Ort, um die Signalverbindung herzustellen. \u00d6ffne dein <code>world.gd<\/code> Script. Wir passen die <code>_ready()<\/code>-Funktion an, um die Referenzen zu holen und die Verbindung herzustellen.<\/li>\n<\/ol>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:python decode:true \" >class_name World extends Node\n\nfunc _ready():\n    var player_node = null\n    var hud_node = null\n\n    # Referenzen auf Player und HUD holen\n    for child in get_children():\n        if child is Player: \n            player_node = child\n        if child is HUD: # Pr\u00fcfen auf HUD-Node (dank class_name HUD)\n            hud_node = child\n\n    if player_node == null:\n        print(\"Fehler: Player-Node in der Szene nicht gefunden!\")\n        return\n    if hud_node == null:\n        print(\"Fehler: HUD-Node in der Szene nicht gefunden!\")\n        return\n\n    # Verbinde das 'score_updated'-Signal des Players mit der 'update_score_display'-Funktion des HUD\n    # player_node ist der Emitter, hud_node ist der Receiver\n    player_node.score_updated.connect(hud_node.update_score_display)\n    print(\"Player-Score-Signal mit HUD verbunden.\")\n\n    # ... (Die Schleife zum Verbinden der M\u00fcnzen bleibt unver\u00e4ndert) ...<\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Darauf achten: Signal-Verbindungen<\/h4>\n\n\n\n<p>Die Signal-Verbindung <code>player_node.score_updated.connect(hud_node.update_score_display)<\/code> ist ein Paradebeispiel f\u00fcr lose Kopplung. Der <code>Player<\/code> wei\u00df nichts \u00fcber das <code>HUD<\/code> und das <code>HUD<\/code> wei\u00df nichts \u00fcber den <code>Player<\/code>. Sie kommunizieren \u00fcber ein abstraktes Signal, das von der <code>World<\/code>-Scene als Vermittler verbunden wird. Nur die <code>World<\/code> kennt beide. Das macht unseren Code robuster und leichter wartbar.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Schritt 5: Speichern und Ausf\u00fchren<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Speichere alles (Strg + Alt + Shift + S).<\/li>\n\n\n\n<li>F\u00fchre das Projekt aus (F5).<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading\">Ergebnis:<\/h4>\n\n\n\n<p>Du siehst nun die Score-Anzeige in der oberen linken Ecke. Wenn du M\u00fcnzen einsammelst, erh\u00f6ht sich die Zahl! Die Aktualisierung erfolgt nun elegant \u00fcber das Signalsystem.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Dein n\u00e4chster Schritt<\/h3>\n\n\n\n<p>Sehr gut! Du hast gelernt, wie man ein grundlegendes <strong>User Interface (UI)<\/strong> in Godot erstellt, indem man eine separate, wiederverwendbare Scene daf\u00fcr nutzt. Du hast das Konzept von <strong><code>CanvasLayer<\/code><\/strong> und <strong><code>Label<\/code><\/strong> verstanden und erfolgreich das <strong>Signalsystem<\/strong> von Godot verwendet, um den Punktestand zwischen dem Player und dem UI zu kommunizieren. Dies ist ein entscheidender Schritt f\u00fcr ein sauberes und erweiterbares Spiel-Design.<\/p>\n\n\n\n<p>Als N\u00e4chstes werden wir unser Spiel um ein <strong>Hauptmen\u00fc<\/strong> erweitern und lernen, wie man <strong>zwischen verschiedenen Szenen wechselt<\/strong>. Dies ist ein essenzielles Konzept, um ein vollst\u00e4ndiges Spielerlebnis zu schaffen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&hellip;<\/p>\n","protected":false},"author":2,"featured_media":661,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[33],"tags":[35,34,46],"class_list":["post-644","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-godot","tag-2d-game","tag-godot","tag-ui"],"_links":{"self":[{"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/posts\/644","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/comments?post=644"}],"version-history":[{"count":3,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/posts\/644\/revisions"}],"predecessor-version":[{"id":654,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/posts\/644\/revisions\/654"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/media\/661"}],"wp:attachment":[{"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/media?parent=644"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/categories?post=644"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/tags?post=644"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}