Physik Einleitung

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

Godot bietet ein ganze Anzahl an Kollisions Objekten in 2D und 3D und unterstützt damit beides, Kollisions-Abfrage und Kollisions-Antwort. Es kann am Anfang verwirrend sein zu entscheiden, welches der beiden zu nutzen ist. Man kann diesem Problemen aus dem Weg gehen und sich die Entwicklung vereinfachen, wenn man erst einmal verstanden hat wie beide funktionieren und wo ihr Vor- und Nachteile sind.

In dieser Anleitung geht es um:

  • Godots vier Arten von Kollisionsobjekten

  • Wie jedes Kollisionsobjekt funktioniert

  • Wann und warum eins dem anderen vorgezogen wird

Bemerkung

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

Kollisionsobjekte

Godot bietet vier Arten von physikalischen Körpern, die CollisionObject2D erweitern:

  • Area2D

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

Die anderen drei Körper erweitern PhysicsBody2D:

  • StaticBody2D

    Ein statischer Körper 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. Read more about using rigid bodies.

  • KinematicBody2D

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

Physik Material

Static bodies and rigid bodies can be configured to use a physics material. This allows adjusting the friction and bounce of an object, and set if it's absorbent and/or rough.

Kollisionsformen

Ein Physikkörper kann eine beliebige Anzahl von Shape2D Objekten als untergeordnete Objekte enthalten. Diese Formen 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 Form ist das Hinzufügen von CollisionShape2D oder CollisionPolygon2D als untergeordnetes Objekt des Objekts. Mit diesen Nodes können Sie die Form direkt im Editor-Arbeitsbereich zeichnen.

Wichtig

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

../../_images/player_coll_shape1.png

Aufruf des Physikprozesses

Die Physik-Engine kann mehrere Threads erzeugen, um die Leistung zu verbessern, sodass sie bis zu einem vollständigen Frame für die Verarbeitung der Physik verwenden kann. Aus diesem Grund ist der Wert der Zustandsvariablen eines Körpers wie position oder linear velocity für den aktuellen Frame möglicherweise nicht genau.

