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

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

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

У головній сцені додайте новий вузол керування Control в якості нащадка Main та назвіть його UserInterface. Ви автоматично потрапите на 2D-екран, де зможете редагувати свій інтерфейс користувача (UI).

Додайте вузол Label і перейменуйте його на ScoreLabel.

image0

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

image1

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

Прогорніть униз до розділу Theme Overrides, розгорніть Colors та клацніть чорне поле поруч із пунктом Font Color, щоб тонувати текст.

image2

Виберіть темний тон, щоб він добре контрастував з 3D-сценою.

image3

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

image4

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

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

Ще раз виберіть вузол UserInterface. У Інспекторі створіть нову тему в Theme -> Theme.

image5

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

Зображення

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

Дивись також

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

Клацніть властивість Default Font і створіть новий DynamicFont.

image7

Розгорніть DynamicFont, натиснувши на нього, і розгорніть розділ Font. Там ви побачите порожнє поле Font Data.

image8

This one expects a font file like the ones you have on your computer. DynamicFont supports the following formats:

  • TrueType (.ttf)

  • OpenType (.otf)

  • Web Open Font Format 1 (.woff)

  • Web Open Font Format 2 (.woff2, since Godot 3.5)

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

Текст трохи малий. Установіть Settings -> Size у 22 пікселі, щоб збільшити розмір тексту.

image9

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

Давайте попрацюємо тепер над рахунком. Прикріпіть новий скрипт до ScoreLabel і визначте змінну score.

extends Label

var score = 0

Рахунок повинен збільшуватися на 1 кожен раз, коли ми чавимо монстра. Ми можемо використовувати їхній сигнал squashed, щоб знати, коли це станеться. Однак, оскільки ми створюємо екземпляри монстрів в коді, то не можемо зробити прив'язку у редакторі.

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

Відкрийте скрипт Main.gd. Якщо він все ще відкритий, ви можете натиснути на його назву в лівому стовпці редактора скриптів.

image10

Крім того, ви можете двічі клацнути файл Main.gd на панелі Файлова система.

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

func _on_MobTimer_timeout():
    #...
    # We connect the mob to the score label to update the score upon squashing one.
    mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")

Цей рядок означає, що коли монстр випромінює сигнал squashed, вузол ScoreLabel отримує його і викликає функцію _on_Mob_squashed().

Поверніться до скрипта ScoreLabel.gd, щоб визначити функцію _on_Mob_squashed().

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

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

Другий рядок використовує значення змінної score замість %s. При використанні цієї функції Godot автоматично перетворює значення в текст, що зручно при виведенні тексту в мітках, або при використанні функції print().

Дивись також

Ви можете дізнатися більше про форматування рядків тут: Форматований текст GDScript.

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

image11

Примітка

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

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

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

Поверніться до головної сцени Main, виберіть вузол UserInterface, додайте вузол ColorRect як дочірній вузол і назвіть його Retry. Цей вузол заповнює прямокутник рівномірним кольором і буде служити тінню для затемнення екрана.

Щоб він охоплював весь вид, можна скористатися меню Макет на панелі інструментів.

image12

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

image13

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

image14

Це пов'язано з тим, що вузли інтерфейсу користувача (всі з зеленою піктограмою) працюють з прив'язками та полями відносно розміру рамки предка. Тут вузол UserInterface має невеликий розмір, а вузол Retry обмежений ним.

Виберіть UserInterface і застосуйте до нього Макет -> Увесь прямокутник. Вузол Retry тепер повинен охоплювати увесь вид.

Давайте змінимо його колір, щоб він затемнив ігрову зону. Натисніть Retry, і в Інспекторі встановіть для його Color щось темне та прозоре. Для цього в палітрі кольорів перетягніть повзунок A ліворуч. Він керує альфа-каналом кольору, тобто його непрозорістю.

image15

Далі додайте мітку Label як дочірню частину Retry та дайте їй текст в Text "Натисніть клавішу Enter, щоб повторити спробу."

image16

Щоб перемістити його та закріпити в центрі екрана, застосуйте Макет -> За центром.

image17

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

Тепер ми можемо перейти до коду, щоб показати і приховати вузол Retry, коли гравець помирає і грає знову.

Відкрийте скрипт Main.gd. Спершу, ми хочемо приховати затінення на початку гри. Додайте цей рядок до функції _ready().

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

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

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

Нарешті, коли відображається вузол Retry, нам потрібно прослухати введення гравця і перезапустити гру, якщо він натискають enter. Для цього ми використовуємо вбудований зворотний виклик _unhandled_input().

Якщо гравець натиснув попередньо визначену дію введення ui_accept, і Retry відображається, перезавантажуємо поточну сцену.

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: автозавантаження.

Щоб відтворити звук, все, що вам потрібно зробити, це додати вузол AudioStreamPlayer до сцени та прикріпити до нього аудіофайл. Коли ви запустите сцену, він почне відтворюватися автоматично. Однак, коли ви перезавантажуєте сцену, звукові вузли також скидаються, і музика починається з самого початку.

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

Створіть нову сцену, перейшовши в меню Сцена та натиснувши кнопку Нова сцена.

image18

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

image19

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

image20

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

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

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

image21

Якщо ви запустите гру зараз, музика буде відтворюватися автоматично. І навіть коли ви програєте і почнете знову, вона продовжуватиметься.

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

image22

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

image23

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

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

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

extends Node

export (PackedScene) var mob_scene


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


func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        get_tree().reload_current_scene()


func _on_MobTimer_timeout():
    var mob = mob_scene.instance()

    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    mob_spawn_location.unit_offset = randf()

    var player_position = $Player.transform.origin
    mob.initialize(mob_spawn_location.translation, player_position)

    add_child(mob)
    mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")


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