Den Spieler mit Code Bewegen

Es ist endlich Zeit zu programmieren! Wir werden die Eingabeaktionen, die wir im letzten Teil erstellt haben verwenden, um unseren Charakter zu bewegen.

Klicken sie mit der Rechten Maustaste auf das Player Node und wählen sie Skript hinzufügen, um ein neues Skript hinzuzufügen. Legen sie in dem Popup die Vorlage als Empty fest, bevor sie den Erstellen Knopf drücken.

image0

Fangen wir mit den Eigenschaften der Klasse an. Wir werden eine Geschwindigkeit, eine Fallbeschleunigung die gravitation repräsentiert und einen Bewegungs-Vektor (velocity) um unseren Charakter zu bewegen definieren.

extends KinematicBody

# How fast the player moves in meters per second.
export var speed = 14
# The downward acceleration when in the air, in meters per second squared.
export var fall_acceleration = 75

var velocity = Vector3.ZERO

Dies sind allgemeine Eigenschaften für einen sich bewegenden Körper. Die velocity ist ein 3D-Vektor, der eine Geschwindigkeit mit einer Richtung kombiniert. Hier definieren wir ihn als Eigenschaft, weil wir seinen Wert über mehrere Frames hinweg aktualisieren und wiederverwenden wollen.

Bemerkung

Diese Werte unterscheiden sich ziemlich von 2D-Code, weil Entfernungen in Metern angegeben werden. Während in 2D ein Tausend Einheiten (Pixel) vielleicht nur der halben Breite Ihres Bildschirms entsprechen, ist es in 3D ein Kilometer.

Lassen Sie uns jetzt die Bewegung programmieren. Wir fangen mit der Berechnung des Eingabe-Richtungs-Vektors an unter Verwendung des globalen Input Objekts in _physics_process().

func _physics_process(delta):
    # We create a local variable to store the input direction.
    var direction = Vector3.ZERO

    # We check for each move input and update the direction accordingly.
    if Input.is_action_pressed("move_right"):
        direction.x += 1
    if Input.is_action_pressed("move_left"):
        direction.x -= 1
    if Input.is_action_pressed("move_back"):
        # Notice how we are working with the vector's x and z axes.
        # In 3D, the XZ plane is the ground plane.
        direction.z += 1
    if Input.is_action_pressed("move_forward"):
        direction.z -= 1

Hier werden wir alle Berechnungen in der virtuellen Funktion _physics_process() durchführen. Wie _process() ermöglicht sie ihnen das Node jeden Frame zu aktualisieren, ist aber speziell für Physikbezogenen Code, wie zum Beispiel das Bewegen eines kinematischen oder starren Körpers konzipiert.

Siehe auch

Um mehr über den Unterschied zwischen _process() und _physics_process() zu erfahren, siehe Idle and Physics Processing.

Wir beginnen mit der initialisierung einer direction Variabel auf Vector3.ZERO. Dann überprüfen wir ob der Spieler eine oder mehrere der move_* Inputs drückt und aktualisieren die x und z Komponenten des Vektors demensprechend. Diese Achsen entsprechen den auf denen die Grundfläche liegt.

Diese vier Bedingungen geben uns acht Möglichkeiten und somit acht mögliche Richtungen.

In dem Fall, dass der Spieler zum Beispiel sowohl W und D gleichzeitig drückt, wird der Vektor eine Länge von etwa 1.4 haben. Drückt er jedoch nur eine einzige Taste, hat er eine Länge von 1. Damit der Spieler sich in alle Richtungen mit derselben Geschwindigkeit bewegt, wollen wir dass die Länge konsistent ist. Um das zu erreichen, können wir die Methode normalize() aufrufen.

#func _physics_process(delta):
    #...

    if direction != Vector3.ZERO:
        direction = direction.normalized()
        $Pivot.look_at(translation + direction, Vector3.UP)

Hier wird der Vektor nur normalisiert, wenn die richtung eine Länge größer als Null hat, was bedeutet, dass der Spieler also eine Richtungstaste drückt.

