Up to date

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

Verwenden von CharacterBody2D/3D

Einführung

Godot bietet mehrere Kollisionsobjekte, die sowohl die Kollisionserkennung als auch die Reaktion darauf ermöglichen. Der Versuch zu entscheiden, welches für Ihr Projekt verwendet werden soll, kann verwirrend sein. Sie können Probleme vermeiden und die Entwicklung vereinfachen, wenn Sie verstehen, wie jedes dieser Objekte funktioniert und welche Vor- und Nachteile sie haben. In diesem Tutorial schauen wir uns den Node CharacterBody2D an und zeigen einige Beispiele, wie man ihn verwenden kann.

Bemerkung

Während dieses Dokument CharacterBody2D in seinen Beispielen verwendet, gelten die gleichen Konzepte auch in 3D.

Was ist ein Character Body?

CharacterBody2D ist für die Implementierung von Körpern gedacht, die über Code gesteuert werden. Character Bodys erkennen Kollisionen mit anderen Bodys, wenn sie sich bewegen, sind aber nicht von den Physik-Eigenschaften der Engine, wie Schwerkraft oder Reibung, betroffen. Dies bedeutet zwar, dass Sie etwas Code schreiben müssen, um ihr Verhalten zu erzeugen, aber es bedeutet auch, dass Sie eine genauere Kontrolle darüber haben, wie sie sich bewegen und reagieren.

Bemerkung

In diesem Dokument wird davon ausgegangen, dass Sie mit den verschiedenen Physik-Bodys von Godot vertraut sind. Bitte lesen Sie zuerst Einführung in die Physik, um einen Überblick über die Physikoptionen zu erhalten.

Tipp

Ein CharacterBody2D kann von der Schwerkraft und anderen Kräften beeinflusst werden, aber Sie müssen die Bewegung im Code berechnen. Die Physik-Engine wird einen CharacterBody2D nicht bewegen.

Bewegung und Kollision

Wenn Sie einen CharacterBody2D bewegen, sollten Sie seine position-Property 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 erkennen Kollisionen.

Warnung

Sie sollten die Bewegung des Physik-Bodys in dem _physics_process()-Callback behandeln.

Die beiden Bewegungsmethoden dienen unterschiedlichen Zwecken. Später in diesem Tutorial sehen Sie Beispiele für deren Funktionsweise.

move_and_collide

Diese Methode benötigt einen Parameter: einen Vector2, der die relative Bewegung des Körpers angibt. Typischerweise ist dies der Geschwindigkeitsvektor, multipliziert mit dem Zeitschritt des Frames (delta). Wenn die Engine irgendwo entlang dieses Vektors eine Kollision feststellt, stoppt der Body sofort seine Bewegung. Wenn dies geschieht, gibt die Methode ein KinematicCollision2D-Objekt zurück.

KinematicCollision2D ist ein Objekt, das Daten über die Kollision und das kollidierende Objekt enthält. Mit diesen Daten können Sie Ihre Kollisionsreaktion berechnen.

Die Funktion move_and_collide ist am nützlichsten, wenn Sie den Body nur bewegen und eine Kollision erkennen wollen, aber keine automatische Reaktion auf eine Kollision benötigen. Wenn Sie zum Beispiel eine Kugel brauchen, die von einer Wand abprallt, können Sie direkt den Winkel der Geschwindigkeit ändern, wenn Sie eine Kollision erkennen. Siehe unten für ein Beispiel.

move_and_slide

Die Methode move_and_slide() ist dazu gedacht, das Kollisionsverhalten in dem häufigen Fall zu vereinfachen, dass ein Body an einem anderen entlang gleiten soll. Sie ist zum Beispiel in Jump'n'Run- oder Top-Down-Spielen besonders nützlich.

