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.

Узел CanvasLayer позволяет нам прорисовывать элементы нашего UI на слое поверх всей остальной игры, поэтому отображаемая информация не перекрывается никакими игровыми элементами, такими как игрок или мобы.

HUD должен отображать следующую информацию:

  • Счет, измененный ScoreTimer.

  • Сообщение, например "Game Over" или "Get Ready!"

  • Кнопка "Start", чтобы начать игру.

Основной узел для элементов UI — это Control. Чтобы создать наш UI, мы будем использовать два типа узлов Control: Label и Button.

Создайте следующие узлы в качестве потомков узла '' HUD'':

  • Label с именем ScoreLabel.

  • Label с именем Message.

  • Button с именем StartButton.

  • Timer с именем MessageTimer.

Нажмите на ScoreLabel и введите число в поле Text в Инспекторе. Стандартный шрифт для узлов Control мал и плохо масштабируется. В ресурсы игры включен файл шрифта под названием "Xolonium-Regular.ttf". Чтобы использовать этот шрифт, сделайте следующее:

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

../../_images/custom_font1.png
  1. Щелкните на "DynamicFont", который вы добавили, и в разделе Font > FontData выберите "Load" и выберите файл "Xolonium-Regular.ttf".

../../_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.

Примечание

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.

Организуйте узлы, как показано ниже. Нажмите кнопку "Макет", чтобы задать макет для узла Control:

../../_images/ui_anchor.png

Вы можете перетаскивать узлы, размещая их вручную, либо используйте следующие параметры для более точного размещения:

ScoreLabel

  • Макет : "Сверху по всей ширине"

  • Text : 0

  • Align : "Center"

Сообщение

  • Макет: "По центру по всей ширине"

  • Text: Уворачивайся от крипов!

  • Align : "Center"

  • Autowrap : "Вкл"

StartButton

  • Text : Старт

  • Макет: "Внизу посередине"

  • Margin :

    • Top: -200

    • Bottom: -100

В MessageTimer установите параметр Wait Time на 2, а параметр One Shot на значение "Вкл".

Теперь добавьте этот скрипт в HUD:

extends CanvasLayer

signal start_game

Сигнал start_game сообщает узлу Main, что кнопка была нажата.

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

Эта функция вызывается, когда мы хотим временно отобразить сообщение, такое как "Приготовьтесь".

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

Эта функция вызывается, когда игрок проигрывает. Она покажет надпись "Game Over" на 2 секунды, затем произойдет возврат к основному экрану, и после короткой паузы появится кнопка "Start".

Примечание

Если вам нужно сделать паузу на короткое время, то альтернативой использованию узла Timer является использование функции SceneTree create_timer(). Может быть очень полезно добавлять задержки наподобие таких, как в вышеприведенном коде, где нам хотелось бы подождать немного времени, прежде чем показывать кнопку "Start".

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

Эта функция вызывается из Main каждый раз, когда изменяется количество очков.

Присоедините сигнал timeout() из MessageTimer и сигнал pressed() из StartButton и добавьте следующий код к новым функциям:

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

func _on_MessageTimer_timeout():
    $Message.hide()

Подключение HUD к Main

Теперь, когда мы закончили создание сцены HUD, вернитесь к Main. Инстанцируйте сцену HUD в Main подобно тому, как вы это делали со сценой Player. Дерево сцены должно выглядеть так, поэтому убедитесь, что вы ничего не упустили:

../../_images/completed_main_scene.png

Теперь нам нужно подключить функционал HUD в наш Main-скрипт. Для этого потребуются некоторые дополнения к сцене Main:

Во вкладке "Узел" присоедините сигнал HUD start_game к функции узла Main new_game(), введя "new_game" в поле "Метод-приёмник" в окне "Подключить сигнал к методу" . Убедитесь, что в скрипте рядом с функцией func new_game() теперь появилась зелёная иконка подключения.

В new_game() обновим отображение счёта и выведем сообщение "Get Ready":

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

В game_over() нам нужно вызвать соответствующую функцию HUD:

$HUD.show_game_over()

Наконец добавьте это в _on_ScoreTimer_timeout(), чтобы синхронизировать отображение с изменением количества очков:

$HUD.update_score(score)

Теперь вы готовы к игре! Нажмите на кнопку "Запустить проект". Вам будет предложено выбрать основную сцену - выбирайте Main.tscn.

Удаляем старых крипов

Если вы играете до "Game Over", а затем сразу начинаете новую игру, то крипы из предыдущей игры могут все еще оставаться на экране. Было бы лучше, если бы все они исчезали в начале новой игры. Нам просто нужен способ сказать всем мобам, чтобы они удалились. Мы можем сделать это с помощью функции "group" ("группа").

В сцене Mob выберите корневой узел и нажмите вкладку "Узел" рядом с Инспектором (там же, где вы находите сигналы узла). Рядом с "Сигналы" нажмите "Группы", введите новое имя группы и нажмите "Добавить".

../../_images/group_tab.png

Теперь все мобы будут в группе "mobs". Затем мы можем добавить следующие строки к функции new_game() в Main:

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

Функция call_group() вызывает каждую именованную функцию на каждом узле в группе - в этом случае мы говорим каждому мобу удалять себя.

Игра на данный момент по большей части готова. В следующей и последней части мы немного отполируем её с помощью добавления фона, проигрывания музыки и нескольких сочетаний клавиш.