mirror of
https://github.com/godotengine/godot-docs.git
synced 2025-12-31 17:49:03 +03:00
315 lines
9.8 KiB
ReStructuredText
315 lines
9.8 KiB
ReStructuredText
.. _doc_your_first_2d_game_heads_up_display:
|
|
|
|
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 :ref:`CanvasLayer <class_CanvasLayer>` node named
|
|
``HUD``. "HUD" stands for "heads-up display", an informational display that
|
|
appears as an overlay on top of the game view.
|
|
|
|
The :ref:`CanvasLayer <class_CanvasLayer>` node lets us draw our UI elements on
|
|
a layer above the rest of the game, so that the information it displays isn't
|
|
covered up by any game elements like the player or mobs.
|
|
|
|
The HUD needs to display the following information:
|
|
|
|
- Score, changed by ``ScoreTimer``.
|
|
- A message, such as "Game Over" or "Get Ready!"
|
|
- A "Start" button to begin the game.
|
|
|
|
The basic node for UI elements is :ref:`Control <class_Control>`. To create our
|
|
UI, we'll use two types of :ref:`Control <class_Control>` nodes: :ref:`Label
|
|
<class_Label>` and :ref:`Button <class_Button>`.
|
|
|
|
Create the following as children of the ``HUD`` node:
|
|
|
|
- :ref:`Label <class_Label>` named ``ScoreLabel``.
|
|
- :ref:`Label <class_Label>` named ``Message``.
|
|
- :ref:`Button <class_Button>` named ``StartButton``.
|
|
- :ref:`Timer <class_Timer>` named ``MessageTimer``.
|
|
|
|
Click on the ``ScoreLabel`` and type a number into the ``Text`` field in the
|
|
Inspector. The default font for ``Control`` nodes is small and doesn't scale
|
|
well. There is a font file included in the game assets called
|
|
"Xolonium-Regular.ttf". To use this font, do the following:
|
|
|
|
Under "Theme Overrides > Fonts", choose "Load" and select the "Xolonium-Regular.ttf" file.
|
|
|
|
.. image:: img/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.
|
|
|
|
.. image:: img/custom_font_size.webp
|
|
|
|
.. note:: **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".
|
|
|
|
.. image:: img/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``.
|
|
|
|
Message
|
|
-------
|
|
|
|
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``.
|
|
|
|
On the ``MessageTimer``, set the ``Wait Time`` to ``2`` and set the ``One Shot``
|
|
property to "On".
|
|
|
|
Now add this script to ``HUD``:
|
|
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
extends CanvasLayer
|
|
|
|
# Notifies `Main` node that the button has been pressed
|
|
signal start_game
|
|
|
|
.. code-tab:: csharp
|
|
|
|
using Godot;
|
|
|
|
public partial class HUD : CanvasLayer
|
|
{
|
|
// Don't forget to rebuild the project so the editor knows about the new signal.
|
|
|
|
[Signal]
|
|
public delegate void StartGameEventHandler();
|
|
}
|
|
|
|
We now want to display a message temporarily,
|
|
such as "Get Ready", so we add the following code
|
|
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
func show_message(text):
|
|
$Message.text = text
|
|
$Message.show()
|
|
$MessageTimer.start()
|
|
|
|
.. code-tab:: csharp
|
|
|
|
public void ShowMessage(string text)
|
|
{
|
|
var message = GetNode<Label>("Message");
|
|
message.Text = text;
|
|
message.Show();
|
|
|
|
GetNode<Timer>("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.
|
|
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
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()
|
|
|
|
.. code-tab:: csharp
|
|
|
|
async public void ShowGameOver()
|
|
{
|
|
ShowMessage("Game Over");
|
|
|
|
var messageTimer = GetNode<Timer>("MessageTimer");
|
|
await ToSignal(messageTimer, Timer.SignalName.Timeout);
|
|
|
|
var message = GetNode<Label>("Message");
|
|
message.Text = "Dodge the Creeps!";
|
|
message.Show();
|
|
|
|
await ToSignal(GetTree().CreateTimer(1.0), SceneTreeTimer.SignalName.Timeout);
|
|
GetNode<Button>("StartButton").Show();
|
|
}
|
|
|
|
.. note:: 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
|
|
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
func update_score(score):
|
|
$ScoreLabel.text = str(score)
|
|
|
|
.. code-tab:: csharp
|
|
|
|
public void UpdateScore(int score)
|
|
{
|
|
GetNode<Label>("ScoreLabel").Text = score.ToString();
|
|
}
|
|
|
|
Connect the ``pressed()`` signal of ``StartButton`` and the ``timeout()``
|
|
signal of ``MessageTimer`` to the ``HUD`` node, and add the following code to the new functions:
|
|
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
func _on_start_button_pressed():
|
|
$StartButton.hide()
|
|
start_game.emit()
|
|
|
|
func _on_message_timer_timeout():
|
|
$Message.hide()
|
|
|
|
.. code-tab:: csharp
|
|
|
|
// We also specified this function name in PascalCase in the editor's connection window.
|
|
private void OnStartButtonPressed()
|
|
{
|
|
GetNode<Button>("StartButton").Hide();
|
|
EmitSignal(SignalName.StartGame);
|
|
}
|
|
|
|
// We also specified this function name in PascalCase in the editor's connection window.
|
|
private void OnMessageTimerTimeout()
|
|
{
|
|
GetNode<Label>("Message").Hide();
|
|
}
|
|
|
|
Connecting HUD to Main
|
|
----------------------
|
|
|
|
Now that we're done creating the ``HUD`` scene, go back to ``Main``. Instance
|
|
the ``HUD`` scene in ``Main`` like you did the ``Player`` scene. The scene tree
|
|
should look like this, so make sure you didn't miss anything:
|
|
|
|
.. image:: img/completed_main_scene.webp
|
|
|
|
Now we need to connect the ``HUD`` functionality to our ``Main`` script. This
|
|
requires a few additions to the ``Main`` scene:
|
|
|
|
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.
|
|
|
|
In ``new_game()``, update the score display and show the "Get Ready" message:
|
|
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
$HUD.update_score(score)
|
|
$HUD.show_message("Get Ready")
|
|
|
|
.. code-tab:: csharp
|
|
|
|
var hud = GetNode<HUD>("HUD");
|
|
hud.UpdateScore(_score);
|
|
hud.ShowMessage("Get Ready!");
|
|
|
|
In ``game_over()`` we need to call the corresponding ``HUD`` function:
|
|
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
$HUD.show_game_over()
|
|
|
|
.. code-tab:: csharp
|
|
|
|
GetNode<HUD>("HUD").ShowGameOver();
|
|
|
|
Finally, add this to ``_on_score_timer_timeout()`` to keep the display in sync
|
|
with the changing score:
|
|
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
$HUD.update_score(score)
|
|
|
|
.. code-tab:: csharp
|
|
|
|
GetNode<HUD>("HUD").UpdateScore(_score);
|
|
|
|
.. warning::
|
|
|
|
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.
|
|
|
|
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" to open the group overview
|
|
and the "+" button to open the "Create New Group" dialog.
|
|
|
|
.. image:: img/group_tab.webp
|
|
|
|
Name the group ``mobs`` and click "ok" to add a new scene group.
|
|
|
|
.. image:: img/add_group_dialog.webp
|
|
|
|
Now all mobs will be in the "mobs" group.
|
|
|
|
.. image:: img/scene_group_mobs.webp
|
|
|
|
We can then add the following line to the ``new_game()`` function in ``Main``:
|
|
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
|
|
get_tree().call_group("mobs", "queue_free")
|
|
|
|
.. code-tab:: csharp
|
|
|
|
// Note that for calling Godot-provided methods with strings,
|
|
// we have to use the original Godot snake_case name.
|
|
GetTree().CallGroup("mobs", Node.MethodName.QueueFree);
|
|
|
|
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.
|