Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Einführung in die Physik

In der Spielentwicklung muss man oft wissen, ob sich zwei Objekte im Spiel berühren oder überlagern. Dies wird als Kollisionserkennung bezeichnet. Falls es zu solch einer Kollision kommt, soll normalerweise etwas passieren. Dies wird als Kollisionsreaktion bezeichnet.

Godot bietet eine Reihe von Kollisionsobjekten in 2D und 3D, um sowohl die Kollisionserkennung als auch die Reaktion darauf zu ermöglichen. Die Entscheidung, welches davon für Ihr Projekt verwendet werden soll, kann verwirrend sein. Sie können Probleme vermeiden und die Entwicklung vereinfachen, wenn Sie verstehen, wie jedes Objekt funktioniert und welche Vor- und Nachteile es hat.

In dieser Anleitung lernen Sie:

  • Godots vier Arten von Kollisionsobjekten

  • Wie jedes Kollisionsobjekt funktioniert

  • Wann und warum man sich für einen Typ entscheidet

Bemerkung

In den Beispielen dieses Dokuments werden 2D-Objekte verwendet. Jedes 2D-Physikobjekt und jede Collision Shape hat ein direktes Äquivalent in 3D und funktioniert in den meisten Fällen auf die gleiche Weise.

Kollisionsobjekte

Godot bietet vier Arten von Kollisionsobjekten, die alle CollisionObject2D erweitern. Die letzten drei unten aufgeführten sind Physik-Bodys und erweitern zusätzlich PhysicsBody2D.

  • Area2D

    Area2D-Nodes bieten Erkennung und Einfluss. Sie können erkennen, wenn sich Objekte überlappen, und Signale ausgeben, wenn Bodys den Bereich betreten oder verlassen. Eine Area2D kann auch verwendet werden, um physikalische Eigenschaften wie Schwerkraft oder Dämpfung in einem definierten Bereich zu umgehen.

  • StaticBody2D

    Ein Static Body wird nicht von der Physik-Engine bewegt. Es nimmt an der Kollisionserkennung teil, bewegt sich jedoch nicht als Reaktion auf die Kollision. Sie werden am häufigsten für Objekte verwendet, die Teil der Umgebung sind oder kein dynamisches Verhalten benötigen.

  • RigidBody2D

    Dies ist der Node, der simulierte 2D-Physik implementiert. Sie steuern einen RigidBody2D nicht direkt, sondern üben stattdessen Kräfte auf ihn aus (Schwerkraft, Impulse usw.) und die Physik-Engine berechnet die resultierende Bewegung. Lesen Sie mehr über die Verwendung von Rigid Bodys

  • CharacterBody2D

    Ein Body, der Kollisionserkennung bietet, aber keine Physik. Alle Bewegungs- und Kollisionsreaktionen müssen im Code implementiert sein.

Physikmaterial

Static Bodys und Rigid Bodys können so konfiguriert werden, dass sie ein PhysicsMaterial verwenden. Dies erlaubt es, die Reibung und das Abprallen eines Objekts einzustellen und festzulegen, ob es absorbierend und/oder rau ist.

Collision Shapes

Ein Physik-Body kann eine beliebige Anzahl von Shape2D-Objekten als Child-Objekte enthalten. Diese Shapes werden verwendet, um die Kollisionsgrenzen des Objekts zu definieren und den Kontakt mit anderen Objekten zu erkennen.

Bemerkung

Um Kollisionen zu erkennen, muss dem Objekt mindestens eine Shape2D zugewiesen werden.

Die häufigste Methode zum Zuweisen einer Shape ist das Hinzufügen von CollisionShape2D oder CollisionPolygon2D als Child des Objekt. Mit diesen Nodes können Sie die Shape direkt im Editor-Arbeitsbereich zeichnen.

Wichtig

Achten Sie darauf, Ihre Collision Shapes niemals im Editor zu skalieren. Die Eigenschaft "Skalierung" im Inspektor sollte (1, 1) bleiben. Wenn Sie die Größe der Collision Shape ändern, sollten Sie immer die Größenregler verwenden und nicht die Node2D-Skalenregler. Das Skalieren einer Shape kann zu unerwartetem Kollisionsverhalten führen.

../../_images/player_coll_shape.png

Physikprozess-Callback

Die Physik-Engine läuft mit einer festen Rate (standardmäßig 60 Iterationen pro Sekunde). Diese Rate unterscheidet sich in der Regel von der Framerate, die je nach gerendertem Inhalt und verfügbaren Ressourcen schwankt.

