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 Dynamik-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 Character Controller, die Sie finden können (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 würden.

Also, was ist der Unterschied?:

  • Ein dynamischer Character Controller verwendet einen starren Körper mit einem unendlichen Trägheitstensor. Es ist ein starrer Körper, der sich nicht drehen kann. Physik-Engines lassen Objekte immer bewegen und kollidieren und lösen dann all ihre Kollisionen zusammen aus. Auf diese Weise können dynamische Character 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 ausgelö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 Character Controller immer in einem nicht kollidierenden Zustand beginnt und sich immer zu einen nicht kollidierenden Zustand bewegt. Wenn es in einem kollidierenden Zustand beginnt, wird es versuchen, sich wie starre Körper zu befreien, 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.

Diese kurze Anleitung konzentriert sich auf den kinematischen Charakter-Controller. Grundsätzlich die altmodische Art, mit Kollisionen umzugehen (was unter der Haube nicht unbedingt einfacher ist, aber gut versteckt und als nette und einfache API präsentiert).

Physikprozess

Um die Logik eines kinematischen Körpers 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 KinematicBody2D

func _physics_process(delta):
    pass

Eine Szene einrichten

Zum testen, hier eine Szene (aus der TileMap-Anleitung) :: download: kbscene.zip <files/kbscene.zip>. Wir werden eine neue Szene für den Charakter erstellen. Verwenden Sie das Roboter-Sprite und erstellen Sie eine Szene wie folgt:

../../_images/kbscene.png

Sie werden feststellen, dass sich neben unserem CollisionShape2D-Node ein Warnsymbol befindet. Das liegt daran, dass wir keine Form 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.png

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

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

Instanzieren Sie schließlich diese Charakterszene in der TileMap und machen Sie die Kartenszene zur Hauptszene, damit sie beim drücken von Wiedergabe ausgeführt wird.

../../_images/kbinstance.png

Den kinematischen Charakter bewegen

Gehen Sie zurück zur Charakterszene und öffnen das Skript, die Magie beginnt jetzt! Der kinematische Körper führt standardmäßig nichts aus, verfügt jedoch über eine nützliche Funktion namens KinematicBody2D.move_and_collide(). Diese Funktion verwendet ein Vector2 als Argument und versucht diese Bewegung auf den kinematischen Körper anzuwenden. Wenn eine Kollision auftritt, stoppt sie genau zum Zeitpunkt der Kollision.

Bewegen wir also unser Sprite nach unten, bis es auf dem Boden aufschlägt:

extends KinematicBody2D

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 beim Aufprall auf den Boden anhält. Ziemlich cool, oder?

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

extends KinematicBody2D

const GRAVITY = 200.0
var velocity = Vector2()

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 einfache Gehhilfe hinzu, indem Sie nach links und rechts drücken:

extends KinematicBody2D

const GRAVITY = 200.0
const WALK_SPEED = 200

var velocity = Vector2()

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

    # We don't need to multiply velocity by delta because "move_and_slide" already takes delta time into account.

    # The second parameter of "move_and_slide" is the normal pointing up.
    # In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal.
    velocity = move_and_slide(velocity, Vector2(0, -1))

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 verteilten Demo-Zip-Datei oder unter https://github.com/godotengine/godot-demo-projects/tree/master/2d/kinematic_character.