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.

Monster spawnen

In diesem Teil werden wir Monster nach dem zufällig entlang eines Pfades spawnen. Am Ende werden Monster auf dem Spielfeld herumlaufen.

image0

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

Bevor wir den Pfad zeichnen, werden wir die Auflösung des Spiels ändern. Unser Spiel hat eine Default-Fenstergröße von 1152x648. 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 Spawn-Pfads

Wie im 2D-Spiel-Tutorial entwerfen Sie einen Pfad und verwenden einen PathFollow3D-Node, um zufällige Stellen auf diesem 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 des Views zu finden, können wir einige Platzhalter-Meshes verwenden. Ihr Viewport 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 unter macOS), um die Ansicht in zwei Teile zu teilen. Wählen Sie den Camera3D-Node und klicken Sie auf das Kontrollkästchen Vorschau im unteren Viewport.

image3

Hinzufügen von Platzhalterzylindern

Fügen wir die Platzhalter-Meshes hinzu. Fügen Sie einen neuen Node3D als Child des Main-Nodes hinzu und nennen Sie ihn Cylinders. Wir werden ihn benutzen, um die Zylinder zu gruppieren. Wählen Sie Cylinders und fügen Sie einen untergeordneten Node MeshInstance3D hinzu

image4

Weisen Sie im Inspektor der Eigenschaft Mesh ein CylinderMesh zu.

image5

Stellen Sie den oberen Viewport auf die orthogonale Ansicht ein von oben, indem Sie das Menü in der oberen linken Ecke des Viewports verwenden. Alternativ können Sie auch die Taste 7 auf dem Numpad drücken.

|image6|

Das Raster kann manchmal stören. Sie können es ein- und ausschalten, indem Sie das Menü Ansicht in der Toolbar aufrufen und auf Raster anzeigen klicken.

|image7|

Bewegen Sie nun den Zylinder entlang der Bodenebene, indem Sie die Kameravorschau im unteren Viewport betrachten. Ich empfehle, dazu die Gitter-Einrastfunktion zu verwenden. Sie können sie ein- und ausschalten, indem Sie auf das Magnet-Icon in der Toolbar klicken oder Y drücken.

|image8|

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

|image9|

Wir werden Kopien des Mesh erstellen und um das Spielfeld herum platzieren. Drücken sie Strg + D (Cmd + D unter macOS), um den Node zu duplizieren. Sie können auch im "Szene"-Dock den Node mit der rechten Maustaste anklicken 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 Umschalt-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 hervorheben, 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 Szene-Dock aus. Klicken Sie dazu auf die erste Instanz und mit gedrückter Umschalttaste auf die Letzte.

|image12|

Erweitern Sie im Inspektor den Abschnitt Material und weisen Sie dem Slot 0 ein StandardMaterial3D zu.

|image13|

../../_images/standard_material.webp

Klicken sie auf das Kugel-Icon, um die Material-Resource zu öffnen. Es wird eine Vorschau des Materials angezeigt, gefolgt von einer langen Liste von Abschnitten, 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 den Abschnitt Albedo.

../../_images/albedo_section.webp

Wählen Sie eine Farbe, die sich vom Hintergrund abhebt, z.B. ein leuchtendes Orange.

|image14|

Wir können nun die Zylinder als Hilfslinien verwenden. Klappen Sie diese im Szene-Dock ein, indem Sie auf den grauen Pfeil neben dem Cylinders-Node klicken. In Zukunft können Sie auch ihre Sichtbarkeit ein- und ausschalten, indem Sie auf das Augen-Icon neben Cylinders klicken.

|image15|

Fügen Sie einen Child-Node Path3D zum Main-Node hinzu. In der Toolbar erscheinen vier Icons. Klicken Sie auf das Tool Punkt hinzufügen, das Icon mit dem grünen "+"-Zeichen.

|image16|

Bemerkung

Es kann zu jedem Tool ein Tooltip mit der Beschreibung des Tools angezeigt werden, indem man den Mauszeiger über das Icon bewegt.

Klicken Sie in die Mitte jedes Zylinders, um einen Punkt zu erstellen. Klicken Sie dann auf das Icon Kurve schließen in der Toolbar, 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|

Ihr Pfad sollte folgendermaßen aussehen.

|image18|

To sample random positions on it, we need a PathFollow3D node. Add a PathFollow3D as a child of the Path3D. Rename the two nodes to SpawnLocation and SpawnPath, respectively. It's more descriptive of what we'll use them for.

image19

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

Mobs zufällig spawnen

Klicken Sie mit der rechten Maustaste auf den Main-Node und fügen Sie ihm ein neues Skript hinzu.

Zuerst exportieren wir eine Variable in den Inspektor, damit wir ihr mob.tscn oder ein anderes Monster zuweisen können.

extends Node

@export var mob_scene: PackedScene

Wir wollen Mobs in regelmäßigen Zeitabständen spawnen lassen. Um das zu tun, müssen wir zurück zur Szene gehen und einen Timer hinzufügen. Zuvor müssen wir jedoch die Datei mob.tscn der obigen Eigenschaft mob_scene zuweisen (sonst ist sie null!)

Gehen Sie zurück zum 3D-Bildschirm und wählen Sie den Main-Node. Ziehen Sie die Datei mob.tscn aus dem Dateisystem-Dock auf den Mob Scene-Slot im Inspektor.

image20

Fügen Sie einen neuen Timer-Node als Child von Main hinzu. Nennen Sie ihn MobTimer.

image21

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

image22

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

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

image23

Verbinden Sie es mit dem Main-Node.

image24

Dies bringt Sie zurück zum Skript, mit einer neuen leeren _on_mob_timer_timeout() Funktion.

Jetzt programmieren wir die Spawn-Logik des Mobs. Das werden wir machen:

  1. die Mob-Szene instanziieren.

  2. eine zufällige Position auf dem Spawnpfad wählen.

  3. die Position des Spielers ermitteln.

  4. die initialize()-Methode des Mobs aufrufen und ihr die zufällige Position und die Position des Spielers übergeben.

  5. den Mob als Child des Main-Nodes hinzufügen.

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

    # 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.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

Oben erzeugt randf() einen Zufallswert zwischen 0 und 1, was vom progress_ratio-Attribut des PathFollow-Nodes erwartet wird: 0 ist der Anfang des Pfades, 1 ist das Ende des Pfades. Der Pfad, den wir gesetzt haben, verläuft um den Viewport der Kamera herum, also ist jeder zufällige Wert zwischen 0 und 1 eine zufällige Position entlang des Viewport-Randes!

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

extends Node

@export var mob_scene: PackedScene


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

    # 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.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

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

image25

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