Um diese Ungenauigkeit zu vermeiden, sollte jeder Code, der auf die Eigenschaften eines Körpers zugreifen muss, in der Funktion Node._physics_process() ausgeführt werden, der vor jedem Physikschritt mit einer konstanten Bildrate (standardmäßig 60 mal pro Sekunde) aufgerufen wird. Dieser Methode wird ein delta-Parameter übergeben, bei dem es sich um eine Gleitkommazahl handelt, die der Zeit entspricht, die seit dem letzten Schritt in Sekunden vergangen ist. Wenn Sie die Standard-Aktualisierungsrate für die Physik von 60 Hz verwenden, entspricht diese normalerweise "0,01666 ..." (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

Eines der leistungsstärksten, aber häufig missverstandenen Kollisionsmerkmale ist das Kollisionsschichtsystem. Mit diesem System können Sie komplexe Interaktionen zwischen einer Vielzahl von Objekten aufbauen. Die Schlüsselkonzepte sind Ebenen und Masken. Jedes CollisionObject2D hat 20 verschiedene physikalische Schichten, mit denen es interagieren kann.

Schauen wir uns nacheinander die einzelnen Eigenschaften 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 Schichten der Körper nach Kollisionen scannen wird. Wenn sich ein Objekt nicht in einer der Maskenebenen befindet, wird es vom Körper ignoriert. Standardmäßig scannen alle Körper 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 -> Ebenennamen vergeben werden.

../../_images/physics_layer_names.png

GUI Beispiele

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

Benennen Sie zunächst die Ebenen 1 bis 4 "Wände", "Spieler", "Feinde" und "Münzen" und platzieren Sie jeden Node-Typ mithilfe der Eigenschaft "Ebene" in der jeweiligen Ebene. Legen Sie dann die "Masken" -Eigenschaft 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 Ebenen als Bitmaske angegeben. Wenn eine Funktion standardmäßig alle Ebenen aktiviert, wird die Ebenenmaske als 0x7fffffff angegeben. Ihr Code kann je nach Wunsch für Ebenenmasken Binär-, Hexadezimal- oder Dezimalschreibweise verwenden.

Das Code-Äquivalent des obigen Beispiels, in dem die Schichten 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 20 is the first bit, layer 1 is the last. The mask for layers 4,3 and 1 is therefore
0b00000000000000001101
# (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) + pow(2, 3) + pow(2, 4)

Area2D

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

Es gibt drei Hauptanwendungen für Area2D:

  • Überschreiben physikalischer Parameter (wie z.B. der Schwerkraft) in einer bestimmten Region.

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

  • Überprüfen anderer Bereiche auf Überlappung.

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

StaticBody2D

Ein statischer Körper wird von der Physik-Engine nicht bewegt. Es nimmt an der Kollisionserkennung teil, bewegt sich jedoch nicht als Reaktion auf die Kollision. Es kann jedoch einem kollidierenden Körper Bewegung oder Rotation verleihen als ob er sich bewegen würde, und zwar unter Verwendung seiner Eigenschaften 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 Prellen, Drehen usw.

You can modify a rigid body's behavior via properties such as "Mass", "Friction", or "Bounce", which can be set in the Inspector.

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

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

Rigid Body Modi

Ein starrer Körper kann auf einen von vier Modi eingestellt werden:

  • Rigid - Der Körper verhält sich wie ein physisches Objekt. Es kollidiert mit anderen Körpern und reagiert auf die auf ihn ausgeübten Kräfte. Dies ist der Standardmodus.

  • Static - Der Körper verhält sich wie ein StaticBody2D und bewegt sich nicht.

  • Character - Ähnlich wie im "Rigid" Modus, aber der Körper kann sich nicht drehen.

  • Kinematic - Der Körper verhält sich wie ein KinematicBody2D und muss per Code verschoben werden.

Nutzung von RigidBody2D

Einer der Vorteile der Verwendung eines starren Körpers 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 Hüpfen wird automatisch von der Physik-Engine berechnet.

Wenn Sie jedoch eine gewisse Kontrolle über den Körper haben möchten, sollten Sie vorsichtig sein - das Ändern der Position, der linearen Geschwindigkeit oder anderer physikalischer Eigenschaften eines starren Körpers kann zu unerwartetem Verhalten führen. Wenn Sie eine der physikbezogenen Eigenschaften ändern müssen, sollten Sie _integrate_forces() anstelle von _physics_process() verwenden. In dieser Funktion haben Sie Zugriff auf Physics2DDirectBodyState des Körpers, wodurch Eigenschaften sicher geändert und mit der Physik-Engine synchronisiert werden können.

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"):
        applied_force = thrust.rotated(rotation)
    else:
        applied_force = Vector2()
    var rotation_dir = 0
    if Input.is_action_pressed("ui_right"):
        rotation_dir += 1
    if Input.is_action_pressed("ui_left"):
        rotation_dir -= 1
    applied_torque = rotation_dir * torque

Beachten Sie, dass wir die Eigenschaften 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 ein starrer Körper schlafen geht, 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 Eigenschaft can_sleep deaktivieren. Beachten Sie, dass sich dies negativ auf die Leistung auswirken kann.

Kontakt-Meldungen

Standardmäßig achten starre Körper nicht auf Kontakte, da dies eine große Menge an Speicher erfordern kann, wenn sich viele Körper in der Szene befinden. Um die Kontaktberichterstellung zu aktivieren, setzen Sie die Eigenschaft contact_reported auf einen Wert ungleich Null. Die Kontakte erhält man dann über Physics2DDirectBodyState.get_contact_count() und verwandte Funktionen.

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

KinematicBody2D

KinematicBody2D Körper erkennen Kollisionen mit anderen Körpern, werden jedoch nicht von physikalischen Eigenschaften wie Schwerkraft oder Reibung beeinflusst. Stattdessen müssen sie vom Benutzer per Code gesteuert werden. Die Physik-Engine bewegt keinen kinematischen Körper.

Wenn Sie einen kinematischen Körper bewegen, sollten Sie seine position nicht direkt einstellen. Stattdessen verwenden Sie die Methoden move_and_collide() oder move_and_slide(). Diese Methoden bewegen den Körper entlang eines bestimmten Vektors und er stoppt sofort, wenn eine Kollision mit einem anderen Körper erkannt wird. Nachdem der Körper kollidiert ist, muss jede Kollisionsreaktion manuell codiert werden.

Kinematische Kollisionsreaktion

Nach einer Kollision möchten Sie möglicherweise, dass der Körper abprallt, an einer Wand entlang gleitet oder die Eigenschaften des getroffenen Objekts ändert. Wie Sie mit der Kollisionsreaktion umgehen, hängt davon ab, mit welcher Methode Sie den KinematicBody2D verschoben 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 Körper enthält. Sie können diese Informationen verwenden, um die Antwort zu bestimmen.

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

extends KinematicBody2D

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.position

Oder um vom kollidierenden Objekt abzuprallen:

extends KinematicBody2D

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.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 auf und ab läuft. Während es möglich ist, diese Antwort nach der Verwendung von move_and_collide() selbst zu codieren, 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 Character zu erstellen, der über den Boden laufen (einschließlich Hänge) und auch springen kann, wenn er auf dem Boden steht:

extends KinematicBody2D

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

var velocity = Vector2()

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()
    velocity = move_and_slide(velocity, Vector2(0, -1))

Siehe auch Kinematischer Charakter (2D) for more details on using ``move_and_slide()``für ein Demo Projekt mit ausführlichem Code.