In diesem Fall holen wir auch das Pivot Node und rufen ihre Methode look_at() auf. Diese Methode nimmt eine Position im Raum in globalen Koordinaten und die Richtung nach oben an. In diesem Fall können wir die Konstante Vector3.UP verwenden.

Bemerkung

Die lokalen Koordinaten einer Node, sind wie translation, relativ zu den Übernodes. Globale Koordinaten sind dagegen relativ zu den Hauptachsen der Welt, die Sie in dem Ansichtsfenster sehen können.

In 3D ist die Eigenschaft die die Position einer Node enthält, translation. Wenn man direction hinzufügt, erhält man eine Position zum Anschauen die einen Meter vom Spieler entfernt ist.

Dann aktualisieren wir den Bewegungs-Vektor. Wir müssen die Bodengeschwindigkeit getrennt von der Fallgeschwindigkeit berechnen. Stellen sie sicher einen Tab zurückzugehen, so dass die Zeile innerhalb der Funktion _physics_process(), aber außerhalb der Bedingung die wir gerade geschrieben haben liegt.

func _physics_process(delta):
    #...
    if direction != Vector3.ZERO:
        #...

    # Ground velocity
    velocity.x = direction.x * speed
    velocity.z = direction.z * speed
    # Vertical velocity
    velocity.y -= fall_acceleration * delta
    # Moving the character
    velocity = move_and_slide(velocity, Vector3.UP)

Für die Vertikale Geschwindigkeit, subtrahieren wir einfach die Fallbeschleunigung multipliziert mit der Deltazeit für jeden Frame. Beachten Sie die Verwendung des Operators -=, der eine Kurzform für Variabel = Variabel - ... ist.

Diese Codezeile bewirkt, dass unser Charakter in jedem Frame fällt. Das mag seltsam wirken wenn er bereits auf dem Boden ist, wir müssen dies allerdings trotzdem tun damit der Charakter jeden Frame mit dem Boden kollidiert.

Die Physik-Engine kann Interaktionen mit Wänden, dem Boden oder anderen Körpern während eines bestimmten Frames erkennen, wenn Bewegungen und Kollisionen stattfinden. Wir werden diese Eigenschaft später verwenden um den Sprung zu programmieren.

In der letzten Zeile rufen wir KinematicBody.move_and_aslide() auf. Es handelt sich hierbei um eine mächtige Methode der KinematicBody Klasse, die es Ihnen erlaubt einen Charakter fließend zu bewegen. Wenn er mitten in einer Bewegung gegen eine Wand stößt, versucht die Engine, dies für Sie auszugleichen.

Die Funktion nimmt zwei Parameter: unseren Bewegungs-Vektor und die Aufwärtsrichtung. Sie bewegt den Charakter und gibt uns einen Rest-Bewegungs-Vektor nach dem Anwenden von Kollisionen zurück. Beim Aufprall auf den Boden oder einer Wand, wird die Funktion die Geschwindigkeit in diese Richtung reduzieren oder zurücksetzen. In unserem Fall, verhindert das Speichern des Rückgabewerts das Ansammeln von vertikalem Momentum, welches ansonsten so groß werden würde dass der Charakter nach einer weile einfach durch die Bodenplatte hindurch fallen würde.

Und das ist schon der gesamte Code den Sie brauchen um den Charakter auf dem Boden zu bewegen.

Hier ist der vollständige Player.gd Code als Referenz.

extends KinematicBody

# How fast the player moves in meters per second.
export var speed = 14
# The downward acceleration when in the air, in meters per second squared.
export var fall_acceleration = 75

var velocity = Vector3.ZERO


