Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

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 den Player-Node und wählen Sie Skript hinzufügen, um ihm ein neues Skript hinzuzufügen. Setzen Sie im Popup die Vorlage auf Leer, bevor Sie den Erstellen-Button drücken.

image0

Fangen wir mit den Eigenschaften der Klasse an. Wir werden eine Bewegungsgeschwindigkeit, eine Fallbeschleunigung, die Schwerkraft repräsentiert, und eine Geschwindigkeit definieren, mit der wir den Charakter bewegen werden.

extends CharacterBody3D

# 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 target_velocity = Vector3.ZERO

Dies sind allgemeine Eigenschaften für einen sich bewegenden Body. Die target_velocity ist ein 3D-Vektor, der eine Geschwindigkeit mit einer Richtung kombiniert. Hier definieren wir sie als eine Eigenschaft, weil wir ihren 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 tausend Einheiten (Pixel) vielleicht nur der halben Breite Ihres Bildschirms entsprechen, ist es in 3D ein Kilometer.

Lassen Sie uns die Bewegung programmieren. Wir beginnen mit der Berechnung des Eingabe-Richtungsvektors 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, den Node jeden Frame zu aktualisieren, ist aber speziell für physikbezogenen Code, wie zum Beispiel das Bewegen eines Kinematic- oder Rigid-Body konzipiert.

Siehe auch

Um mehr über den Unterschied zwischen _process() und _physics_process() zu erfahren, siehe Leerlauf und Physik-Prozessierung.

Wir beginnen mit der Initialisierung einer direction-Variablen auf Vector3.ZERO. Dann überprüfen wir, ob der Spieler eine oder mehrere der move_*-Eingaben drückt und aktualisieren die x und z Komponenten des Vektors demensprechend. Diese Achsen entsprechen denen der Boden-Ebene.

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

Wenn der Spieler z.B. W und D gleichzeitig drückt, hat der Vektor eine Länge von etwa 1,4. Drückt er aber nur eine Taste, hat er eine Länge von 1. Wir wollen, dass die Länge des Vektors konsistent ist und sich nicht diagonal schneller bewegt. Um dies zu erreichen, können wir seine Methode normalized() aufrufen.

#func _physics_process(delta):
    #...

    if direction != Vector3.ZERO:
        direction = direction.normalized()
        # Setting the basis property will affect the rotation of the node.
        $Pivot.basis = Basis.looking_at(direction)

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

We compute the direction the $Pivot is looking by creating a Basis that looks in the direction direction.

Dann aktualisieren wir die Geschwindigkeit. Wir müssen die Bodengeschwindigkeit und die Fallgeschwindigkeit separat berechnen. Gehen Sie eine Einrückungstiefe zurück, so dass sich die Zeilen innerhalb der Funktion _physics_process() befinden, aber außerhalb der Bedingung, die wir eben oben geschrieben haben.

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

    # Ground Velocity
    target_velocity.x = direction.x * speed
    target_velocity.z = direction.z * speed

    # Vertical Velocity
    if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
        target_velocity.y = target_velocity.y - (fall_acceleration * delta)

    # Moving the Character
    velocity = target_velocity
    move_and_slide()

Die Funktion CharacterBody3D.is_on_floor() gibt true zurück, wenn der Body in diesem Frame mit dem Boden kollidiert ist. Das ist der Grund, warum wir die Schwerkraft auf den Player nur anwenden, während er in der Luft ist.

Für die vertikale Geschwindigkeit subtrahieren wir die Fallbeschleunigung multipliziert mit der Delta-Zeit für jedes Frame. Diese Codezeile bewirkt, dass unser Charakter in jedem Frame fällt, solange er nicht auf dem Boden steht oder mit diesem kollidiert.

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