Beim Aufruf von move_and_slide() verwendet die Funktion eine Reihe von Node-Propertys, um das Gleitverhalten zu berechnen. Diese Propertys können im Inspektor gefunden oder im Code gesetzt werden.

  • velocity - default value: Vector2( 0, 0 )

    Diese Property stellt den Geschwindigkeitsvektor des Bodys in Pixeln pro Sekunde dar. move_and_slide() ändert diesen Wert bei Kollisionen automatisch.

  • motion_mode - default value: MOTION_MODE_GROUNDED

    Diese Property wird typischerweise verwendet, um zwischen Side-Scrolling und Top-Down-Bewegung zu unterscheiden. Wenn Sie den Default-Wert verwenden, können Sie die Methoden is_on_floor(), is_on_wall() und is_on_ceiling() verwenden, um zu erkennen, mit welcher Art von Oberfläche der Body in Kontakt ist, zudem interagiert der Body mit Schrägen. Wenn MOTION_MODE_FLOATING verwendet wird, werden alle Kollisionen als "Wände" betrachtet.

  • up_direction - default value: Vector2( 0, -1 )

    Mit dieser Property können Sie festlegen, welche Oberflächen die Engine als Boden betrachten soll. Mit ihrem Wert können Sie die Methoden is_on_floor(), is_on_wall() und is_on_ceiling() verwenden, um zu erkennen, mit welcher Art von Oberfläche der Body in Kontakt ist. Der Default-Wert bedeutet, dass die Oberseite von horizontalen Flächen als "Boden" betrachtet wird.

  • floor_stop_on_slope - default value: true

    Dieser Parameter verhindert, dass ein Body im Stillstand an Schrägen herunterrutscht.

  • wall_min_slide_angle - default value: 0.261799 (in Bogenmaß, entspricht 15 Grad)

    Dies ist der Mindestwinkel, bei dem der Body beim Auftreffen auf einer Schräge gleiten darf.

  • floor_max_angle - default value: 0.785398 (in Bogenmaß, entspricht 45 Grad)

    Dieser Parameter ist der maximale Winkel, bevor eine Oberfläche nicht mehr als "Boden" betrachtet wird.

Es gibt viele andere Propertys, die verwendet werden können, um das Verhalten des Bodys unter bestimmten Umständen zu ändern. Siehe die CharacterBody2D-Dokumente für alle Details.

Erkennen von Kollisionen

Bei Verwendung von move_and_collide() gibt die Funktion direkt eine KinematicCollision2D zurück, die Sie in Ihrem Code verwenden können.

Bei der Verwendung von move_and_slide() ist es möglich, daß mehrere Kollisionen auftreten, während die Slide-Reaktion berechnet wird. Um diese Kollisionen zu verarbeiten, verwenden Sie get_slide_collision_count() und get_slide_collision():

# Using move_and_collide.
var collision = move_and_collide(velocity * delta)
if collision:
    print("I collided with ", collision.get_collider().name)

# Using move_and_slide.
move_and_slide()
for i in get_slide_collision_count():
    var collision = get_slide_collision(i)
    print("I collided with ", collision.get_collider().name)

Bemerkung

Die Funktion get_slide_collision_count() zählt nur, wenn der Body kollidiert ist und die Richtung geändert hat.

Siehe KinematicCollision2D für Details zu den zurückgegebenen Kollisionsdaten.

Welche Bewegungsmethode ist zu verwenden?

Eine häufige Frage von neuen Godot-Nutzern lautet: "Wie entscheidet man, welche Bewegungsfunktion man verwenden soll?" Oft ist die Antwort, move_and_slide() zu benutzen, weil es einfacher erscheint, aber das ist nicht unbedingt der Fall. Man kann sich das so vorstellen, daß move_and_slide() ein Spezialfall ist, und move_and_collide() allgemeiner ist. Zum Beispiel führen die folgenden zwei Codeschnipsel zu der gleichen Kollisionsreaktion:

../../_images/k2d_compare.gif
# using move_and_collide
var collision = move_and_collide(velocity * delta)
if collision:
    velocity = velocity.slide(collision.get_normal())

# using move_and_slide
move_and_slide()

Alles was Sie mit move_and_slide() tun, kann auch mit move_and_collide() ausgeführt werden, es kann jedoch etwas mehr Code erforderlich sein. Wie wir in den folgenden Beispielen sehen werden, gibt es Fälle, in denen move_and_slide() nicht die gewünschte Reaktion liefert.

In dem obigen Beispiel ändert move_and_slide() automatisch die Variable velocity. Das liegt daran, dass die Funktion die Geschwindigkeit intern neu berechnet, wenn der Charakter mit der Umgebung kollidiert, um die Verlangsamung widerzuspiegeln.

