The main game scene

Now it's time to bring everything we did together into a playable game scene.

Create a new scene and add a Node named Main. (The reason we are using Node instead of Node2D is because this node will be a container for handling game logic. It does not require 2D functionality itself.)

Click the Instance button (represented by a chain link icon) and select your saved Player.tscn.

../../_images/instance_scene.png

Fügen Sie nun die folgenden Nodes als untergeordnete Elemente von Main hinzu und benenne sie wie abgebildet (Werte sind in Sekunden):

  • Timer (genannt MobTimer) - um zu steuern, wie oft Gegner erscheinen

  • Timer (genannt ScoreTimer) - um die Punktzahl jede Sekunde zu erhöhen

  • Timer (genannt StartTimer) - um eine Verzögerung vor dem Start zu geben

  • Position2D (genannt StartPosition) - um die Startposition des Spielers anzuzeigen

Stellen Sie die Eigenschaft Wait Time von jedem der Timer Nodes wie folgt ein:

  • MobTimer: 0.5

  • ScoreTimer: 1

  • StartTimer: 2

Stellen Sie zusätzlich die Eigenschaft One Shot von StartTimer auf "An" und die Position des StartPosition-Nodes auf (240, 450).

Gegner (Mobs) erzeugen

Der Main-Node wird neue Gegner hervorbringen, und wir möchten, dass sie an einer beliebigen Stelle am Rande des Bildschirms erscheinen. Fügen Sie einen Path2D <class_Path2D>`Node namens ``MobPath` als Unterobjekt von Main hinzu. Wenn Sie Path2D auswählen, sehen Sie oben im Editor einige neue Schaltflächen:

../../_images/path2d_buttons.png

Wählen Sie die Mittlere ("Punkt hinzufügen (in leerem Raum)"). Zeichnen Sie den Pfad, indem Sie durch klicken auf die dargestellten Ecken die Punkte hinzuzufügen. Damit die Punkte am Gitter einrasten, stellen Sie sicher, dass "Gitter-Einrasten benutzen(Shift+G)" aktiviert ist. Diese Option findet sich links vom "Schloss"-Symbol und wird als ein "Magnet neben sich schneidender Linien" dargestellt.

../../_images/grid_snap_button.png

Wichtig

Zeichnen Sie den Pfad im Uhrzeigersinn, sonst erscheinen Ihre Gegner nach außen statt nach innen!

../../_images/draw_path2d.gif

Nachdem Sie den Punkt 4 im Bild platziert haben, klicken Sie auf die Schaltfläche "Kurve schließen" und Ihre Kurve ist vollständig.

Jetzt, nachdem der Pfad definiert ist, fügen Sie einen PathFollow2D Node als Unterobjekt von MobPath hinzu und nenne ihn MobSpawnLocation. Dieser Node dreht sich automatisch und folgt dem Pfad, während er sich bewegt, so dass wir damit eine beliebige Position und Richtung entlang des Pfades auswählen können.

Die Szene sollte so aussehen:

../../_images/main_scene_nodes.png

Main-Skript

Füge ein Skript zu Main hinzu. Am Anfang des Skripts verwenden wir export (PackedScene), damit wir die Mob-Szene auswählen können, die wir als Instanz verwenden wollen.

extends Node

export(PackedScene) var mob_scene
var score

We also add a call to randomize() here so that the random number generator generates different random numbers each time the game is run:

func _ready():
    randomize()

Click the Main node and you will see the Mob Scene property in the Inspector under "Script Variables".

Man kann einen Wert für eine Eigenschaft auf zwei Wege hinzufügen:

  • Ziehen Sie Mob.tscn aus dem Panel "FileSystem" und legen Sie es in der Eigenschaft Mob unter den Skript-Variablen des Nodes Main ab.

  • Klicken Sie auf den Pfeil nach unten neben "[leer]" und wählen "Lade". Klicken Sie anschließend auf Mob.tscn.

Wählen Sie den „Player“-Node im Szenenbaum aus und klicken dann auf die "Node"-Registerkarte. Als nächstes stellen Sie sicher, dass "Signale" ausgewählt ist.

Sie sollten eine Liste der Signale für den Player-Node sehen. Suchen und doppelklicken Sie das hit Signal (oder Rechtsklicke darauf und drücken "Verbinden..."). Dies wird das Fenster für die Signal-Verbindung öffnen. Wir wollen eine neue Funktion namens game_over erstellen, welche angeben wird, was passieren muss, wenn das Spiel beendet wurde. Schreiben Sie "game_over" in das "Empfängermethode"-Eingabefeld unten im Fenster und klicken auf "Verbinden". Fügen Sie dann sowohl den folgenden Code zur neuen Funktion, als auch eine new_game-Funktion, die alles für ein neues Spiel einstellen wird:

func game_over():
    $ScoreTimer.stop()
    $MobTimer.stop()

func new_game():
    score = 0
    $Player.start($StartPosition.position)
    $StartTimer.start()

Verbinden Sie nun das Signal Timeout() von jedem der Timer-Nodes (StartTimer, ScoreTimer ` und MobTimer) mit dem Main-Skript. StartTimer startet die anderen beiden Timer. ScoreTimer erhöht die Punktzahl um 1.

func _on_ScoreTimer_timeout():
    score += 1

func _on_StartTimer_timeout():
    $MobTimer.start()
    $ScoreTimer.start()

In _on_MobTimer_timeout(), we will create a mob instance, pick a random starting location along the Path2D, and set the mob in motion. The PathFollow2D node will automatically rotate as it follows the path, so we will use that to select the mob's direction as well as its position. When we spawn a mob, we'll pick a random value between 150.0 and 250.0 for how fast each mob will move (it would be boring if they were all moving at the same speed).

Beachten Sie, dass der Szene mit add_child() eine neue Instanz hinzugefügt werden muss.

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

    # Choose a random location on Path2D.
    var mob_spawn_location = get_node("MobPath/MobSpawnLocation")
    mob_spawn_location.offset = randi()

    # Set the mob's direction perpendicular to the path direction.
    var direction = mob_spawn_location.rotation + PI / 2

    # Set the mob's position to a random location.
    mob.position = mob_spawn_location.position

    # Add some randomness to the direction.
    direction += rand_range(-PI / 4, PI / 4)
    mob.rotation = direction

    # Choose the velocity for the mob.
    var velocity = Vector2(rand_range(150.0, 250.0), 0.0)
    mob.linear_velocity = velocity.rotated(direction)

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

Wichtig

Why PI? In functions requiring angles, Godot uses radians, not degrees. Pi represents a half turn in radians, about 3.1415 (there is also TAU which is equal to 2 * PI). If you're more comfortable working with degrees, you'll need to use the deg2rad() and rad2deg() functions to convert between the two.

Die Szene testen

Let's test the scene to make sure everything is working. Add this new_game call to _ready():

func _ready():
    randomize()
    new_game()

Lassen Sie uns auch Main zu unserer "Main-Szene" hinzufügen - die automatisch läuft, wenn das Spiel startet. Drücken Sie dazu den "Start"-Knopf und wählen Main.tscn im Dialog.

Tipp

If you had already set another scene as the "Main Scene", you can right click Main.tscn in the FileSystem dock and select "Set As Main Scene".

Es sollte jetzt möglich sein den Spieler zu bewegen, erscheinende Gegner zu sehen, und den Spieler verschwinden zu sehen, wenn er von einem Gegner getroffen wird.

Wenn Sie sicher sind, dass alles läuft, löschen Sie den Funktionsaufruf new_game() von _ready().

What's our game lacking? Some user interface. In the next lesson, we'll add a title screen and display the player's score.