Anzeigesystem

Das Letzte, was unser Spiel noch benötigt, ist eine Benutzeroberfläche (UI) um Dinge anzeigen wie die Punktzahl, ein "Spiel Vorbei" Text und einen Neustart Knopf.

Erstellen Sie eine neue Szene und fügen Sie ein CanvasLayer-Node mit dem Namen HUD hinzu. "HUD" steht für "heads-up display", eine Informationsanzeige, die als Overlay über der Spielansicht erscheint.

Mit dem Node CanvasLayer können wir unsere Oberflächenelemente auf einer Ebene über dem Rest des Spiels zeichnen, so dass die angezeigten Informationen nicht durch irgendwelche Spielelemente wie den Spieler oder Gegner verdeckt werden.

Das HUD sollte die folgenden Informationen anzeigen:

  • Punktzahl, geändert durch ScoreTimer.

  • Eine Nachricht, wie z.B. "Game Over" oder "Get Ready!"

  • Eine "Start"-Taste, um das Spiel zu starten.

Der Basis-Node für UI-Elemente ist Control. Um unsere Benutzeroberfläche zu erstellen, verwenden wir zwei Arten vom Control Node: Label und Button.

Erstelle das Folgende als Unterobjekte des HUD-Nodes:

  • Label genannt ScoreLabel.

  • Label genannt Message.

  • Button genannt StartButton.

  • Timer genannt MessageTimer.

Klicken Sie auf das ScoreLabel und geben eine Zahl in das Text-Feld im Inspektor ein. Die Standardschriftart für Control-Nodes ist klein und lässt sich nicht gut skalieren. In den Spielassets ist eine Schriftdatei namens "Xolonium-Regular.ttf" enthalten. Um diese Schriftart zu verwenden, gehen Sie für jeden der drei Control-Nodes wie folgt vor:

  1. Klicken Sie auf die leere Box unter Theme overrides > Fonts und wählen sie "New DynamicFont"

../../_images/custom_font1.png
  1. Klicken Sie auf die von Ihnen hinzugefügte "DynamicFont" und wählen unter "Font/Font Data" erst den Menüeintrag "Lade" und dann die Datei "Xolonium-Regular.ttf".

../../_images/custom_font2.png

Lege die Eigenschaft "Size" unter Settings fest, 64 funktioniert gut.

../../_images/custom_font3.png

Sobald Sie dies auf ScoreLabel getan haben, können Sie auf den Abwärtspfeil neben der Eigenschaft "Schriftart" klicken und "Kopieren" wählen, dann "Einfügen" an der gleichen Stelle auf den beiden anderen Kontrollnodes.

Bemerkung

Anker und Ränder: Control Nodes haben eine Position und eine Größe, aber sie haben auch Anker und Ränder. Anker definieren den Ursprung - den Referenzpunkt für die Kanten des Nodes. Die Ränder werden automatisch aktualisiert, wenn Sie ein Steuernode verschieben oder dessen Größe ändern. Sie stellen den Abstand zwischen den Kanten des Steuernodes und dessen Anker dar.

Ordnen Sie die Nodes wie unten gezeigt an. Klicken Sie auf den "Layout"-Knopf, um das Layout eines Control-Nodes festzulegen:

../../_images/ui_anchor.png

Sie können die Nodes manuell verschieben um sie zu platzieren oder verwenden Sie die folgenden Einstellungen für eine genauere Platzierung:

ScoreLabel (HighScore)

  • Layout : "Oben groß"

  • Text : 0

  • Align : "Center"

Nachricht

  • Layout : "HCenter groß"

  • Text : Dodge the Creeps!

  • Align : "Center"

  • Autowrap : "An"

StartButton (Startknopf)

  • Text : Start

  • Layout : "Mitte unten"

  • Margin :

    • Top: -200

    • Bottom: -100

Stellen Sie im MessageTimer die Wait Time auf 2 und stellen Sie die One Shot Eigenschaft auf "An".

Fügen Sie nun dieses Skript zu HUD hinzu:

extends CanvasLayer

signal start_game

Das Signal start_game signalisiert dem Node Main, dass die Taste gedrückt wurde.

func show_message(text):
    $Message.text = text
    $Message.show()
    $MessageTimer.start()

Diese Funktion wird aufgerufen, wenn wir eine Nachricht vorübergehend anzeigen möchten, z.B. "Get Ready".