Wenn Ihre Figur zum Beispiel auf den Boden fällt, soll sie nicht durch die Schwerkraft an vertikaler Geschwindigkeit zunehmen. Stattdessen soll die vertikale Geschwindigkeit auf Null zurückgesetzt werden.

Die Funktion move_and_slide() kann die Geschwindigkeit des Kinematic Bodys auch mehrmals in einer Schleife neu berechnen, da sie den Charakter bewegt und standardmäßig bis zu fünf Mal kollidiert, um eine flüssige Bewegung zu erzeugen. Am Ende des Prozesses steht die neue Geschwindigkeit des Charakters für die Verwendung im nächsten Frame zur Verfügung.

Beispiele

Um diese Beispiele in Aktion zu sehen, laden Sie das Beispielprojekt herunter: character_body_2d_starter.zip

Bewegung und Wände

Wenn Sie das Beispielprojekt heruntergeladen haben, befindet sich dieses Beispiel in "basic_movement.tscn".

Für dieses Beispiel fügen Sie ein CharacterBody2D mit zwei Child-Nodes hinzu: ein Sprite2D und ein CollisionShape2D. Benutzen Sie die Godot "icon.svg" als Textur des Sprite2Ds (ziehen Sie sie aus dem Dateisystem-Dock auf die Textur-Property des Sprite2D). In der Shape-Property des CollisionShape2D wählen Sie "Neu: RectangleShape2D" und vergrößern das Rechteck so, daß es über das Sprite-Bild passt.

Bemerkung

Siehe Übersicht 2D-Bewegung für Beispiele zur Implementierung von 2D-Bewegungsschemas.

Hängen Sie ein Skript an den CharacterBody2D an und fügen Sie den folgenden Code hinzu:

extends CharacterBody2D

var speed = 300

func get_input():
    var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    velocity = input_dir * speed

func _physics_process(delta):
    get_input()
    move_and_collide(velocity * delta)

Starten Sie diese Szene und Sie werden sehen, dass move_and_collide() wie erwartet funktioniert und den Body entlang des Geschwindigkeitsvektors bewegt. Lassen Sie uns nun sehen, was passiert, wenn Sie einige Hindernisse hinzufügen. Fügen Sie einen StaticBody2D mit einer rechteckigen Collision Shape hinzu. Für die Sichtbarkeit können Sie ein Sprite2D oder ein Polygon2D verwenden oder die Option "Sichtbare Collision Shapes" im Menü "Debug" aktivieren.

Starten Sie die Szene erneut und versuchen Sie, sich auf das Hindernis zuzubewegen. Sie werden sehen, dass der CharacterBody2D das Hindernis nicht durchdringen kann. Versuchen Sie jedoch, sich in einem Winkel auf das Hindernis zuzubewegen und Sie werden feststellen, dass sich das Hindernis wie Klebstoff verhält - es fühlt sich an, als ob der Body stecken bleibt.

Dies geschieht, weil es keine Kollisionreaktion gibt. move_and_collide() stoppt die Bewegung des Bodys, wenn eine Kollision auftritt. Wir müssen jede gewünschte Reaktion auf die Kollision programmieren.

Versuchen Sie, die Funktion in move_and_slide() zu ändern und erneut auszuführen.

move_and_slide() bietet eine Default-Kollisionsreaktion, die den Body entlang des Kollisionsobjekts verschiebt. Dies ist nützlich für sehr viele Typen von Spielen und kann schon alles sein, was Sie benötigen, um das gewünschte Verhalten zu erreichen.

Abprallen/Reflektieren

Was ist, wenn Sie keine gleitende Kollisionsreaktion wünschen? In diesem Beispiel ("bounce_and_collide.tscn" im Beispielprojekt) haben wir einen Charakter, der Kugeln abschießt, und wir wollen, dass die Kugeln von den Wänden abprallen.

In diesem Beispiel werden drei Szenen verwendet. Die Hauptszene enthält den Spieler und die Wände. Die Geschosse und die Wand sind separate Szenen, damit sie instanziiert werden können.

