Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

Рахунок та повтор

У цій частині ми додамо рахунок, відтворення музики та можливість перезапустити гру.

Ми повинні відстежувати поточний рахунок в змінній і відображати її на екрані за допомогою мінімального інтерфейсу. Для цього ми будемо використовувати текстову мітку.

In the main scene, add a new child node Control to Main and name it UserInterface. You will automatically be taken to the 2D screen, where you can edit your User Interface (UI).

Додайте вузол Label і назвіть його ScoreLabel

image1

У Інспекторі в Label Text введіть текст "Score: 0" (або "Рахунок: 0", якщо шрифт підтримує кирилицю).

image2

Крім того, текст за замовчуванням білий, як і фон нашої гри. Нам потрібно змінити його колір, щоб побачити під час виконання.

Прокрутіть вниз до Перевизначення теми, розгорніть Кольори та увімкніть Колір шрифту, щоб відтінити текст у чорний колір (який добре контрастує з білою 3D-сценою)

image3

Нарешті, клацніть і перетягніть текст у вікні перегляду, щоб перемістити його до верхнього лівого кута.

image4

The UserInterface node allows us to group our UI in a branch of the scene tree and use a theme resource that will propagate to all its children. We'll use it to set our game's font.

Створення теми інтерфейсу користувача

Once again, select the UserInterface node. In the Inspector, create a new theme resource in Theme -> Theme.

image5

Натисніть на неї, щоб відкрити редактор тем у нижній панелі. Він дає вам попередній перегляд того, як будуть виглядати всі вбудовані елементи інтерфейсу користувача у вашій темі.

Зображення

За замовчуванням тема має лише одну властивість, Default Font (Шрифт за замовчуванням).

Дивись також

Ви можете додати більше властивостей до теми для розробки складних інтерфейсів користувача, але це виходить за рамки цієї серії. Щоб дізнатися більше про створення та редагування тем гляньте на Introduction to GUI skinning.

Тут має бути файл шрифту, подібний до тих, які є на вашому комп'ютері. DynamicFont підтримує наступні формати.

In the FileSystem dock, expand the fonts directory and click and drag the Montserrat-Medium.ttf file we included in the project onto the Default Font. The text will reappear in the theme preview.

The text is a bit small. Set the Default Font Size to 22 pixels to increase the text's size.

image7

Відстеження рахунку

Let's work on the score next. Attach a new script to the ScoreLabel and define the score variable.

extends Label

var score = 0

The score should increase by 1 every time we squash a monster. We can use their squashed signal to know when that happens. However, because we instantiate monsters from the code, we cannot connect the mob signal to the ScoreLabel via the editor.

Замість цього, ми повинні закладати зв'язок в коді кожен раз, коли створюємо монстра.

Open the script main.gd. If it's still open, you can click on its name in the script editor's left column.

image8

Alternatively, you can double-click the main.gd file in the FileSystem dock.

У нижній частині функції _on_MobTimer_timeout() додайте наступний рядок:

func _on_mob_timer_timeout():
    #...
    # We connect the mob to the score label to update the score upon squashing one.
    mob.squashed.connect($UserInterface/ScoreLabel._on_mob_squashed.bind())

This line means that when the mob emits the squashed signal, the ScoreLabel node will receive it and call the function _on_mob_squashed().

Head back to the ScoreLabel.gd script to define the _on_mob_squashed() callback function.

В ній ми збільшуємо рахунок та оновлюємо відображуваний текст.

func _on_mob_squashed():
    score += 1
    text = "Score: %s" % score

The second line uses the value of the score variable to replace the placeholder %s. When using this feature, Godot automatically converts values to string text, which is convenient when outputting text in labels or when using the print() function.

Дивись також

You can learn more about string formatting here: Форматований текст GDScript. In C#, consider using string interpolation with "$".

Тепер ви можете зіграти в гру і розчавити кілька ворогів, щоб побачити збільшення рахунку.

image9

Примітка

У складній грі ви можете повністю відокремити свій інтерфейс користувача від ігрового світу. У цьому випадку ви не будете відстежувати рахунок через мітку. Замість цього ви можете зберегти його в окремому, виділеному об'єкті. Але в прототипах, або простих проектах, краще, щоб ваш код був простим. Програмування завжди є балансуванням.

Повторна спроба гри

Тепер ми додамо можливість грати знову після смерті. Коли гравець помре, ми відобразимо повідомлення на екрані і дочекаємося підтвердження.

Head back to the main.tscn scene, select the UserInterface node, add a child node ColorRect, and name it Retry. This node fills a rectangle with a uniform color and will serve as an overlay to darken the screen.

To make it span over the whole viewport, you can use the Anchor Preset menu in the toolbar.

image10

