Up to date

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

Kinematischer Charakter (2D)

Einführung

Ja, der Name klingt seltsam: "Kinematischer Charakter". Was ist das? Der Grund für den Namen ist, dass als Physik-Engines herauskamen, sie "Dynamics"-Engines genannt wurden (weil sie sich hauptsächlich mit Kollisionsreaktionen befassten). Es wurden viele Versuche unternommen, mithilfe der Dynamics-Engines einen Charakter-Controller zu erstellen, aber es war nicht so einfach wie es schien. Godot verfügt über eine der besten Implementierungen des dynamischen Charakter-Controllers, die es gibt (wie in dem 2D/Platformer-Beispiel zu sehen ist), aber die Verwendung erfordert ein beträchtliches Maß an Geschick und Verständnis für Physik-Engines (oder viel Geduld beim Ausprobieren).

Einige Physik-Engines wie Havok scheinen auf dynamische Charakter-Controller als beste Option zu schwören, während andere (PhysX) eher die kinematische bevorzugen.

Also, was ist der Unterschied?:

  • Ein dynamischer Charakter-Controller verwendet einen starren Körper mit einem unendlichen Trägheitstensor. Es ist ein starrer Körper, der nicht rotieren kann. Physik-Engines lassen Objekte immer bewegen und kollidieren und lösen dann all ihre Kollisionen zusammen auf. Auf diese Weise können dynamische Charakter-Controller nahtlos mit anderen Physikobjekten interagieren, wie in dem Platformer-Beispiel gezeigt. Diese Wechselwirkungen sind jedoch nicht immer vorhersehbar. Es kann mehr als einen Frame dauern, bis Kollisionen aufgelöst sind. Einige Kollisionen scheinen sich also geringfügig zu verschieben. Diese Probleme können behoben werden, erfordern jedoch ein gewisses Maß an Geschicklichkeit.

  • Es wird angenommen, dass ein kinematischer Charakter-Controller immer in einem nicht kollidierenden Zustand beginnt und sich immer zu einen nicht kollidierenden Zustand bewegt. Wenn er in einem kollidierenden Zustand beginnt, wird er versuchen, sich zu lösen wie es starre Körper tun, aber dies ist die Ausnahme, nicht die Regel. Dies macht ihre Steuerung und Bewegung viel vorhersehbarer und einfacher zu programmieren. Als Nachteil können sie jedoch nicht direkt mit anderen Physikobjekten interagieren, es sei denn, dies erfolgt manuell im Code.

Dieses kurze Tutorial konzentriert sich auf den kinematischen Charakter-Controller. Es verwendet die alte Art der Handhabung von Kollisionen, die unter der Haube nicht unbedingt einfacher ist, aber gut versteckt und als API präsentiert wird.

Physikprozess

Um die Logik eines kinematischen Bodys oder Charakters zu verwalten wird immer empfohlen einen Physikprozess zu verwenden, da dieser vor dem Physikschritt aufgerufen wird und seine Ausführung mit dem Physikserver synchronisiert ist. Außerdem wird er immer gleich oft pro Sekunde aufgerufen. Dadurch funktionieren Physik und Bewegungsberechnung vorhersehbarer als bei normalen Prozessen, die Spitzen aufweisen oder an Genauigkeit verlieren können, wenn die Bildrate zu hoch oder zu niedrig ist.

extends CharacterBody2D

func _physics_process(delta):
    pass

Einrichten einer Szene

Um etwas zum Testen zu haben, hier ist die Szene (aus dem Tilemap-Tutorial): kinematic_character_2d_starter.zip. Wir werden eine neue Szene für den Charakter erstellen. Verwenden Sie das Roboter-Sprite und erstellen Sie eine Szene wie diese:

../../_images/kbscene.webp

Sie werden feststellen, dass sich neben unserem CollisionShape2D-Node ein Warnsymbol befindet. Das liegt daran, dass wir keine Shape dafür definiert haben. Erstellen Sie eine neue CircleShape2D in der Shape-Eigenschaft von CollisionShape2D. Klicken Sie auf <CircleShape2D>, um zu den entsprechenden Optionen zu gelangen, und setzen Sie den Radius auf 30:

../../_images/kbradius.webp

Hinweis: Wie bereits in der Physik-Anleitung erwähnt, kann die Physik-Engine die Skalierung für die meisten Arten von Shapes nicht verarbeiten (nur Kollisionspolygone, Ebenen und Segmente funktionieren). Ändern Sie daher stattdessen immer die Parameter (z.B. den Radius) der Shape anstatt sie zu skalieren. Gleiches gilt auch für die Kinematic/Rigid/Static Body selbst, da ihre Skalierung die Shape-Größe beeinflusst.

Erstellen Sie nun ein Skript für den Charakter. Das oben als Beispiel verwendete Skript sollte als Basis dienen.

Instanziieren Sie schließlich diese Charakterszene in der TileMap und machen Sie die Map-Szene zur Hauptszene, damit sie beim Drücken von Wiedergabe ausgeführt wird.

../../_images/kbinstance.webp

Den kinematischen Charakter bewegen

Gehen Sie zurück zur Charakterszene und öffnen Sie das Skript, und jetzt beginnt die Magie! Kinematic Body macht standardmäßig nichts, aber es hat eine nützliche Funktion namens CharacterBody2D.move_and_collide(). Diese Funktion nimmt einen Vector2 als Argument und versucht, diese Bewegung auf den Kinematic Body anzuwenden. Wenn es zu einer Kollision kommt, wird die Bewegung im Moment der Kollision angehalten.

Bewegen wir also unser Sprite nach unten, bis es den Boden berührt:

extends CharacterBody2D

func _physics_process(delta):
    move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame

Das Ergebnis ist, dass sich der Charakter bewegt, aber Berühren des Bodens anhält. Ziemlich cool, oder?

Der nächste Schritt besteht darin, dem Ganzen Schwerkraft zu verleihen. Auf diese Weise verhält es sich etwas mehr wie ein normaler Spielcharakter:

extends CharacterBody2D

const GRAVITY = 200.0

func _physics_process(delta):
    velocity.y += delta * GRAVITY

    var motion = velocity * delta
    move_and_collide(motion)

Jetzt fällt der Charakter sanft nach unten. Lassen Sie ihn zur Seite gehen, links und rechts, durch drücken der Richtungstasten. Denken Sie daran, dass die verwendeten Werte (zumindest für die Geschwindigkeit) Pixel/Sekunde sind.

Dies fügt eine grundlegende Unterstützung für das Gehen hinzu, wenn man nach links und rechts drückt:

extends CharacterBody2D

const GRAVITY = 200.0
const WALK_SPEED = 200

func _physics_process(delta):
    velocity.y += delta * GRAVITY

    if Input.is_action_pressed("ui_left"):
        velocity.x = -WALK_SPEED
    elif Input.is_action_pressed("ui_right"):
        velocity.x =  WALK_SPEED
    else:
        velocity.x = 0

    # "move_and_slide" already takes delta time into account.
    move_and_slide()

Und probieren Sie es aus.

Dies ist ein guter Ausgangspunkt für einen Plattformer. Eine vollständigeres Beispiel finden Sie in der mit der Engine veröffentlichen Demo-Zip-Datei oder unter https://github.com/godotengine/godot-demo-projects/tree/master/2d/kinematic_character.