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, 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.

Il nodo :ref:'CanvasLayer <class_CanvasLayer>' ci consente di disegnare i nostri elementi dell'interfaccia utente su un livello sopra il resto del gioco, in modo che le informazioni visualizzate non vengano coperte da elementi di gioco come il giocatore o i mob.

L'HUD deve mostrare le seguenti informazioni:

  • Punteggio, modificato da ''ScoreTimer''.

  • Un messaggio, ad esempio "Game Over" o "Get Ready!"

  • Un pulsante "Start" per iniziare il gioco.

Il nodo di base per gli elementi dell'interfaccia utente è :ref:'Control <class_Control>'. Per creare l'interfaccia utente, useremo due tipi di nodi :ref:'Control <class_Control>': :ref:'Label <class_Label>' e :ref:'Button <class_Button>'.

Crea i seguenti come figli del nodo HUD:

  • Label denominato ScoreLabel.

  • Label denominato Message.

  • Button denominato StartButton.

  • Timer denominato MessageTimer.

Fare clic su ScoreLabel e digitare un numero nel campo Testo nell'Inspector. Il tipo di font predefinito per i nodi Control è piccolo e non scala bene. C'è un file di font incluso nelle risorse di gioco chiamato "Xolonium-Regular.ttf". Per utilizzare questo font, eseguire le operazioni seguenti:

  1. Under Theme overrides > Fonts click on the empty box and select "New DynamicFont"

../../_images/custom_font1.png
  1. Click on the "DynamicFont" you added, and under Font > FontData, choose "Load" and select the "Xolonium-Regular.ttf" file.

../../_images/custom_font2.png

Set the "Size" property under Settings, 64 works well.

../../_images/custom_font3.png

Once you've done this on the ScoreLabel, you can click the down arrow next to the Font property and choose "Copy", then "Paste" it in the same place on the other two Control nodes.

Nota

Anchors and Margins: Control nodes have a position and size, but they also have anchors and margins. Anchors define the origin - the reference point for the edges of the node. Margins update automatically when you move or resize a control node. They represent the distance from the control node's edges to its anchor.

Disponi i nodi come illustrato di seguito. Fare clic sul pulsante "Layout" per impostare l'ancoraggio di un nodo Control:

../../_images/ui_anchor.png

È possibile trascinare i nodi per posizionarli manualmente o, per un posizionamento più preciso, utilizzare le seguenti impostazioni:

ScoreLabel

  • Layout : "Top Wide"

  • Testo : 0

  • Allinea: "Centro"

Messaggio

  • Layout : "HCenter Wide"

  • Text : Dodge the Creeps!

  • Allinea: "Centro"

  • Autowrap : "On"

StartButton

  • Testo : Start

  • *Layout *: "Center Bottom"

  • Margine :

    • In alto: -200

    • In basso: -100

Sul MessageTimer, imposta Tempo d'attesa su 2 e imposta la proprietà One Shot su "On".

Ora aggiungi questo script a ''HUD'':

extends CanvasLayer

signal start_game

Il segnale start_game dice al nodo Main che il pulsante è stato premuto.

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

Questa funzione è chiamata quando si desidera visualizzare temporaneamente un messaggio, ad esempio "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()

Questa funzione viene chiamata quando il giocatore perde. Mostra "Game Over" per 2 secondi, poi ritorna alla schermata del titolo e, dopo una breve pausa, mostra il pulsante "Start".

Nota

Quando è necessario sospendere per un breve periodo di tempo, un'alternativa all'utilizzo di un nodo Timer consiste nell'utilizzare la funzione create_timer() di SceneTree. Questo può essere molto utile per ritardare, come nel codice sopra, dove vogliamo aspettare un po 'di tempo prima di mostrare il pulsante "Start".

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

Questa funzione è chiamata da Main ogni volta che il punteggio cambia.

Collega il segnale timeout() di MessageTimer e il segnale pressed() di StartButton e aggiungi il seguente codice alle nuove funzioni:

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

func _on_MessageTimer_timeout():
    $Message.hide()

Collegamento di HUD a Main

Ora che abbiamo finito di creare la scena HUD, tornare a Main. Istanza la scena HUD in Main come hai fatto per la scena Player. L'albero della scena completo dovrebbe essere simile a questo, quindi assicurati di non aver tralasciato nulla:

../../_images/completed_main_scene.png

Ora dobbiamo collegare la funzionalità HUD al nostro script Main. Ciò richiede alcune aggiunte alla scena Main:

Nella scheda Node, collegare il segnale start_game dell'HUD alla funzione new_game() del nodo principale digitando "new_game" nel "Receiver Method" nella finestra "Connect a Signal". Verificare che l'icona verde di connessione appaia ora accanto a func new_game() nello script.

In new_game(), aggiorna la visualizzazione del punteggio e mostra il messaggio "Get Ready":

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

In game_over() dobbiamo chiamare la corrispondente funzione HUD:

$HUD.show_game_over()

Infine, aggiungi questo a _on_ScoreTimer_timeout() per mantenere il display sincronizzato con il punteggio che cambia:

$HUD.update_score(score)

Ora sei pronto per giocare! Fai clic sul pulsante "Riproduci il progetto". Ti verrà chiesto di selezionare una scena principale, quindi scegli Main.tscn.

Rimuovere i vecchi "creeps"

Se giochi fino al "Game Over" e inizi subito una nuova partita, i creeps del gioco precedente potrebbero essere ancora sullo schermo. Sarebbe meglio se scomparissero tutti all'inizio di una nuova partita. Abbiamo solo bisogno di un modo per dire a tutti i mob di sparire. Possiamo farlo con la funzione "gruppo".

Nella scena Mob, selezionare il nodo radice e cliccare sulla scheda "Nodo" accanto all'Inspector (lo stesso posto dove si trovano i segnali del nodo). Accanto a "Segnali", cliccare su "Gruppi" e si può digitare un nuovo nome per il gruppo e cliccare su "Aggiungi".

../../_images/group_tab.png

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")

La funzione call_group() chiama la funzione cosi denominata su ogni nodo di un gruppo - cosi facendo diciamo ad ogni mob di cancellarsi.

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.