Відкрийте його та застосуйте команду Увесь прямокутник.

image11

Nothing happens. Well, almost nothing; only the four green pins move to the corners of the selection box.

image12

This is because UI nodes (all the ones with a green icon) work with anchors and margins relative to their parent's bounding box. Here, the UserInterface node has a small size and the Retry one is limited by it.

Select the UserInterface and apply Anchor Preset -> Full Rect to it as well. The Retry node should now span the whole viewport.

Let's change its color so it darkens the game area. Select Retry and in the Inspector, set its Color to something both dark and transparent. To do so, in the color picker, drag the A slider to the left. It controls the color's Alpha channel, that is to say, its opacity/transparency.

image13

Next, add a Label as a child of Retry and give it the Text "Press Enter to retry." To move it and anchor it in the center of the screen, apply Anchor Preset -> Center to it.

image14

Кодування можливості повторної спроби

We can now head to the code to show and hide the Retry node when the player dies and plays again.

Open the script main.gd. First, we want to hide the overlay at the start of the game. Add this line to the _ready() function.

func _ready():
    $UserInterface/Retry.hide()

Потім, коли гравець отримує удар, ми показуємо затінення.

func _on_player_hit():
    #...
    $UserInterface/Retry.show()

Finally, when the Retry node is visible, we need to listen to the player's input and restart the game if they press enter. To do this, we use the built-in _unhandled_input() callback, which is triggered on any input.

If the player pressed the predefined ui_accept input action and Retry is visible, we reload the current scene.

func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        # This restarts the current scene.
        get_tree().reload_current_scene()

Функція get_tree() дає нам доступ до глобального об'єкта SceneTree, що дозволяє перезавантажити і перезапустити поточну сцену.

Додавання музики

Щоб додати музику, яка постійно відтворюється у фоновому режимі, ми збираємося використовувати іншу функцію в Godot: автозавантаження.

To play audio, all you need to do is add an AudioStreamPlayer node to your scene and attach an audio file to it. When you start the scene, it can play automatically. However, when you reload the scene, like we do to play again, the audio nodes are also reset, and the music starts back from the beginning.

Ви можете використовувати функцію автозавантаження, щоб Godot автоматично завантажував вузол, або сцену, на початку гри, за межами поточної сцени. Ви також можете використовувати його для створення глобально доступних об'єктів.

Створіть нову сцену, перейшовши до меню Сцена і натиснувши Нова сцена або за допомогою піктограми + поруч з поточною відкритою сценою.

image15

Натисніть кнопку Інший вузол, щоб створити AudioStreamPlayer і перейменувати його на MusicPlayer.

image16

Ми включили музичний саундтрек House In a Forest Loop.ogg до каталогу art/. Клацніть і перетягніть його до властивості Stream у Інспекторі. Крім того, увімкніть автовідтворення Autoplay, щоб музика відтворювалася автоматично на початку гри.

image17

Збережіть сцену під назвою MusicPlayer.tscn.

Для автоматичного завантаження сцени перейдіть до меню Проєкт -> Параметри проєкту на вкладку AutoLoad (Автозавантаження).

У полі Path потрібно ввести шлях до сцени. Клацніть піктограму папки, щоб відкрити файловий браузер, і двічі клацніть на MusicPlayer.tscn. Потім натисніть кнопку Додати праворуч, щоб зареєструвати вузол.

image18

MusicPlayer.tscn now loads into any scene you open or play. So if you run the game now, the music will play automatically in any scene.

Перш ніж ми завершимо цей урок, ось короткий погляд на те, як все працює всередині. Коли ви запускаєте гру, ваша панель Сцена змінюється, щоб дати вам дві вкладки: Віддалений і Локальний.

image19

Вкладка Віддалений дозволяє візуалізувати дерево вузлів вашої запущеної гри. Там ви побачите головний вузол Main і все, що містить сцена, і вставлені екземпляри монстрів внизу.

image20

У верхній частині знаходиться автозавантажуваний MusicPlayer і вузол root, який є вікном перегляду вашої гри.

І це все для цього уроку. У наступній частині ми додамо анімацію, щоб гра виглядала набагато приємніше.

Ось повний скрипт main.gd для довідки.

extends Node

@export var mob_scene: PackedScene

func _ready():
    $UserInterface/Retry.hide()


func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on the SpawnPath.
    # We store the reference to the SpawnLocation node.
    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    # And give it a random offset.
    mob_spawn_location.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

    # We connect the mob to the score label to update the score upon squashing one.
    mob.squashed.connect($UserInterface/ScoreLabel._on_mob_squashed.bind())

func _on_player_hit():
    $MobTimer.stop()
    $UserInterface/Retry.show()

func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        # This restarts the current scene.
        get_tree().reload_current_scene()