Monster erzeugen

In diesem Teil werden wir Monster nach dem Zufallsprinzip entlang eines Pfades aussetzen. Am Ende werden Monster auf dem Spielfeld herumlaufen.

image0

Doppelklicken Sie auf Main.tscn im Dock Dateisystem, um die Szene Main zu öffnen.

Bevor wir den Pfad zeichnen, werden wir die Auflösung des Spiels ändern. Unser Spiel hat eine Standard-Fenstergröße von 1024x600. Wir werden es auf 720x540 einstellen, eine nette kleine Box.

Gehen Sie zu Projekt -> Projekteinstellungen.

image1

Navigieren Sie im linken Menü nach unten zu Anzeige -> Fenster. Auf der rechten Seite setzen Sie die Breite auf 720 und die Höhe auf 540.

image2

Erstellen des Erzeugpfads

Wie im 2D-Spiel-Tutorial werden Sie einen Pfad entwerfen und ein PathFollow Node verwenden, um zufällige Stellen auf dem Pfad zu markieren.

In 3D ist es allerdings etwas komplizierter, den Pfad zu zeichnen. Wir wollen, dass er um die Spielansicht herum verläuft, damit die Monster direkt außerhalb des Bildschirms erscheinen. Aber wenn wir einen Pfad zeichnen, werden wir ihn in der Kameravorschau nicht sehen.

Um die Grenzen der Ansicht zu finden, können wir einige Platzhalter-Meshes verwenden. Ihr Ansichtsfenster sollte immer noch in zwei Teile geteilt sein, mit der Kameravorschau am unteren Rand. Wenn das nicht der Fall ist, drücken Sie Strg + 2 (Cmd + 2 auf macOS), um die Ansicht in zwei Teile zu teilen. Wählen Sie Kamera aus und klicken Sie auf das Kontrollkästchen Vorschau im unteren Ansichtsfenster.

image3

Hinzufügen von Platzhalterzylindern

Fügen wir nun die Platzhalternetze hinzu. Fügen Sie ein neues Node Spatial als untergeordnetes Element des Nodes Main hinzu und nennen Sie es Cylinders. Wir werden es verwenden, um die Zylinder zu gruppieren. Fügen Sie als untergeordnetes Element ein MeshInstance Node hinzu.

image4

Weisen Sie im Inspektor der Eigenschaft Mesh ein CylinderMesh zu.

image5

Stellen Sie das obere Ansichtsfenster auf die obere orthogonale Ansicht ein, indem Sie das Menü in der oberen linken Ecke des Ansichtsfensters verwenden. Alternativ können Sie auch die Taste 7 auf der Tastatur drücken.

|image6|

Das Raster ist für mich etwas störend. Sie können es ausschalten, indem Sie das Menü Ansicht in der Symbolleiste aufrufen und auf Gitter anzeigen klicken.

|image7|

Bewegen Sie nun den Zylinder entlang der Bodenebene, indem Sie die Kameravorschau im unteren Ansichtsfenster betrachten. Ich empfehle, dazu die Einrastfunktion zu verwenden. Sie können sie einschalten, indem Sie auf das Magnetsymbol in der Symbolleiste klicken oder Y drücken.

|image8|

Platzieren Sie den Zylinder so, dass er sich direkt außerhalb des Blickfelds der Kamera in der oberen linken Ecke befindet.

|image9|

Wir werden Kopien des Mesh erstellen und um das Spielfeld herum platzieren. Drücken sie Ctrl + D (Cmd + D auf macOS), um das Node zu duplizieren. Sie können auch im "Scene"-Panel das Node und Duplizieren auswählen. Bewegen sie die Kopie entlang der blauen Z-Achse bis sie sich gerade außerhalb der Kameravorschau befindet.

Wählen sie beide Zylinder aus, indem sie die Shift-Taste drücken und auf den nicht ausgewählten Zylinder klicken, und duplizieren Sie beide.

|image10|

Bewegen sie diese nach rechts, indem sie die rote X-Achse bewegen.

|image11|

Sie sind ein bisschen schwer zu sehen, wenn sie weiß sind, oder? Wir werden sie aufwerten, indem wir ihnen ein neues Material geben.

In 3D definieren Materialien die visuellen Eigenschaften einer Oberfläche, wie etwa ihre Farbe, wie sie das Licht reflektiert, und so weiter. Wir können Materialien benutzen, um die Farbe des Mesh zu ändern.

Wir können alle vier Zylinder auf einmal aktualisieren. Wählen Sie alle Mesh-Instanzen im Panel*Szene* aus. Klicken Sie dazu auf die erste Instanz und mit gedrückter Umschalttaste auf die Letzte.

|image12|

Im Inspektor, klappen Sie die Abteilung Material aus und weisen Sie dem Steckplatz 0 ein SpatialMaterial zu.

|image13|

Klicken sie auf das Kugelsymbol, um die Material-Resource zu öffnen. Es wird eine Vorschau des Materials angezeigt, gefolgt von einer langen Liste von Abteilungen, die mit Eigenschaften gefüllt sind. Diese können benutzt werden, um alle möglichen Arten von Oberflächen zu erschaffen, von Metall über Stein bis hin zu Wasser.

Erweitern Sie die Albedo Abteilung und wählen Sie eine Farbe, die im Kontrast zum Hintergrund steht, wie etwa ein helles Orange.

|image14|