Es ist wichtig, dass der gesamte physikbezogene Code mit dieser festen Rate läuft. Deshalb unterscheidet Godot zwischen Physik- und Leerlaufverarbeitung. Code, der bei jedem Frame läuft, wird Leerlaufverarbeitung genannt und Code, der bei jedem Physik-Tick läuft, wird Physik-Verarbeitung genannt. Godot bietet zwei verschiedene Callbacks, einen für jede dieser Verarbeitungsraten.

Der Physik-Callback, Node._physics_process(), wird vor jedem Physikschritt aufgerufen. Jeder Code, der auf die Propertys eines Körpers zugreifen muss, sollte hier ausgeführt werden. Dieser Methode wird ein Parameter delta übergeben, der eine Float-Zahl ist, die der Zeit in Sekunden seit dem letzten Schritt entspricht. Wenn man die Default-Aktualisierungsrate der Physik von 60 Hz verwendet, wird sie normalerweise gleich 0.01666... sein (aber nicht immer, siehe unten).

Bemerkung

Es wird empfohlen immer den Parameter delta zu verwenden wenn dies für Ihre Physikberechnungen relevant ist, damit sich das Spiel korrekt verhält wenn Sie die Aktualisierungsrate der Physik ändern oder wenn das Gerät des Spielers nicht mithalten kann.

Kollisionsebenen und -masken

Eine der leistungsfähigsten, aber häufig missverstandenen Kollisionsfunktionen ist das System der Kollisionsebenen. Dieses System ermöglicht es Ihnen, komplexe Interaktionen zwischen einer Vielzahl von Objekten aufzubauen. Die Schlüsselkonzepte sind Ebenen und Masken. Jedes CollisionObject2D hat 32 verschiedene Physik-Ebenen, mit denen es interagieren kann.

Schauen wir uns nacheinander die einzelnen Propertys an:

  • collision_layer

    Dies beschreibt die Ebenen, in denen das Objekt erscheint. Standardmäßig sind alle Objekte auf Ebene 1.

  • collision_mask

    Dies beschreibt, welche Ebenen der Body auf Kollisionen prüfen wird. Wenn sich ein Objekt nicht in einer der Maskenebenen befindet, wird es vom Body ignoriert. Standardmäßig scannen alle Bodys die Ebene 1.

Diese Eigenschaften können per Code oder im Inspektor konfiguriert werden.

Es kann schwierig sein den Überblick darüber zu behalten, wofür Sie die einzelnen Ebenen verwenden. Daher ist es möglicherweise hilfreich, den von Ihnen verwendeten Ebenen Namen zuzuweisen. Namen können unter Projekteinstellungen -> Ebenen-Namen vergeben werden.

../../_images/physics_layer_names.png

GUI-Beispiel

Sie haben vier Node-Typen in Ihrem Spiel: Wände, Spieler, Gegner und Münze. Sowohl der Spieler als auch der Gegner sollten mit Wänden kollidieren können. Der Spieler-Node sollte Kollisionen mit dem Gegner und der Münze erkennen, aber der Gegner und die Münze sollten sich gegenseitig ignorieren.

Benennen Sie zunächst die Ebenen 1 bis 4 "walls", "player", "enemies" und "coins" und platzieren Sie jeden Node-Typ mithilfe der Property "Ebene" in der jeweiligen Ebene. Legen Sie dann die "Masken"-Property jedes Nodes fest, indem Sie die Ebenen auswählen, mit denen er interagieren soll. Die Einstellungen des Spielers sehen beispielsweise folgendermaßen aus:

../../_images/player_collision_layers.png ../../_images/player_collision_mask.png

Code-Beispiel

In Funktionsaufrufen werden die Ebenen als Bitmaske angegeben. Wenn eine Funktion standardmäßig alle Ebenen aktiviert, wird die Ebenenmaske als 0xffffffff angegeben. Ihr Code kann je nach Vorliebe binäre, hexadezimale oder dezimale Notation für Ebenenmasken verwenden.

Das Code-Äquivalent des obigen Beispiels, in dem die Ebenen 1, 3 und 4 aktiviert wurden, lautet wie folgt:

# Example: Setting mask value for enabling layers 1, 3 and 4

# Binary - set the bit corresponding to the layers you want to enable (1, 3, and 4) to 1, set all other bits to 0.
# Note: Layer 32 is the first bit, layer 1 is the last. The mask for layers 4,3 and 1 is therefore
0b00000000_00000000_00000000_00001101
# (This can be shortened to 0b1101)

# Hexadecimal equivalent (1101 binary converted to hexadecimal)
0x000d
# (This value can be shortened to 0xd)

# Decimal - Add the results of 2 to the power of (layer to be enabled - 1).
# (2^(1-1)) + (2^(3-1)) + (2^(4-1)) = 1 + 4 + 8 = 13
pow(2, 1-1) + pow(2, 3-1) + pow(2, 4-1)

Area2D