func show_game_over():
    show_message("Game Over")
    # Wait until the MessageTimer has counted down.
    yield($MessageTimer, "timeout")

    $Message.text = "Dodge the\nCreeps!"
    $Message.show()
    # Make a one-shot timer and wait for it to finish.
    yield(get_tree().create_timer(1), "timeout")
    $StartButton.show()

Diese Funktion wird aufgerufen, wenn der Spieler verliert. "Game Over" wird für 2 Sekunden angezeigt und wechselt dann zum Titelbildschirm. Nach einer kurzen Pause wird die "Start"-Schaltfläche angezeigt.

Bemerkung

Falls eine kurze Pause benötigt wird, kann als Alternative zum Timer-Node die Funktion create_timer() von SceneTree verwendet werden. Das kann sehr nützlich sein, um eine Verzögerung hinzuzufügen. Wie im obigen Code, wo wir ein wenig warten wollen, bevor der "Start"-Knopf angezeigt wird.

func update_score(score):
    $ScoreLabel.text = str(score)

Diese Funktion wird von Main aufgerufen, wenn sich der Punktestand ändert.

Verbinden Sie jeweils das timeout()-Signal von MessageTimer und das pressed()-Signal von StartButton und fügen Sie den folgenden Code in die neuen Funktionen ein:

func _on_StartButton_pressed():
    $StartButton.hide()
    emit_signal("start_game")

func _on_MessageTimer_timeout():
    $Message.hide()

Das HUD mit Main verbinden

Jetzt, da wir die HUD-Szene erstellt haben, gehen Sie zurück zu Main und platziere die HUD-Szene in Main genauso wie die Player-Szene. Der gesamte Szenenbaum sollte so aussehen, also stellen Sie sicher, dass nichts fehlt:

../../_images/completed_main_scene.png

Nun müssen wir die HUD-Funktionalität mit unserem Main-Skript verbinden. Dies erfordert einige Ergänzungen in der Main-Szene:

Verbinden Sie auf der Registerkarte "Node" das Signal start_game des HUD mit der Funktion new_game() des Main-Nodes, indem Sie "new_game" in die "Empfängermethode" im Fenster "Ein Signal mit einer Methode verbinden" eingeben. Stellen Sie sicher, dass das grüne Verbindungssymbol jetzt neben func new_game() im Skript angezeigt wird.

Aktualisieren Sie in new_game() die Punkteanzeige und zeigen Sie die Meldung "Get Ready" an:

$HUD.update_score(score)
$HUD.show_message("Get Ready")

In game_over() müssen wir die entsprechende HUD Funktion aufrufen:

$HUD.show_game_over()

Abschließend fügen Sie nachfolgendes in _on_ScoreTimer_timeout() hinzu, um die Anzeige mit den sich ändernden Punkten zu synchronisieren:

$HUD.update_score(score)

Jetzt können Sie spielen! Klicken Sie auf die Schaltfläche "Projekt abspielen (F5)". Sie werden aufgefordert, eine Hauptszene auszuwählen. Wählen Sie dann Main.tscn aus.

Alte Gegner entfernen

Wenn Sie bis zum "Game Over" spielen und dann ein neues Spiel starten, befinden sich die Gegner des vorherigen Spiel noch auf dem Bildschirm. Es wäre besser wenn diese beim Start eines neuen Spiels verschwinden würden. Wir benötigen einen Weg um alle Gegner zu entfernen und dies geschieht mit der "Gruppen" Funktion.

Wählen Sie in der Szene Mob den Wurzel-Node und klicken Sie auf die Registerkarte "Node" neben dem Inspektor (die gleiche Stelle, an der Sie die Signale des Nodes finden). Klicken Sie neben "Signale" auf "Gruppen", und Sie können einen neuen Gruppennamen eingeben und auf "Hinzufügen" klicken.

../../_images/group_tab.png

Nun sind alle Gegner in der Gruppe"mobs". Jetzt können wir folgende Zeile zur Funktion new_game() in Main hinzufügen:

get_tree().call_group("mobs", "queue_free")

Die Funktion call_group() ruft die benannte Funktion auf jedem Node in einer Gruppe auf - in diesem Fall sagen wir jedem Gegner, dass er sich selbst löschen soll.

Das Spiel ist nun zum größten Teil fertig. Im nächsten letzten Teil werden wir es etwas aufpolieren, indem wir einen Hintergrund, eine Musikschleife und einige Tastenkombinationen hinzufügen.