Wir können nun die Zylinder als Hilfslinien verwenden. Klappen Sie diese im Panel Szene ein, indem Sie auf den grauen Pfeil neben ihnen klicken. In Zukunft können Sie auch ihre Sichtbarkeit umschalten, indem Sie auf das Augensymbol neben Zylinder klicken.

|image15|

Fügen sie ein Pfad-Node als untergeordnetes Element von Main hinzu. In der Symbolleiste sollten vier Symbole angezeigt werden. Klicken Sie auf das Werkzeug Punkt hinzufügen (das Symbol mit dem grünen "+").

|image16|

Bemerkung

Es kann zu jedem Werkzeug ein Tooltip mit der Beschreibung des Werkzeugs angezeigt werden, indem man den Mauszeiger über das Symbol bewegt.

Klicken Sie in die Mitte jedes Zylinders, um einen Punkt zu erstellen. Klicken Sie dann auf das Symbol Kurve schließen in der Werkzeugleiste, um den Pfad zu schließen. Wenn ein Punkt etwas abweicht, können Sie darauf klicken und ihn bewegen, um ihn neu zu positionieren.

|image17|

Dein Pfad sollte folgendermaßen aussehen.

|image18|

Um eine Stichprobe von zufälligen Positionen zu erstellen, benötigen wir ein PathFollow-Node. Fügen Sie ein PathFollow-Node als untergeordnetes Element des Pfads hinzu. Benennen Sie die beiden Knoten in SpawnPath bzw. SpawnLocation um. Das beschreibt besser, wofür wir sie verwenden werden.

image19

Damit sind wir bereit, den Spawn-Mechanismus zu programmieren.

Gegner (Mobs) zufällig erzeugen

Wählen Sie den Control-Node aus und hängen Sie ein Skript an.

Wir exportieren zunächst eine Variable in den Inspektor, so dass wir Mob.tscn oder jedes andere Monster zuweisen können.

Da wir die Monster prozedural spawnen werden, wollen wir jedes Mal, wenn wir das Spiel spielen, die Zahlen zufällig generieren. Wenn wir das nicht tun, werden die Monster immer in der gleichen Reihenfolge spawnen.

extends Node

export (PackedScene) var mob_scene


func _ready():
    randomize()

Wir wollen die Gegner in regelmäßigen Abständen spawnen lassen. Um das zu tun, müssen wir zurück zur Szene gehen und einen Timer hinzufügen. Vorher müssen wir jedoch die Datei Mob.tscn der Eigenschaft mob_scene zuweisen.

Gehen Sie zurück zum 3D-Bildschirm und wählen Sie das Node Main. Ziehen Sie Mob.tscn aus dem FileSystem Panel auf den Mob Scene Steckplatz im Inspektor.

image20

Fügen Sie ein neues Timer-Node als untergeordnetes Element von Main. Nennen sie es MobTimer.

image21

Legen sie im Inspektor seine Wartezeit auf ``0.5``Sekunden fest und aktivieren sie Autostart, sodass automatisch gestartet wird, wenn wir das Spiel starten.

image22

Timer senden immer dann ein timeout-Signal, wenn sie das Ende ihrer Wartezeit erreichen. Standardmäßig starten sie dann automatisch neu, wodurch sie das Signal in einem Zyklus senden. Wir können uns mit diesem Signal vom Main-Node verbinden, um alle 0.5 Sekunden Monster zu spawnen.

Während der MobTimer noch ausgewählt ist, gehen Sie zum Node Dock auf der rechten Seite und doppelklicken Sie auf das timeout Signal.

image23

Verbinden Sie es mit dem Knoten Main.

image24

Während der MobTimer noch ausgewählt ist, gehen Sie zum Node-Panel auf der rechten Seite und doppelklicken Sie auf das timeout Signal.

Jetzt programmieren wir die Spawn-Logik der Gegner. Das werden wir machen:

  1. Die Gegner-Szene instanziieren.

  2. Wählen Sie eine zufällige Position auf dem Spawnpfad.

  3. Ermitteln Sie die Position des Spielers.

  4. Rufen Sie die initialize()-Methode des Gegners auf und übergeben Sie ihr die zufällige Position und die Position des Spielers.

  5. Füge den Gegner als Kind des Main-Nodes hinzu.

func _on_MobTimer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instance()

    # Choose a random location on the SpawnPath.
    # We store the reference to the SpawnLocation node.
    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    # And give it a random offset.
    mob_spawn_location.unit_offset = randf()

    var player_position = $Player.transform.origin
    mob.initialize(mob_spawn_location.translation, player_position)

    add_child(mob)

Oben erzeugt randf() einen Zufallswert zwischen 0 und 1, was der unit_offset des PathFollow-Nodes erwartet.

Hier ist das komplette Main.gd Skript, als Referenz.

extends Node

export (PackedScene) var mob_scene


func _ready():
    randomize()


func _on_MobTimer_timeout():
    var mob = mob_scene.instance()

    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    mob_spawn_location.unit_offset = randf()
    var player_position = $Player.transform.origin
    mob.initialize(mob_spawn_location.translation, player_position)

    add_child(mob)

Sie können die Szene durch das Drücken von F6 testen. Ein Monster sollte nun erscheinen und sich in einer geraden Linie bewegen.

image25

Noch krachen sie in einander, wenn sich ihre Wege kreuzen. Wir werden dieses Problem im nächsten Teil angehen.