Area-Nodes bieten Erkennung und Einfluss. Sie können erkennen wenn sich Objekte überlappen und Signale aussenden wenn Bodys den Bereich betreten oder verlassen. Bereiche können auch verwendet werden, um Physik-Eigenschaften wie Schwerkraft oder Dämpfung in einem definierten Bereich zu umgehen.

Es gibt drei Hauptanwendungen für Area2D:

  • Überschreiben von Physik-Parametern (wie z.B. der Schwerkraft) in einer bestimmten Region.

  • Erkennen, wann andere Bodys eine Region betreten oder verlassen oder welche Bodys sich derzeit in einer Region befinden.

  • Überprüfen anderer Bereiche auf Überlappung.

Standardmäßig empfangen Bereiche auch Maus- und Touchscreen-Eingaben.

StaticBody2D

Ein Static Body wird von der Physik-Engine nicht bewegt. Es nimmt an der Kollisionserkennung teil, bewegt sich jedoch nicht als Reaktion auf die Kollision. Er kann jedoch einem kollidierenden Körper Bewegung oder Rotation verleihen als ob er sich bewegen würde, und zwar unter Verwendung seiner Propertys constant_linear_velocity und constant_angular_velocity (konstante lineare Geschwindigkeit und konstante Winkelgeschwindigkeit).

`` StaticBody2D``-Nodes werden am häufigsten für Objekte verwendet, die Teil der Umgebung sind oder kein dynamisches Verhalten benötigen.

Beispiele für StaticBody2D:

  • Plattformen (einschließlich beweglicher Plattformen)

  • Förderbänder

  • Wände und andere Hindernisse

RigidBody2D

Dies ist der Node, der simulierte 2D-Physik implementiert. Sie steuern ein RigidBody2D nicht direkt. Stattdessen üben Sie Kräfte darauf aus, und die Physik-Engine berechnet die resultierende Bewegung, einschließlich Kollisionen mit anderen Körpern, und Kollisionsreaktionen wie Abprallen, Rotation etc.

Sie können das Verhalten eines Rigid Bodys über Eigenschaften wie "Masse", "Reibung" oder "Elastizität" ändern, die im Inspektor eingestellt werden können.

Das Verhalten des Bodys wird auch von den Eigenschaften der Welt beeinflusst, wie unter Projekteinstellungen -> Physik festgelegt ist, oder durch Eingabe eines Area2D, das die globalen Physik-Eigenschaften überschreibt.

Wenn ein Rigid Body in Ruhe ist und sich eine Weile nicht bewegt hat, legt er sich schlafen. Ein schlafender Body wirkt wie ein Static Body und seine Kräfte werden nicht von der Physik-Engine berechnet. Der Körper wacht auf, wenn Kräfte angendet werden, entweder durch eine Kollision oder per Code.

Nutzung von RigidBody2D

Einer der Vorteile der Verwendung eines Rigid Bodys besteht darin, dass viele Verhaltensweisen "kostenlos" ausgeführt werden können, ohne dass Code geschrieben werden muss. Wenn Sie beispielsweise ein Spiel im "Angry Birds"-Stil mit fallenden Blöcken erstellen, müssen Sie nur RigidBody2Ds erstellen und deren Eigenschaften anpassen. Das Stapeln, Fallen und Abprallen wird automatisch von der Physik-Engine berechnet.

Wenn Sie jedoch eine gewisse Kontrolle über den Body haben wollen, sollten Sie vorsichtig sein - das Ändern der position, linear_velocity oder anderer Physik-Propertys eines Rigid Bodys kann zu unerwartetem Verhalten führen. Wenn Sie eine der physikbezogenen Propertys ändern müssen, sollten Sie den _integrate_forces()-Callback anstelle von _physics_process() verwenden. In diesem Callback hat man Zugriff auf den PhysicsDirectBodyState2D des Bodys, was eine sichere Änderung von Propertys und deren Synchronisation mit der Physik-Engine ermöglicht.

Hier ist zum Beispiel der Code für ein Raumschiff im "Asteroids"-Stil:

extends RigidBody2D

var thrust = Vector2(0, -250)
var torque = 20000

func _integrate_forces(state):
    if Input.is_action_pressed("ui_up"):
        state.apply_force(thrust.rotated(rotation))
    else:
        state.apply_force(Vector2())
    var rotation_direction = 0
    if Input.is_action_pressed("ui_right"):
        rotation_direction += 1
    if Input.is_action_pressed("ui_left"):
        rotation_direction -= 1
    state.apply_torque(rotation_direction * torque)

Beachten Sie, dass wir die Propertys linear_velocity oder angular_velocity nicht direkt einstellen, sondern Kräfte (thrust und torque - "Schub" und "Drehmoment") auf den Körper ausüben, und die Physik-Engine die resultierende Bewegung berechnet .

