Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Heads up display

The final piece our game needs is a User Interface (UI) to display things like score, a "game over" message, and a restart button.

Create a new scene, click the "Other Node" button and add a CanvasLayer node named HUD. "HUD" stands for "heads-up display", an informational display that appears as an overlay on top of the game view.

Węzeł CanvasLayer pozwala nam narysować nasze elementy interfejsu użytkownika na warstwie będącą przed resztą gry, tak aby wyświetlane przez niego informacje nie były zakryte żadnymi elementami gry, takimi jak gracz lub przeciwnik.

HUD powinien wyświetlić następujące informacje:

  • Wynik, zmieniany przez ScoreTimer.

  • Wiadomości takie jak "Koniec Gry" lub "Przygotuj Się!"

  • Przycisk "Start" rozpoczyna grę.

Podstawowym węzłem elementów interfejsu użytkownika jest Control. Aby utworzyć nasz interfejs użytkownika, będziemy używać dwóch typów węzła ref:Control <class_Control> - Label oraz Button.

Utwórz je jako dzieci węzła HUD:

  • Label nazwany ScoreLabel.

  • Label nazwany Message.

  • Przycisk Button o nazwie StartButton.

  • Timer o nazwie MessageTimer.

Domyślna czcionka dla węzłów Control jest mała i nie skaluje się dobrze. W zasobach gry znajduje się plik czcionki o nazwie "Xolonium-Regular.ttf". Aby użyć tej czcionki, wykonaj następujące czynności dla każdego z trzech węzłów Control:

Under "Theme Overrides > Fonts", choose "Load" and select the "Xolonium-Regular.ttf" file.

../../_images/custom_font_load_font.webp

The font size is still too small, increase it to 64 under "Theme Overrides > Font Sizes". Once you've done this with the ScoreLabel, repeat the changes for the Message and StartButton nodes.

../../_images/custom_font_size.webp

Informacja

Anchors: Control nodes have a position and size, but they also have anchors. Anchors define the origin - the reference point for the edges of the node.

Arrange the nodes as shown below. You can drag the nodes to place them manually, or for more precise placement, use "Anchor Presets".

../../_images/ui_anchor.webp

ScoreLabel

  1. Add the text 0.

  2. Set the "Horizontal Alignment" and "Vertical Alignment" to Center.

  3. Choose the "Anchor Preset" Center Top.

Wiadomość

  1. Add the text Dodge the Creeps!.

  2. Set the "Horizontal Alignment" and "Vertical Alignment" to Center.

  3. Set the "Autowrap Mode" to Word, otherwise the label will stay on one line.

  4. Under "Control - Layout/Transform" set "Size X" to 480 to use the entire width of the screen.

  5. Choose the "Anchor Preset" Center.

StartButton

  1. Add the text Start.

  2. Under "Control - Layout/Transform", set "Size X" to 200 and "Size Y" to 100 to add a little bit more padding between the border and text.

  3. Choose the "Anchor Preset" Center Bottom.

  4. Under "Control - Layout/Transform", set "Position Y" to 580.

Ta funkcja jest wywoływana, gdy chcemy tymczasowo wyświetlić komunikat, taki jak "Przygotuj się". W MessageTimer ustaw Czas oczekiwania na 2 i ustaw właściwość Jednokrotny na "Włączone".

Teraz dodaj ten skrypt do HUD:

extends CanvasLayer

# Notifies `Main` node that the button has been pressed
signal start_game

We now want to display a message temporarily, such as "Get Ready", so we add the following code

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

We also need to process what happens when the player loses. The code below will show "Game Over" for 2 seconds, then return to the title screen and, after a brief pause, show the "Start" button.

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

    $Message.text = "Dodge the Creeps!"
    $Message.show()
    # Make a one-shot timer and wait for it to finish.
    await get_tree().create_timer(1.0).timeout
    $StartButton.show()

Ta funkcja jest wywoływana, gdy gracz przegrywa. Przez 2 sekundy na ekranie będzie wyświetlał się napis "Game Over" (Koniec Gry), następnie powróci do ekranu tytułowego i pojawi się przycisk "Start".

Informacja

When you need to pause for a brief time, an alternative to using a Timer node is to use the SceneTree's create_timer() function. This can be very useful to add delays such as in the above code, where we want to wait some time before showing the "Start" button.

Add the code below to HUD to update the score

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

Connect the pressed() signal of StartButton and the timeout() signal of MessageTimer, and add the following code to the new functions:

func _on_start_button_pressed():
    $StartButton.hide()
    start_game.emit()

func _on_message_timer_timeout():
    $Message.hide()

Podłączenie HUD do Main

Teraz, kiedy już stworzyłeś scenę HUD, zapisz ją i wróć do Main`. Stwórz instancję HUD w Main tak, jak zrobiłeś to Player scenę i umieść ją na dole drzewa. Pełne drzewo powinno wyglądać tak, więc upewnij się, że niczego nie przegapiłeś:

../../_images/completed_main_scene.webp

Teraz musimy podłączyć funkcję HUD do naszego skryptu Main. Wymaga to paru poprawek do sceny Main:

In the Node tab, connect the HUD's start_game signal to the new_game() function of the Main node by clicking the "Pick" button in the "Connect a Signal" window and selecting the new_game() method or type "new_game" below "Receiver Method" in the window. Verify that the green connection icon now appears next to func new_game() in the script.

W new_game(), zaktualizuj wyświetlany wynik i pokaż komunikat "Przygotuj się":

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

W game_over() musimy wywołać odpowiednią funkcję w HUD:

$HUD.show_game_over()

Finally, add this to _on_score_timer_timeout() to keep the display in sync with the changing score:

$HUD.update_score(score)

Ostrzeżenie

Remember to remove the call to new_game() from _ready() if you haven't already, otherwise your game will start automatically.

Now you're ready to play! Click the "Play the Project" button. You will be asked to select a main scene, so choose main.tscn.

Removing old creeps

If you play until "Game Over" and then start a new game right away, the creeps from the previous game may still be on the screen. It would be better if they all disappeared at the start of a new game. We just need a way to tell all the mobs to remove themselves. We can do this with the "group" feature.

In the Mob scene, select the root node and click the "Node" tab next to the Inspector (the same place where you find the node's signals). Next to "Signals", click "Groups" and you can type a new group name and click "Add".

../../_images/group_tab.webp

Now all mobs will be in the "mobs" group. We can then add the following line to the new_game() function in Main:

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

The call_group() function calls the named function on every node in a group - in this case we are telling every mob to delete itself.

The game's mostly done at this point. In the next and last part, we'll polish it a bit by adding a background, looping music, and some keyboard shortcuts.