Springen und Zerdrücken von Monstern

In diesem Teil werden wir die Fähigkeit zu Springen und Monster zu zerdrücken hinzufügen. In der nächsten Lektion werden wir den Spieler sterben lassen, wenn ihn ein Monster berührt.

Zuerst müssen wir einige Einstellungen in Bezug auf physikalische Interaktionen ändern. Betreten Sie die Welt der Physik-Layer.

Steuerung der physikalischen Interaktionen

Physikalische Körper haben zugriff auf zwei komplementäre Eigenschaften: Ebenen und Masken. Ebenen definieren auf welcher/-n physikalischen Ebene(n) ein Objekt ist.

Masken steuern die Ebenen, auf die ein Körper hört und die er erkennt. Dies wirkt sich auf die Kollisionserkennung aus. Wenn zwei Körper miteinander interagieren sollen, muss mindestens einer eine Maske haben, die dem anderen entspricht.

Wenn das verwirrend ist, keine Sorge, wir werden gleich drei Beispiele sehen.

Wichtig ist, dass Sie Ebenen und Masken verwenden können, um physikalische Interaktionen zu filtern, die Leistung zu steuern und um zusätzliche Bedingungen in Ihrem Code überflüssig zu machen.

Standardmäßig sind alle physikalischen Körper und Flächen sowohl auf Ebene als auch auf Maske 0 eingestellt. Das bedeutet, dass sie alle miteinander kollidieren.

Physikalische Schichten werden durch Zahlen dargestellt, aber wir können ihnen Namen geben, um den Überblick darüber zu behalten, welche es sind.

Ebenennamen festlegen

Geben wir unseren Physikebenen einen Namen. Gehen Sie zu Projekt -> Projekteinstellungen.

image0

Im linked Menü bis zum Eintrag Ebenen Namen -> 3D Physik navigieren. Dort ist eine Liste der Ebenen zu sehen, die mit einem Feld auf der rechten Seite versehen sind. In diese Felder werden die Bezeichnungen der Ebenen eingetragen. Dort sollten die ersten drei Ebenen player, enemies und world benannt werden.

image1

Jetzt können wir sie unseren Physik-Nodes zuweisen.

Ebenen und Masken zuweisen

Wählen Sie in der Main Szene den Knoten Ground. Erweitern Sie im Inspector den Abschnitt Collision. Dort sehen Sie die Ebenen und Masken des Knotens als ein Raster von Schaltflächen.

image2

Der ausgewählte Ground ist Teil der Welt, also soll er Teil der dritten Layer Ebene sein. Klicken Sie auf die vorgehobene Schaltfläche, um die erste Ebene zu deaktivieren und die dritte Ebene zu aktivieren. Minimieren Sie die Anzeige, indem Sie sie anklicken.

image3

Wie ich bereits erwähnt habe, ermöglicht die Eigenschaft Collision der Node,auf die Interaktion mit anderen physikalischen Objekten zu hören, aber für Kollisionen brauchen wir diese nicht. Der Ground muss auf nichts hören; er ist nur da, um zu verhindern, dass Kreaturen herunterfallen.

Beachten Sie, dass Sie auf die Schaltfläche "..." auf der rechten Seite der Eigenschaften klicken können, um eine Liste der benannten Kontrollkästchen anzuzeigen.

image4

Als nächstes sind Player und Mob dran. Öffnen Sie Player.tscn indem Sie im Dateisystem-Panel darauf doppel-klicken.

Wählen Sie die Node Player und setzen Sie seine Collision -> Mask auf "enemies" und "world". Sie können die Standardeigenschaft Layer beibehalten, da die erste Ebene die des "Spielers" ist.

image5

Öffnen Sie dann die Mob-Szene durch Doppelklick auf Mob.tscn und wählen Sie die Mob Node aus.

Setzen Sie Collision -> Layer auf "Feinde" und entfernen sie alle Auswahlfelder in der Collision -> Mask. So dass die Masken leer bleiben.

|image6|

Diese Einstellungen bedeuten, dass sich die Monster durch einander durch bewegen können. Wenn Sie möchten, dass die Monster miteinander kollidieren und gegeneinander gleiten, schalten Sie die Collosion -> Mask auf "Feinde" zu.

Bemerkung

Die Mobs brauchen die "Welt"-Ebene nicht zu maskieren, da sie sich nur auf der XZ-Ebene bewegen. Wir wenden keine Schwerkraft auf sie an.

Springen

Die Sprungmechanik selbst erfordert nur zwei Codezeilen. Öffnen Sie das Skript Player. Wir brauchen einen Wert, um die Stärke des Sprungs zu kontrollieren, und aktualisieren _physics_process(), um den Sprung zu erstellen.

Fügen sie am Anfang des Skripts nach der Zeile, die fall_acceleration definiert, jump_impulse ein.

#...
# Vertical impulse applied to the character upon jumping in meters per second.
export var jump_impulse = 20

Innerhalb von _physics_process() fügen Sie den folgenden Code vor der Zeile ein, wo wir move_and_slide() aufgerufen haben.

func _physics_process(delta):
    #...

    # Jumping.
    if is_on_floor() and Input.is_action_just_pressed("jump"):
        velocity.y += jump_impulse

    #...

Das ist alles, was man zum Springen braucht!

Die Methode is_on_floor() ist ein Werkzeug der Klasse KinematicBody. Sie gibt true zurück, wenn der KinematicBody-Körper in diesem Frame mit dem Boden kollidiert ist. Das ist der Grund, warum wir die Schwerkraft auf den Player anwenden. Damit kann die Klasse mit dem Boden kollidieren, anstatt über ihm zu schweben wie die Monster.