Bemerkung

Wenn sich ein Rigid Body schlafen legt, wird die Funktion _integrate_forces() nicht aufgerufen. Um dieses Verhalten zu überschreiben, müssen Sie den Körper wach halten, indem Sie eine Kollision erstellen, eine Kraft auf ihn ausüben oder die Property can_sleep deaktivieren. Beachten Sie, dass sich dies negativ auf die Performance auswirken kann.

Kontakt-Meldungen

Standardmäßig behalten Rigid Bodys keine Kontakte im Auge, da dies bei vielen Bodys in der Szene sehr viel Speicherplatz beanspruchen kann. Um die Kontakt-Meldungen zu aktivieren, setzen Sie die Property max_contacts_reported auf einen Wert ungleich Null. Die Kontakte können dann über PhysicsDirectBodyState2D.get_contact_count() und verwandte Funktionen ermittelt werden.

Die Kontaktüberwachung über Signale kann über die Property contact_monitor aktiviert werden. Eine Liste der verfügbaren Signale finden Sie unter RigidBody2D.

CharacterBody2D

CharacterBody2D-Bodys erkennen Kollisionen mit anderen Bodys, werden aber nicht von physikalischen Eigenschaften wie Schwerkraft oder Reibung beeinflusst. Stattdessen müssen sie vom Benutzer durch Code gesteuert werden. Die Physik-Engine wird einen Character Body nicht bewegen.

Wenn man einen Character Body bewegt, sollte man seine position nicht direkt setzen. Stattdessen benutzen Sie die Methoden move_and_collide() oder move_and_slide(). Diese Methoden bewegen den Body entlang eines gegebenen Vektors, und er hält sofort an, wenn eine Kollision mit einem anderen Body festgestellt wird. Nachdem der Body kollidiert ist, muss jede Kollisionsreaktion manuell kodiert werden.

Character-Kollisions-Reaktion

Nach einer Kollision möchten Sie vielleicht, dass der Body abprallt, an einer Wand entlang gleitet oder die Eigenschaften des getroffenen Objekts ändert. Die Art und Weise, wie Sie die Kollisionsreaktion behandeln, hängt davon ab, welche Methode Sie zum Bewegen des CharacterBody2D verwendet haben.

move_and_collide

Bei Verwendung von move_and_collide() gibt die KinematicCollision2D-Funktion ein Objekt zurück, das Informationen über die Kollision und den kollidierenden Body enthält. Sie können diese Informationen verwenden, um die Reaktion zu bestimmen.

Wenn Sie beispielsweise den Punkt im Raum suchen möchten, an dem die Kollision aufgetreten ist:

extends PhysicsBody2D

var velocity = Vector2(250, 250)

func _physics_process(delta):
    var collision_info = move_and_collide(velocity * delta)
    if collision_info:
        var collision_point = collision_info.get_position()

Oder um vom kollidierenden Objekt abzuprallen:

extends PhysicsBody2D

var velocity = Vector2(250, 250)

func _physics_process(delta):
    var collision_info = move_and_collide(velocity * delta)
    if collision_info:
        velocity = velocity.bounce(collision_info.get_normal())

move_and_slide

Gleiten ist eine häufige Kollisionsreaktion. Stellen Sie sich einen Spieler vor, der sich in einem Top-Down-Spiel an Wänden entlang bewegt oder in einem Plattformer an Schrägen auf und ab läuft. Während es möglich ist, diese Reaktion nach der Verwendung von move_and_collide() selbst zu programmieren, bietet move_and_slide() eine bequeme Möglichkeit, eine Gleitbewegung zu implementieren, ohne viel Code zu schreiben.

Warnung

move_and_slide() bezieht automatisch den Zeitschritt in seine Berechnung ein, daher sollten Sie den Geschwindigkeitsvektor nicht mit delta multiplizieren.

Verwenden Sie beispielsweise den folgenden Code, um einen Charakter zu erstellen, der über den Boden laufen (einschließlich Schrägen) und auch springen kann, wenn er auf dem Boden steht:

extends CharacterBody2D

var run_speed = 350
var jump_speed = -1000
var gravity = 2500

func get_input():
    velocity.x = 0
    var right = Input.is_action_pressed('ui_right')
    var left = Input.is_action_pressed('ui_left')
    var jump = Input.is_action_just_pressed('ui_select')

    if is_on_floor() and jump:
        velocity.y = jump_speed
    if right:
        velocity.x += run_speed
    if left:
        velocity.x -= run_speed

func _physics_process(delta):
    velocity.y += gravity * delta
    get_input()
    move_and_slide()

Siehe Kinematischer Charakter (2D) für weitere Details zur Verwendung von move_and_slide(), einschließlich eines Demo-Projekts mit detailliertem Code.