func _physics_process(delta):
    var direction = Vector3.ZERO

    if Input.is_action_pressed("move_right"):
        direction.x += 1
    if Input.is_action_pressed("move_left"):
        direction.x -= 1
    if Input.is_action_pressed("move_back"):
        direction.z += 1
    if Input.is_action_pressed("move_forward"):
        direction.z -= 1

    if direction != Vector3.ZERO:
        direction = direction.normalized()
        $Pivot.look_at(translation + direction, Vector3.UP)

    velocity.x = direction.x * speed
    velocity.z = direction.z * speed
    velocity.y -= fall_acceleration * delta
    velocity = move_and_slide(velocity, Vector3.UP)

Testen der Bewegung unseres Spielers

Wir werden unseren Spieler in die Szene Main einfügen um ihn zu testen. Dazu müssen wir den Spieler instanziieren und eine Kamera hinzufügen. Anders als in 2D, werden Sie in 3D nichts sehen wenn der Viewport keine enthält Kamera hat die auf etwas gerichtet ist.

Speichern Sie Ihere Szene Player und öffnen Sie die Szene Main. Klicken Sie dazu oben im Editor auf den Reiter Main.

image1

Wenn Sie die Szene zuvor geschlossen haben, gehen sie zum Dateisystem Panel und doppelklicken sie auf Main.tscn, um sie erneut zu öffnen.

Um den Player zu instanziieren, klicken sie mit der rechten Maustaste auf das Main Node und wählen Sie Szene hier Instanziieren.

image2

Doppelklicken Sie in dem Popup auf Player.tscn. Der Charakter sollte nun in der Mitte des Ansichtsfensters erscheinen.

Hinzufügen einer Kamera

Fügen wir als nächstes die Kamera hinzu. Wie bei dem Pivot unseres Players, werden wir ein simples Rig anlegen. Klicken sie erneut mit der Rechten Maustaste auf das Main Node und wählen Sie dieses mal Node hier anhängen. Erstellen Sie eine neue Position3D, nennen Sie es CameraPivot und fügen sie dann ein Camera Node als Unternode hinzu. Ihr Szenenbaum sollte nun in etwa wie folgt aussehen.

image3

Beachten sie das Kontrollkästchen Vorschau das in der oberen linken Ecke erscheint wenn Sie die Camera ausgewählt haben. Sie können es anklicken, um eine Vorschau der Kameraprojektion im Spiel zu erhalten.

image4

Wir werden den Pivot verwenden, um die Kamera zu drehen, als wäre sie auf einem Kran. Teilen wir zunächst die 3D-Ansicht, um frei in der Szene navigieren zu können und zu sehen was die Kamera sieht.

Klicken sie in der Werkzeugleiste über dem Ansichtsfenster auf Ansicht und dann Zwei Ansichten. Sie können hierfür auch Ctrl+2 (Cmd+2 auf macOS) drücken.

image5

Wählen sie auf der unteren Ansicht die Camera und schalten sie die Kameravorschau ein, indem sie auf das Kontrollkästchen klicken.

|image6|

Verschieben Sie die Kamera in der oberen Ansicht um ungefähr 19 Einheiten auf der Z-Achse (die blaue).

|image7|

Jetzt kommt der Clou. Wählen Sie den CameraPivot und drehen Sie ihn um 45 Grad um die X-Achse (mit dem roten Kreis). Sie werden sehen, dass sich die Kamera bewegt, als wäre sie an einem Kran befestigt.

|image8|

Sie können die Szene durch Drücken von F6 starten und die Pfeiltasten drücken, um den Charakter zu bewegen.

|image9|

Wegen der perspektivischen Projektion ist um den Charakter etwas leerer Raum zu sehen. In diesem Spiel werden wir stattdessen eine orthografische Projektion verwenden, um den Spielbereich besser einzugrenzen und es für den Spieler einfacher zu machen Entfernungen zu erkennen.

Wählen Sie die Camera erneut aus und stellen sie die Projection auf Orthogonal und die Size auf 19. Der Charakter sollte nun flacher aussehen und der Boden sollte den Hintergrund ausfüllen.

|image10|

Damit haben wir sowohl Spieler bewegung als auch die Ansicht eingerichtet. Als Nächstes werden wir an den Monstern arbeiten.