{"id":623,"date":"2025-06-12T09:00:00","date_gmt":"2025-06-12T07:00:00","guid":{"rendered":"https:\/\/staratnight.de\/blog\/?p=623"},"modified":"2025-06-07T13:07:46","modified_gmt":"2025-06-07T11:07:46","slug":"sound-und-visuelles-feedback","status":"publish","type":"post","link":"https:\/\/staratnight.de\/blog\/sound-und-visuelles-feedback\/","title":{"rendered":"Sound und visuelles Feedback"},"content":{"rendered":"\n<p>Unser Spiel nimmt immer mehr Form an! Die M\u00fcnzen sind animiert und unser Player bewegt sich geschmeidig. Aber noch fehlt etwas, um das Einsammeln der M\u00fcnzen wirklich befriedigend zu machen: <strong>Sound<\/strong> und ein <strong>visueller Effekt<\/strong>.<\/p>\n\n\n\n<p>In diesem Artikel werden wir:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Einen Sound abspielen, wenn eine M\u00fcnze eingesammelt wird.<\/li>\n\n\n\n<li>Eine <strong>neue Animation<\/strong> oberhalb der bestehenden Player-Animation abspielen, um das Einsammeln der M\u00fcnze zus\u00e4tzlich hervorzuheben.<\/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: Einen Sound f\u00fcr die M\u00fcnze hinzuf\u00fcgen<\/h3>\n\n\n\n<p>Soundeffekte sind entscheidend f\u00fcr das Spielerlebnis. Ein kurzer &#8222;Klingeln&#8220;-Sound, wenn eine M\u00fcnze gesammelt wird, gibt sofortiges Feedback. In Godot nutzen wir daf\u00fcr den Node <strong><code>AudioStreamPlayer2D<\/code><\/strong>.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>F\u00fcge einen Sound-Node zur M\u00fcnze hinzu:<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u00d6ffne deine Szene <strong><code>coin.tscn<\/code><\/strong>.<\/li>\n\n\n\n<li>W\u00e4hle im Scene Dock deinen <strong>Coin<\/strong> (<code>Area2D<\/code>) Node aus.<\/li>\n\n\n\n<li>Klicke auf den <strong>&#8222;+&#8220;<\/strong> Button (Kind-Node hinzuf\u00fcgen).<\/li>\n\n\n\n<li>Suche und w\u00e4hle <strong><code>AudioStreamPlayer2D<\/code><\/strong>. Klicke <strong>&#8222;Create&#8220;<\/strong>. Benenne ihn zum Beispiel in <strong><code>CollectSound<\/code><\/strong> um.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Lade eine Sounddatei:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Gehe in den FileSystem-Dock und erstelle einen neuen Ordner &#8222;<strong>sounds<\/strong>&#8222;.<\/li>\n\n\n\n<li>Suche dir eine Sound-Datei aus (z.B. ein <a href=\"https:\/\/itch.io\/game-assets\/tag-coins\/tag-sound-effects\">Sound-Effekt von itch.io<\/a>), lade sie herunter und entpacke sie. Danach ziehe die ausgew\u00e4hlte Datei in den FileSystem-Dock in den gerade erstellen Ordner &#8222;<strong>sounds<\/strong>&#8222;.<\/li>\n\n\n\n<li>W\u00e4hle den neuen <strong><code>CollectSound<\/code><\/strong> Node im Scene Dock aus.<\/li>\n\n\n\n<li>Gehe in den Inspector. Du siehst dort die Eigenschaft <strong>&#8222;Stream&#8220;<\/strong>, die momentan &#8222;&lt;empty>&#8220; ist.<\/li>\n\n\n\n<li>Klicke auf das Dropdown-Men\u00fc neben &#8222;Stream&#8220; und w\u00e4hle <strong>&#8222;Load&#8220;<\/strong>. Navigiere zu deiner Sounddatei und w\u00e4hle sie aus. <\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Spiele den Sound im Script ab:<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u00d6ffne dein <code>coin.gd<\/code> Script.<\/li>\n\n\n\n<li>Finde die Funktion <code>_on_Coin_body_entered(body)<\/code>.<\/li>\n\n\n\n<li>Wir m\u00fcssen den Sound-Node in unserem Script referenzieren, um ihn abzuspielen. F\u00fcge dazu die folgende Zeile oben im Script ein, \u00e4hnlich wie du es f\u00fcr <code>AnimatedSprite2D<\/code> gemacht hast:<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:python decode:true \" >class_name Coin extends Area2D\n# Deklariere ein neues Signal, das ausgesendet wird, wenn die M\u00fcnze eingesammelt wird. \nsignal collected\n\n@onready var collect_sound = $CollectSound \n# ... restlicher Code<\/pre><\/div>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Jetzt kannst du <code>collect_sound.play()<\/code> aufrufen, bevor die M\u00fcnze entfernt wird.<\/li>\n<\/ul>\n\n\n\n<p>Dein <code>_on_Coin_body_entered<\/code> sollte nun so aussehen:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:python decode:true \" >func _on_Coin_body_entered(body):\n    if body.is_in_group(\"players\"):\n        print(\"M\u00fcnze ber\u00fchrt!\")\n\n        collect_sound.play() # Aufsammeln-Sound abspielen\n        emit_signal(\"collected\")\n\n        # Wichtig: Wir warten auf das Ende des Sounds. \n        # Damit es so aussieht als sei die M\u00fcnze schon gesammelt, blenden wir sie aus\n        hide()\n        await collect_sound.finished\n\n        queue_free()<\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Darauf achten: <code>queue_free()<\/code> und Sound<\/h4>\n\n\n\n<p>Wenn du <code>queue_free()<\/code> aufrufst, wird der Node am Ende des aktuellen Frames aus der Szene entfernt. <code>AudioStreamPlayer2D<\/code> Nodes spielen ihren Sound nicht mehr weiter, wenn er entfernt wird. Da er ein Child-Node unseres Coins ist, w\u00fcrde der Sound also sofort wieder enden. <br>Wir warten mit <code>await sound.finished <\/code>bis der Soundeffekt fertig abgespielt wurde. Erst dann rufen wir <code>queue_free()<\/code> auf.<\/p>\n\n\n\n<p>Das ist wichtig, da wir die M\u00fcnze sofort verschwinden lassen wollen, der Sound aber komplett abgespielt werden soll.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Schritt 2: Eine Sammel-Animation f\u00fcr den Player hinzuf\u00fcgen<\/h3>\n\n\n\n<p>Neben dem Sound k\u00f6nnen wir auch eine kurze Animation am Player abspielen, die signalisiert, dass etwas eingesammelt wurde. Diese Animation soll kurz \u00fcber der normalen Lauf- oder Idle-Animation erscheinen. Daf\u00fcr nutzen wir einen <strong>zweiten <code>AnimatedSprite2D<\/code><\/strong> am Player.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>F\u00fcge einen zweiten AnimatedSprite2D zum Player hinzu:<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u00d6ffne deine Szene <strong><code>player.tscn<\/code><\/strong>.<\/li>\n\n\n\n<li>W\u00e4hle im Scene Dock deinen <strong>Player<\/strong> (<code>CharacterBody2D<\/code>) Node aus.<\/li>\n\n\n\n<li>Klicke auf den <strong>&#8222;+&#8220;<\/strong> Button (Kind-Node hinzuf\u00fcgen).<\/li>\n\n\n\n<li>Suche und w\u00e4hle <strong><code>AnimatedSprite2D<\/code><\/strong>. Klicke <strong>&#8222;Create&#8220;<\/strong>.<\/li>\n\n\n\n<li>Benenne diesen neuen Node in <strong><code>AnimatioEffect<\/code><\/strong> um, um ihn von deinem bestehenden Player-Animations-Node zu unterscheiden.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Erstelle eine neue SpriteFrames-Ressource f\u00fcr die Sammel-Animation:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Suche dir eine weitere Animation aus (z.B. einen <a href=\"https:\/\/opengameart.org\/content\/item-popup-starburst\">Effekt von OpenGameArt.org<\/a>), lade sie herunter und entpacke sie. Danach ziehe die Datei 0 &#8211; 14 in den FileSystem-Dock in den Ordner &#8222;<strong>images<\/strong>&#8222;.<\/li>\n\n\n\n<li>W\u00e4hle den <strong><code>AnimationEffect<\/code><\/strong> Node im Scene Dock aus.<\/li>\n\n\n\n<li>Gehe in den Inspector. Erstelle eine <strong>&#8222;New SpriteFrames&#8220;<\/strong> Ressource f\u00fcr die Eigenschaft <strong>&#8222;Sprite Frames&#8220;<\/strong>.<\/li>\n\n\n\n<li>Klicke auf das <strong>SpriteFrames<\/strong>-Drop-Down um den Sprite-Frame-Editor zu \u00f6ffnen.<\/li>\n\n\n\n<li>Benenne die Animation &#8222;<strong>default<\/strong>&#8220; um in <strong>&#8222;collect&#8220;<\/strong>.<\/li>\n\n\n\n<li>F\u00fcge Frames hinzu, die den kurzen visuellen Effekt f\u00fcr das Einsammeln darstellen sollen. Achte darauf, dass diese Animation <strong>NICHT<\/strong> loopend ist (<code>Loop<\/code> deaktivieren) und stelle eine angemessene <strong>&#8222;Speed (FPS)&#8220;<\/strong> ein (z.B. 15 FPS f\u00fcr die 15 Bilder des verlinkten Effekts).<\/li>\n\n\n\n<li>Optional: du kannst die Gr\u00f6\u00dfe der Animation im Viewport anpassen.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Spiele die Animation im Player-Script ab:<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u00d6ffne dein <code>player.gd<\/code> Script.<\/li>\n\n\n\n<li>Referenziere den neuen <code>CollectAnimation<\/code> Node, \u00e4hnlich wie du es f\u00fcr die erste <code>Animation<\/code> gemacht hast:<\/li>\n<\/ul>\n<\/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\n@onready var animation = $Animation\n@onready var animation_effect = $AnimationEffect\n# ... restlicher Code<\/pre><\/div>\n\n\n\n<ul class=\"wp-block-list\">\n<li>In der <code>collect_coin()<\/code> Funktion, die aufgerufen wird, wenn das M\u00fcnz-Signal empfangen wird, k\u00f6nnen wir nun diese neue Animation starten.<\/li>\n<\/ul>\n\n\n\n<p>Deine <code>collect_coin()<\/code>-Funktion sollte nun so aussehen:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:python decode:true \" >func collect_coin(): \n\tprint(\"Player hat die M\u00fcnze erfolgreich gesammelt!\") \n\tanimation_effect.show() # Effect-Animation sichtbar machen\n\tanimation_effect.play(\"collect\") # Sammel-Animation abspielen<\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Darauf achten: \u00dcberlappende Animationen<\/h4>\n\n\n\n<p>Indem wir einen separaten <code>AnimatedSprite2D<\/code> f\u00fcr die Sammel-Animation verwenden, kann dieser Effekt <strong>\u00fcber<\/strong> dem Spieler abgespielt werden. Die Reihenfolge der Nodes im Scene-Node steuert dabei die Sichtbarkeit. Je weiter untenim Scene Tree steht, desto sp\u00e4ter wird er in die Szene gezeichnet. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Schritt 3: Sammel-Animation nach Beendigung ausblenden<\/h3>\n\n\n\n<p>Damit unsere Sammel-Animation nicht einfach stehen bleibt, nachdem sie abgespielt wurde, blenden wir sie wieder aus. <code>AnimatedSprite2D<\/code> Nodes haben ein sehr n\u00fctzliches Signal namens <code>animation_finished()<\/code>, das ausgel\u00f6st wird, sobald eine nicht-loopende Animation beendet ist.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Verbinde das <code>animation_finished()<\/code>-Signal der <code>CollectAnimation<\/code>:<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u00d6ffne deine Szene <strong><code>player.tscn<\/code><\/strong>.<\/li>\n\n\n\n<li>W\u00e4hle im Scene Dock den <strong><code>Animation<\/code>Effect<\/strong> Node aus.<\/li>\n\n\n\n<li>Gehe ins <strong>Node Dock<\/strong> (neben dem Inspector).<\/li>\n\n\n\n<li>Suche im Tab &#8222;<strong>Signals<\/strong>&#8220; das Signal <strong><code>animation_finished()<\/code><\/strong>.<\/li>\n\n\n\n<li>Klicke doppelt darauf oder w\u00e4hle es aus und klicke <strong>&#8222;Connect&#8230;&#8220;<\/strong>.<\/li>\n\n\n\n<li>Im &#8222;Connect a Signal&#8220;-Fenster sollte der <strong>Player<\/strong> als &#8222;Receiver&#8220; ausgew\u00e4hlt sein. Die Methode sollte <code>_on_animation_effect_animation_finished<\/code> lauten.<\/li>\n\n\n\n<li>Klicke <strong>&#8222;Connect&#8220;<\/strong>. Godot erstellt die Funktion in deinem <code>player.gd<\/code> Script.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Implementiere die Ausblend-Logik im Player-Script:<\/strong><ul><li>\u00d6ffne dein <code>player.gd<\/code> Script.In der neu erstellten Funktion <code><code>_on_animation_effect_animation_finished<\/code>()<\/code> blenden wir den <code>CollectAnimation<\/code>-Node einfach wieder aus.<\/li><\/ul>Dein <code>player.gd<\/code> Script sollte nun diese neue Funktion enthalten:<\/li>\n<\/ol>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:python decode:true \" >func _on_CollectAnimation_animation_finished():\n    # Blendet die Sammel-Animation aus, sobald sie beendet ist\n    animation_effect.hide()<\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Darauf achten: <code>animation_finished()<\/code> vs. <code>animation_looped()<\/code><\/h4>\n\n\n\n<p><code>AnimatedSprite2D<\/code> Nodes bieten zwei wichtige Signale, die sich auf den Status der Animation beziehen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>animation_finished()<\/code><\/strong>: Dieses Signal wird <strong>einmalig<\/strong> ausgel\u00f6st, wenn eine Animation <strong>ihr Ende erreicht hat<\/strong>. Es wird nur f\u00fcr Animationen, die nur einmal abgespielt werden sollen (z.B. ein Sammel-Effekt, ein Treffer, ein Tod), und deren <code>Loop<\/code>-Eigenschaft im SpriteFrames-Editor auf <strong>false<\/strong> gesetzt ist, ausgef\u00fchrt.<\/li>\n\n\n\n<li><strong><code>animation_looped()<\/code><\/strong>: Dieses Signal wird jedes Mal ausgel\u00f6st, wenn eine Animation <strong>einen vollst\u00e4ndigen Durchlauf abgeschlossen und erneut begonnen hat<\/strong>. Es wird nur f\u00fcr Animationen, die in einer Endlosschleife laufen (wie die Idle- oder Lauf-Animation unseres Players, deren <code>Loop<\/code>-Eigenschaft auf <strong>true<\/strong> gesetzt ist) und du auf jeden Zyklus reagieren m\u00f6chtest (z.B. einen Schritt-Sound nach jedem Schritt-Zyklus abspielen), ausgef\u00fchrt.<\/li>\n<\/ul>\n\n\n\n<p>F\u00fcr unsere <code>CollectAnimation<\/code>, bei der die <code>Loop<\/code>-Eigenschaft im SpriteFrames-Editor auf <strong>false<\/strong> gesetzt ist, ist <code>animation_finished()<\/code> die richtige Wahl.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Schritt 4: Speichern und Testen<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Speichere deine Szene <strong><code>player.tscn<\/code><\/strong> (Strg + S).<\/li>\n\n\n\n<li>Speichere dein <code>player.gd<\/code> Skript (Strg + S) und dein <code>coin.gd<\/code> Skript (Strg + S).<\/li>\n\n\n\n<li>F\u00fchre dein Projekt aus (F5).<\/li>\n<\/ol>\n\n\n\n<p>Jetzt sollte dein Player beim Einsammeln einer M\u00fcnze nicht nur einen Sound abspielen, sondern auch eine zus\u00e4tzliche, kurze Animation zeigen!<\/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>Wir haben unseren Interaktionen Sound und zus\u00e4tzliche visuelle Effekte hinzugef\u00fcgt, was das Spielerlebnis deutlich verbessert. Du hast gelernt, wie man:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>AudioStreamPlayer2D<\/code><\/strong> Nodes f\u00fcr Soundeffekte einsetzt.<\/li>\n\n\n\n<li>Mehrere <strong><code>AnimatedSprite2D<\/code><\/strong> Nodes an einem Charakter verwendet, um \u00fcberlappende Animationen zu erzeugen.<\/li>\n\n\n\n<li>Daf\u00fcr sorgt, dass Animationen ausgeblendet werden, nachdem sie abgespielt wurden.<\/li>\n<\/ul>\n\n\n\n<p>Als N\u00e4chstes werden wir unser Spiel noch dynamischer gestalten, indem wir einen <strong>Partikeleffekt<\/strong> erzeugen, wenn eine M\u00fcnze eingesammelt wird. Das f\u00fchrt uns in die Welt der <strong><code>CPUParticles2D<\/code><\/strong> Nodes und deren Konfiguration.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Unser Spiel nimmt immer mehr Form an! Die M\u00fcnzen sind animiert und unser Player bewegt sich geschmeidig. Aber noch fehlt etwas, um das&hellip;<\/p>\n","protected":false},"author":2,"featured_media":634,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[33],"tags":[35,43,34,44],"class_list":["post-623","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-godot","tag-2d-game","tag-animation","tag-godot","tag-sound"],"_links":{"self":[{"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/posts\/623","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=623"}],"version-history":[{"count":3,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/posts\/623\/revisions"}],"predecessor-version":[{"id":651,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/posts\/623\/revisions\/651"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/media\/634"}],"wp:attachment":[{"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/media?parent=623"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/categories?post=623"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/staratnight.de\/blog\/wp-json\/wp\/v2\/tags?post=623"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}