Nachdem wir unseren Sprite2D
mit einem Script zum Rotieren gebracht und ihm eine grundlegende Bewegung über Input Actions verliehen haben, ist es Zeit für den nächsten wichtigen Schritt: Kollisionserkennung.
In den meisten Spielen müssen Objekte erkennen, wann sie sich berühren oder überlappen. Das ist nötig für Dinge wie:
- Unser Player stößt gegen eine Wand.
- Eine Kugel trifft einen Gegner.
- Unser Player sammelt einen Gegenstand auf.
- Ein Charakter tritt auf eine Druckplatte.
Godot handhabt Kollisionen und Physik mit speziellen Physics Bodies Nodes. Diese Nodes haben die Fähigkeit, mit anderen Physik-Körpern zu interagieren.
Die wichtigsten 2D Physics Body Nodes
Die wichtigsten 2D Physics Body Nodes sind:
- StaticBody2D: Für Objekte, die sich nicht bewegen sollen (Wände, Boden, unbewegliche Hindernisse). Sie reagieren auf Kollisionen, bewegen sich aber selbst nicht.
- RigidBody2D: Für Objekte, die komplett von der Physik-Engine simuliert werden (z.B. eine Kiste, die fällt, ein Ball, der rollt). Wir steuern sie nicht direkt über die Position, sondern über Kräfte oder Impulse.
- CharacterBody2D: Speziell für von Spieler:innen gesteuerte Charaktere entwickelt. Wir steuern die Bewegung direkt (wie wir es tun), und der Body kümmert sich darum, auf Kollisionen mit
StaticBody2D
s oderRigidBody2D
s zu reagieren (z.B. stoppen vor einer Wand, an einer Schräge entlangrutschen). - Area2D: Dient nicht für solide Kollisionen, sondern nur zur Erkennung von Überlappungen (Detection). Nützlich für Trigger-Zonen oder das Einsammeln von Objekten (wenn der Spieler:in eine
Area2D
betritt, kann etwas passieren).
Ein Physics Body Node allein hat noch keine Form für die Kollision. Du musst ihm Child Nodes hinzufügen, die die Kollisionsform definieren. Das sind meist CollisionShape2D (mit einer einfachen Form wie Rechteck, Kreis, Kapsel) oder CollisionPolygon2D (für komplexere, polygonale Formen).
Da unser Sprite ein vom Spieler:in gesteuerter Charakter werden soll, ist CharacterBody2D
der am besten geeignete Node. Wir werden die Scene leicht umbauen müssen, um ihn als Root zu verwenden und unsere Bewegung in seinem Script umzusetzen. Der Vorteil: CharacterBody2D
hat eingebaute Funktionen (move_and_slide
), die Bewegung und Kollisionsreaktion in einem Schritt erledigen.
Schritt 1: Eine neue Scene für den Player erstellen
Es ist best-practice, den Spieler:in (und eigentlich alle Objekte) in einer eigenen Scene zu definieren und diese dann in der Haupt-Level-Scene zu instanziieren.
- Gehe im Menü auf Scene -> New Scene (Strg + N).
- Klicke auf „Other Node“ und wähle CharacterBody2D als Root Node und klicke Create.
- Benenne den Root Node im Scene Dock um zu Player (Rechtsklick auf den Node -> Rename, oder F2 drücken).
- Speichere diese neue Scene: Scene -> Save Scene (Strg + S). Nenne sie z.B.
player.tscn
und speichere sie im Projekt-Root (oder lege einen neuen Ordnerscenes
an und speichere sie dort, was später ordentlicher ist – ich gehe im Folgenden davon aus).
Schritt 2: Visuelle Darstellung und Kollisionsform hinzufügen
Ein CharacterBody2D
benötigt Child Nodes, um sichtbar zu sein und eine Kollisionsform zu erhalten. Wir fügen unserem Player
jetzt einen Sprite2D
für die Grafik und einen CollisionShape2D
für die Kollision hinzu.
- Stelle sicher, dass du im Scene Dock den Player (
CharacterBody2D
) Node ausgewählt hast. - Klicke auf den Button „+“ (Child Node hinzufügen) oberhalb des Scene Trees.
- Suche in der Liste nach Sprite2D, wähle ihn aus und klicke Create. Benenne ihn um in „Sprite“
- Wähle den neuen
Sprite2D
Node im Scene Dock aus. Gehe im Inspector zum Abschnitt „Texture“ und lade die Dateiicon.svg
(Godot-Logo) als seine Texture. - Klicke erneut auf den Button „+“ (Child Node hinzufügen), während der Player Node noch ausgewählt ist.
- Suche in der Liste nach CollisionShape2D, wähle ihn aus und klicke Create. Benne ihn um in „Collision“
- Wähle den neuen
CollisionShape2D
Node im Scene Dock aus. Im Inspector siehst du die Eigenschaft „Shape“, die momentan „<empty>“ ist. - Klicke auf „<empty>“ neben „Shape“ und wähle im Dropdown-Menü „New RectangleShape2D“.
- Godot erstellt eine neue Form. Wechsel auf den Viewport (oben auf „2D“ klicken). Hier siehst du nun die Kollisionsform als blaues Rechteck im Zentrum des Godot-Logos.
- Ziehe die Ankerpunkte des Rechtecks im Viewport so, dass die Kollisionsform das Godot-Logo umschließt.
Schritt 3: Script an den CharacterBody2D anhängen
Jetzt hängen wir unser Script an den Player
Node, um ihm Logik zu verleihen. Dieses Script wird die Bewegung des Players steuern und mit Kollisionen umgehen.
- Wähle im Scene Dock den Root Node Player (
CharacterBody2D
) aus. - Suche im Inspector ganz unten nach „Script“ Klicke auf „<empty>“ und wähle „Load…“.
- Es öffnet sich ein Fenster. Suche das Script „Player.gd“ (vermutlich im Ordner scenes zu finden) und wähle es aus.
- Klicke „Open“.
- Neben dem Node „Player“ im Node-Dock erscheint nun eine Pergamentrolle. Klicke darauf und der Code-Editor öffnet sich.
Dein player.gd
Script sollte jetzt so aussehen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class_name Player extends CharacterBody2D var speed = 200 # Einheit: Pixel pro Sekunde # Called when the node enters the scene tree for the first time. func _ready(): pass # Called every frame. 'delta' ist die Zeit seit dem letzten Frame. func _process(delta): # Rotation (bleibt vorerst hier, da visuell) rotation_degrees += 45 * delta # Bewegung Input verarbeiten var direction = Vector2.ZERO # Wir erzeugen einen Vektor (0,0) für die Bewegungsrichtung if Input.is_action_pressed("move_right"): direction.x += 1 if Input.is_action_pressed("move_left"): direction.x -= 1 if Input.is_action_pressed("move_down"): direction.y += 1 if Input.is_action_pressed("move_up"): direction.y -= 1 if direction.length() > 0: direction = direction.normalized() # HIER kommt gleich der neue Physik-Code hin # Die Bewegung mit move_and_slide() gehört in _physics_process |
Aufräumen: Alte Dateien löschen
Da wir jetzt eine saubere player.tscn
erstellt und unseren Player von Grund auf neu strukturiert haben, brauchen wir die alte Scene main_scene.tscn
nicht mehr, die wir zuvor für unser Test-Sprite verwendet haben.
- Gehe ins Filesystem Dock (links unten).
- Klicke mit der rechten Maustaste auf
main_scene.tscn
und wähle „Delete“ und Bestätige die Löschung.
Schritt 4: Eine Welt-Scene erstellen und den Player instanziieren
Wir brauchen eine Scene, in der unser Player „lebt“ und mit anderen Objekten interagieren kann. Dies wird unsere Haupt-Spielwelt sein.
- Erstelle eine neue Scene (Strg + N).
- Klicke auf „Other Node“ und wähle einen generischen Node als Root Node und klicke Create.
- Benenne den Root Node um in World.
- Speichere diese Scene als
world.tscn
im scene-Ordner. - Instanziiere deine
player.tscn
Scene in die World-Scene (zieh die Dateiplayer.tscn
aus dem Filesystem Dock auf denWorld
Node im Scene Dock). Du siehst jetzt Player als Child von World. - Wechsel auf den Viewport (2D) und du siehst den Player in der World.
Schritt 5: Eine Wand hinzufügen (StaticBody2D)
Jetzt fügen wir ein unbewegliches Objekt hinzu, mit dem der Player kollidieren kann – eine Wand.
- Stelle sicher, dass du die Scene
world.tscn
geöffnet hast. - Wähle im Scene Dock den Root Node World aus.
- Klicke auf den „+“ Button (Child Node hinzufügen).
- Suche und wähle StaticBody2D. Klicke Create. Benenne ihn um, z.B. in Wall.
- Füge dem Wall (
StaticBody2D
) einen CollisionShape2D als Child hinzu (wähle Wall, klicke „+“, suche CollisionShape2D). Nenne ihn „Collision“. - Wähle den
CollisionShape2D
der Wand aus und erstelle im Inspector eine „New RectangleShape2D“ als Shape. - Passe die Größe und Position dieser Wand-Kollisionsform im Viewport an, um eine Wand zu erstellen (z.B. einen langen, schmalen Block). Platziere sie so, dass dein Player später dagegen laufen kann.
- Um die Wand sichtbar zu machen, wähle den Wall-Node (
StaticBody2D
) aus, füge einen ColorRect Node als Child hinzu. Passe die Größe desColorRect
im Inspector unter „Rect“ -> „Size“ an, damit er dieselbe Größe hat wie deine Kollisionsform. Wähle eine Farbe unter „Color“ im Inspector. - Achtung: Hier kommt der frühere Hinweis zum Tragen, dass der Viewport gewöhnungsbedürftig in der Bedienung ist.
- Zum Verschieben wähle „Move“ (W).
- Wenn du die ganze Wall, samt Collision und ColorRect verschieben möchtest, achte darauf, dass im Node Dock Wall ausgewählt ist.
- Zum Ändern der Größe, wähle das Objekt aus, dessen Größe du ändern möchtest und nutze den „Select Mode“ (Q). „Scale“ (S) verhält sich für diesen Fall nicht gut.
- Zum Prüfen: klicke auf ColorRect und prüfe im Inspector, dass die Position 0,0 ist
- Ist das nicht der Fall, befindet sich die Wall – der Parent Node – woanders.
- Setze ihn die Position auf 0,0 und verschiebe die Wall als Ganzes. Das erspart dir später Kopfschmerzen 😄
- Speichere die Scene
world.tscn
(Strg + S).
Schritt 6: Bewegungslogik in _physics_process
verschieben und move_and_slide()
nutzen
Um eine reibungslose Kollisionserkennung zu gewährleisten, ist es wichtig, die Physik- und Bewegungslogik in der Funktion _physics_process(delta)
zu verarbeiten, da diese Funktion mit einer festen Frequenz (Physik-Tick) aufgerufen wird, unabhängig von der Framerate. Die _process(delta)
-Funktion ist eher für visuelle Updates gedacht.
- Öffne dein
player.gd
Script. - Schneide die Bewegungslogik (alles von
var direction = ...
bisposition += ...
) aus der_process(delta)
Funktion aus. - Füge die Funktion
_physics_process(delta):
hinzu (sie ist nicht standardmäßig im Template enthalten). - Füge die Bewegungslogik (von
var direction = Vector2.ZERO
bis zur Zeile vor der altenposition += ...
Zeile) in die_physics_process(delta)
Funktion ein. - Füge am Ende der
_physics_process(delta)
Funktion die zwei wichtigen Zeilen hinzu:velocity = direction * speed
undmove_and_slide()
.
Dein player.gd
Script sollte jetzt so aussehen:
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 27 28 29 30 |
class_name Player extends CharacterBody2D var speed = 200 # Einheit: Pixel pro Sekunde # Called when the node enters the scene tree for the first time. func _ready(): pass # Nichts Besonderes bei Start # Called every frame (für visuelle Updates) func _process(delta): # Rotation bleibt hier rotation_degrees += 45 * delta # Called on physics tick (für Physik und Bewegung mit Kollision) func _physics_process(delta): # Bewegung Input verarbeiten var direction = Vector2.ZERO if Input.is_action_pressed("move_right"): direction.x += 1 if Input.is_action_pressed("move_left"): direction.x -= 1 if Input.is_action_pressed("move_down"): direction.y += 1 if Input.is_action_pressed("move_up"): direction.y -= 1 # Normalisieren bei diagonaler Bewegung if direction.length() > 0: direction = direction.normalized() # Setze den velocity Vektor des CharacterBody2D # velocity ist eine Eigenschaft von CharacterBody2D velocity = direction * speed # KEIN * delta hier! move_and_slide() nutzt delta intern mit velocity # move_and_slide() führt die Bewegung aus und handhabt Kollisionen # Es verschiebt den Body um velocity * delta und stoppt bei Kollisionen move_and_slide() |
Wichtiger Unterschied: velocity
und move_and_slide()
Beachte, dass wir bei der Zuweisung zu velocity
nicht mit delta
multiplizieren (velocity = direction * speed
). Die move_and_slide()
Funktion macht das intern, basierend auf dem festen Physik-Tick. Sie nimmt die velocity
Eigenschaft des CharacterBody2D
und verwendet sie, um die Bewegung pro Frame zu berechnen, unter Berücksichtigung von Kollisionen und der delta
-Zeit des Physik-Ticks.
Schritt 7: Speichern und Ausführen
Nun ist es an der Zeit, unsere Kollisionserkennung in Aktion zu sehen!
- Speichere dein
player.gd
Script (Strg + S). - Stelle sicher, dass die Scene
world.tscn
als Haupt-Scene eingestellt ist. Wenn nicht, drücke F5 und wähle sie aus, oder stelle es in den Project Settings ein (Project
->Project Settings
->Application
->Run
->Main Scene
). - Führe das Projekt aus (F5).
Ergebnis:
Du siehst jetzt das rotierende Godot-Logo, das du mit den Tasten bewegen kannst. Wenn du versuchst, durch die Wand zu laufen, die du gezeichnet hast, stoppt dein Player davor. Die Rotation zeigt auch, dass die Kollisionen sauber an den Schnittkanten der Wall und des Players funktionieren.
Dein nächster Schritt
Puh, du hast einen großen Schritt gemacht! Du hast jetzt die Grundlagen der Kollisionserkennung in Godot verstanden und implementiert. Wir haben gelernt:
- Die verschiedenen Physics Body-Typen und ihre Anwendungsbereiche.
- Die Notwendigkeit von CollisionShape2D Nodes zur Definition der Kollisionsform.
- Das Konzept des CharacterBody2D und die Funktion
move_and_slide()
für vom Spieler:in gesteuerte Bewegung mit Kollisionsbehandlung. - Wie man Szenen ineinander instanziiert, um komplexe Spielwelten aufzubauen.
- Den entscheidenden Unterschied zwischen den Funktionen
_process(delta)
(für visuelle Updates) und_physics_process(delta)
(für Physik und Bewegung).
Als Nächstes werden wir das Signalsystem von Godot detailliert beleuchten. Dieses leistungsstarke System ermöglicht es Nodes, miteinander zu kommunizieren, ohne voneinander wissen zu müssen – ein Schlüsselkonzept für sauberen und erweiterbaren Code in Godot.