In der letzten Zeile rufen wir CharacterBody3D.move_and_slide() auf. Dies ist eine mächtige Methode der Klasse CharacterBody3D, die es Ihnen ermöglicht, einen Charakter fließend zu bewegen. Wenn de Charakter in der Mitte einer Bewegung gegen eine Wand stößt, versucht die Engine, dies für Sie auszugleichen. Sie verwendet den velocity-Wert des CharacterBody3D

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 CharacterBody3D

# 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 target_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.basis = Basis.looking_at(direction)

    # Ground Velocity
    target_velocity.x = direction.x * speed
    target_velocity.z = direction.z * speed

    # Vertical Velocity
    if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
        target_velocity.y = target_velocity.y - (fall_acceleration * delta)

    # Moving the Character
    velocity = target_velocity
    move_and_slide()

Testen der Bewegung unseres Spielers

Wir werden unseren Player in die Main-Szene einsetzen, um ihn zu testen. Dazu müssen wir den Player instanziieren und dann eine Kamera hinzufügen. Anders als in 2D wird man in 3D nichts sehen, wenn der Viewport keine Kamera hat, die auf etwas gerichtet ist.

Speichern Sie Ihre Player-Szene und öffnen Sie die Main-Szene. Sie können auf den Main-Tab oben im Editor klicken, um dies zu tun.

image1

Wenn Sie die Szene zuvor geschlossen hatten, gehen Sie zum Dateisystem-Dock und doppelklicken Sie auf main.tscn, um sie erneut zu öffnen.

Um den Player zu instanziieren, klicken Sie mit der rechten Maustaste auf den Main-Node und wählen Sie Child-Szene instanziieren.

image2

Doppelklicken Sie in dem Popup auf player.tscn. Der Charakter sollte in der Mitte des Viewports erscheinen.

Hinzufügen einer Kamera

Fügen wir als nächstes die Kamera hinzu. Wie wir es mit dem Pivot unseres Players gemacht haben, werden wir ein einfaches Rig erstellen. Klicken Sie erneut mit der rechten Maustaste auf den Main-Node und wählen Sie Child-Node hinzufügen. Erstellen Sie einen neuen Marker3D, und nennen Sie ihn CameraPivot. Wählen Sie CameraPivot und fügen Sie einen Child-Node Camera3D dazu. Ihr Szenenbaum sollte nun 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 Toolbar über dem Viewport auf Ansicht und dann 2 Viewports. Sie können hierfür auch Strg+2 (Cmd+2 unter macOS) drücken.

|image11|

image5

Wählen Sie in der unteren Ansicht Ihre Camera3D und schalten Sie die Kamera-Vorschau 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 passiert die Magie. Wählen Sie den CameraPivot und rotieren Sie ihn um -45 Grad um die X-Achse (mit Hilfe des roten Kreises). Sie werden sehen, dass sich die Kamera bewegt, als ob sie an einem Kran befestigt wäre.

|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 Größe auf 19. Der Charakter sollte nun flacher aussehen und der Boden sollte den Hintergrund ausfüllen.

Bemerkung

Wenn Sie eine orthogonale Kamera in Godot 4 verwenden, hängt die Qualität des Richtungsschattens vom Far-Wert der Kamera ab. Je höher der Far-Wert ist, desto weiter weg kann die Kamera sehen. Höhere Far-Werte verschlechtern jedoch auch die Schattenqualität, da die Schattenwiedergabe eine größere Entfernung zurücklegen muss.

Wenn Richtungsschatten nach dem Wechsel zu einer orthogonalen Kamera zu verschwommen aussehen, verringern Sie die Far-Eigenschaft der Kamera auf einen niedrigeren Wert wie etwa 100. Verringern Sie die Far-Eigenschaft nicht zu sehr, da sonst Objekte in der Ferne verschwinden.

|image10|

Testen Sie Ihre Szene. Sie sollten sich in alle 8 Richtungen bewegen können und nicht durch den Boden glitchen!

Zuletzt haben wir sowohl die Bewegungen des Spielers als auch die Ansicht fertiggestellt. Als Nächstes werden wir an den Monstern arbeiten.