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. EineArea2D
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.
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.
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:
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)
using Godot;
public partial class Spaceship : RigidBody2D
{
private Vector2 _thrust = new Vector2(0, -250);
private float _torque = 20000;
public override void _IntegrateForces(PhysicsDirectBodyState2D state)
{
if (Input.IsActionPressed("ui_up"))
state.ApplyForce(_thrust.Rotated(Rotation));
else
state.ApplyForce(new Vector2());
var rotationDir = 0;
if (Input.IsActionPressed("ui_right"))
rotationDir += 1;
if (Input.IsActionPressed("ui_left"))
rotationDir -= 1;
state.ApplyTorque(rotationDir * _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()
using Godot;
public partial class Body : PhysicsBody2D
{
private Vector2 _velocity = new Vector2(250, 250);
public override void _PhysicsProcess(double delta)
{
var collisionInfo = MoveAndCollide(_velocity * (float)delta);
if (collisionInfo != null)
{
var collisionPoint = collisionInfo.GetPosition();
}
}
}
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())
using Godot;
public partial class Body : PhysicsBody2D
{
private Vector2 _velocity = new Vector2(250, 250);
public override void _PhysicsProcess(double delta)
{
var collisionInfo = MoveAndCollide(_velocity * (float)delta);
if (collisionInfo != null)
_velocity = _velocity.Bounce(collisionInfo.GetNormal());
}
}
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()
using Godot;
public partial class Body : CharacterBody2D
{
private float _runSpeed = 350;
private float _jumpSpeed = -1000;
private float _gravity = 2500;
private void GetInput()
{
var velocity = Velocity;
velocity.X = 0;
var right = Input.IsActionPressed("ui_right");
var left = Input.IsActionPressed("ui_left");
var jump = Input.IsActionPressed("ui_select");
if (IsOnFloor() && jump)
velocity.Y = _jumpSpeed;
if (right)
velocity.X += _runSpeed;
if (left)
velocity.X -= _runSpeed;
Velocity = velocity;
}
public override void _PhysicsProcess(double delta)
{
var velocity = Velocity;
velocity.Y += _gravity * (float)delta;
Velocity = velocity;
GetInput();
MoveAndSlide();
}
}
Siehe Kinematischer Charakter (2D) für weitere Details zur Verwendung von move_and_slide()
, einschließlich eines Demo-Projekts mit detailliertem Code.