Wenn die Figur auf dem Boden steht und der Spieler auf "springen" drückt, geben wir ihr sofort eine Menge vertikale Geschwindigkeit hinzu. In Spielen muss die Steuerung wirklich reaktionsschnell sein, und solche sofortigen Geschwindigkeitsschübe sind zwar unrealistisch, fühlen sich aber toll an.

Beachten Sie, dass die Y-Achse positiv nach oben verläuft. Das ist anders als in 2D, wo die Y-Achse positiv nach unten verläuft.

Monster zerquetschen

Fügen wir als Nächstes die Squash-Mechanik hinzu. Wir werden die Figur über Monster springen lassen und sie gleichzeitig töten.

Wir müssen Kollisionen mit einem Monster erkennen und sie von Kollisionen mit dem Boden unterscheiden. Dazu können wir Godots group Tagging-Funktionen verwenden.

Öffnen Sie die Szene Mob.tscn erneut und wählen Sie die Node Mob. Gehen Sie zum Node-Dock auf der rechten Seite, um eine Liste der Signale zu sehen. Das Node-Dock hat zwei Registerkarten: Signale, die Sie bereits verwendet haben, und Gruppen, mit denen Sie den Knoten Tags zuweisen können.

Klicken Sie darauf, um ein Feld zu öffnen, in das Sie einen Tag-Namen eingeben können. Geben Sie "mob" in das Feld ein und klicken Sie auf die Schaltfläche Hinzufügen.

|image7|

Ein Symbol erscheint im Szenen-Dock, um anzuzeigen, dass der Knoten Teil mindestens einer Gruppe ist.

|image8|

Wir können nun die Gruppe aus dem Code verwenden, um Kollisionen mit Monstern von Kollisionen mit dem Boden zu unterscheiden.

Die Zerquetsch-Mechanik programmieren

Head back to the Player script to code the squash and bounce.

Am Anfang des Skripts brauchen wir eine weitere Variabel, bounce_impulse. Wenn wir einen Feind zerquetschen, wollen wir nicht unbedingt, dass die Figur so hoch fliegt wie beim Springen.

# Vertical impulse applied to the character upon bouncing over a mob in
# meters per second.
export var bounce_impulse = 16

Fügen Sie dann am Ende von _physics_process() die folgende Schleife ein. Bei move_and_slide() lässt Godot die Node manchmal mehrmals hintereinander bewegen, um die Bewegung der Figur zu glätten. Wir müssen also eine Schleife über alle Kollisionen machen, die möglicherweise stattgefunden haben.

In jeder Iteration der Schleife prüfen wir, ob wir auf einem Mob gelandet sind. Wenn ja, töten wir ihn und hüpfen weiter.

Mit diesem Code wird die Schleife nicht durchlaufen, wenn in einem bestimmten Frame keine Kollisionen aufgetreten sind.

func _physics_process(delta):
    #...
    for index in range(get_slide_count()):
        # We check every collision that occurred this frame.
        var collision = get_slide_collision(index)
        # If we collide with a monster...
        if collision.collider.is_in_group("mob"):
            var mob = collision.collider
            # ...we check that we are hitting it from above.
            if Vector3.UP.dot(collision.normal) > 0.1:
                # If so, we squash it and bounce.
                mob.squash()
                velocity.y = bounce_impulse

Das sind eine Menge neuer Funktionen. Hier finden Sie weitere Informationen über sie.

Die Funktionen get_slide_count() und get_slide_collision() stammen beide aus der Klasse KinematicBody und sind mit move_and_slide() verbunden.

get_slide_collision()` gibt ein KinematicCollision Objekt zurück, das Informationen darüber enthält, wo und wie die Kollision stattgefunden hat. Wir verwenden zum Beispiel die Eigenschaft collider ``, um zu prüfen, ob wir mit einem "Mob" kollidiert sind, indem wir ``is_in_group() an collision.collider.is_in_group("mob") aufrufen.

Bemerkung

Die Methode is_in_group() ist auf jedem Node verfügbar.

To check that we are landing on the monster, we use the vector dot product: Vector3.UP.dot(collision.normal) > 0.1. The collision normal is a 3D vector that is perpendicular to the plane where the collision occurred. The dot product allows us to compare it to the up direction.

Wenn das Ergebnis des Punktprodukts größer ist als 0, befinden sich die beiden Vektoren in einem Winkel von weniger als 90 Grad. Ein Wert, der größer als 0.1 ist, bedeutet, dass wir uns ungefähr über dem Monster befinden.

Wir rufen eine undefinierte Funktion auf, mob.squash(). Wir müssen sie der Klasse Mob hinzufügen.

Öffnen Sie das Skript Mob.gd durch Doppelklick im Dock FileSystem. Am Anfang des Skripts wollen wir ein neues Signal namens squashed definieren. Unten können Sie die Squash-Funktion hinzufügen, mit der wir das Signal ausgeben und den Mob zerstören.

# Emitted when the player jumped on the mob.
signal squashed

# ...


func squash():
    emit_signal("squashed")
    queue_free()

In der nächsten Lektion werden wir das Signal verwenden, um die Punkte zu addieren.

Damit sollten Sie in der Lage sein, Monster zu töten, indem Sie auf sie springen. Sie können F5 drücken, um das Spiel auszuprobieren und Main.tscn als die Hauptszene deines Projekts einzustellen.

Allerdings wird der Spieler noch nicht sterben. Daran werden wir im nächsten Teil arbeiten.