Der Spieler wird mit den Tasten w und s für vorwärts und rückwärts gesteuert. Das Zielen erfolgt mit dem Mauszeiger. Hier ist der Code für den Spieler, unter Verwendung von move_and_slide():

extends CharacterBody2D

var Bullet = preload("res://bullet.tscn")
var speed = 200

func get_input():
    # Add these actions in Project Settings -> Input Map.
    var input_dir = Input.get_axis("backward", "forward")
    velocity = transform.x * input_dir * speed
    if Input.is_action_just_pressed("shoot"):
        shoot()

func shoot():
    # "Muzzle" is a Marker2D placed at the barrel of the gun.
    var b = Bullet.instantiate()
    b.start($Muzzle.global_position, rotation)
    get_tree().root.add_child(b)

func _physics_process(delta):
    get_input()
    var dir = get_global_mouse_position() - global_position
    # Don't move if too close to the mouse pointer.
    if dir.length() > 5:
        rotation = dir.angle()
        move_and_slide()

Und der Code für das Geschoss:

extends CharacterBody2D

var speed = 750

func start(_position, _direction):
    rotation = _direction
    position = _position
    velocity = Vector2(speed, 0).rotated(rotation)

func _physics_process(delta):
    var collision = move_and_collide(velocity * delta)
    if collision:
        velocity = velocity.bounce(collision.get_normal())
        if collision.get_collider().has_method("hit"):
            collision.get_collider().hit()

func _on_VisibilityNotifier2D_screen_exited():
    # Deletes the bullet when it exits the screen.
    queue_free()

Die Aktion geschieht in _physics_process(). Nach der Verwendung von move_and_collide() wird, falls eine Kollision auftritt, ein KinematicCollision2D-Objekt zurückgegeben (andernfalls ist die Rückgabe null).

Wenn es eine zurückgegebene Kollision gibt, verwenden wir die Normale der Kollision, um die Geschwindigkeit velocity des Geschosses mit der Vector2.bounce()-Methode zu reflektieren.

Wenn das kollidierende Objekt (collider) eine hit-Methode hat, rufen wir sie auf. Im Beispielprojekt haben wir der Wand einen blinkenden Farbeffekt hinzugefügt, um dies zu demonstrieren.

../../_images/k2d_bullet_bounce.gif

Plattformer-Bewegungen

Versuchen wir es mit einem weiteren populären Beispiel: dem 2D-Plattformer. Die Funktion move_and_slide() ist ideal, um schnell einen funktionierenden Charakter-Controller zum Laufen zu bringen. Wenn Sie das Beispielprojekt heruntergeladen haben, finden Sie dies in "platformer.tscn".

In diesem Beispiel gehen wir davon aus, dass Sie ein Level haben, das aus einem oder mehreren StaticBody2D-Objekten besteht. Sie können jede Form und Größe haben. Im Beispielprojekt verwenden wir Polygon2D, um die Plattformformen zu erstellen.

Hier ist der Code für den Spieler-Body:

extends CharacterBody2D

var speed = 300.0
var jump_speed = -400.0

# Get the gravity from the project settings so you can sync with rigid body nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")


func _physics_process(delta):
    # Add the gravity.
    velocity.y += gravity * delta

    # Handle Jump.
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = jump_speed

    # Get the input direction.
    var direction = Input.get_axis("ui_left", "ui_right")
    velocity.x = direction * speed

    move_and_slide()
../../_images/k2d_platform.gif

In diesem Code benutzen wir move_and_slide() wie oben beschrieben - um den Body entlang seines Geschwindigkeitsvektors zu bewegen und entlang von Kollisionsflächen, wie dem Boden oder einer Plattform, zu gleiten. Wir benutzen auch is_on_floor(), um zu prüfen, ob ein Sprung erlaubt sein sollte. Ohne diese Funktion könnte man in der Luft "springen"; das ist toll, wenn man Flappy Bird macht, aber nicht für ein Jump'n'Run-Spiel.

Es gibt noch viel mehr, was zu einem vollständigen Plattformer-Charakter gehört: Beschleunigung, Doppelsprünge, Coyote-Time und vieles mehr. Der obige Code ist nur ein Startpunkt. Sie können ihn als Basis verwenden, um das Bewegungsverhalten zu erweitern, das Sie für Ihre eigenen Projekte benötigen.