*Вы можете найти ссылки на предыдущие части в нижней части этого руководства.

С нашими вражескими препятствиями и пикапами, мы должны быть в состоянии перейти на следующий уровень, когда мы достигнем вершины нашей сцены. Для этого мы можем создать простую дверь, которая вызовет отображение нашего меню прогресса уровня, что позволит нашему игроку перезапустить свой текущий уровень или перейти на следующий.

ЧТО ВЫ УЗНАЕТЕ В ЭТОЙ ЧАСТИ:

  • Создание пользовательского интерфейса.
  • Как добавить переход между сценами с помощью ресурсов PackedScene.
  • Как работать с объектом String.
  • Как работать с объектом Time.

ШАГ 1: НАСТРОЙКА СЦЕНЫ

В своем проекте создайте новую сцену с узлом Area2D в ее корне. Добавьте к этой сцене узел CollisionShape2D, узел Sprite2D и узел CanvasLayer и сохраните его в папке «Сцены» как «LevelDoor». Переименуйте узлы, как показано на изображении ниже.

Измените текстуру вашего узла Sprite2D на «res://Assets/Kings and Pigs/Sprites/11-Door/Idle.png» и измените CollisionShape следующим образом:

Если вы размещаете свою дверь в основной сцене, она должна быть размещена в таком месте, где игрок может столкнуться с фигурой столкновения, не будучи заблокированным.

ШАГ 2. ИНТЕРФЕЙС СЦЕНЫ

В следующей части будет довольно много работы с пользовательским интерфейсом, так что пристегнитесь! В узел слоя холста пользовательского интерфейса добавьте новый узел ColorRect. Переименуйте его в «Меню».

В меню ColorRect добавьте новый ColorRect с именем «TimeCompleted», который содержит два узла Label в качестве дочерних элементов. Это покажет время в секундах, которое потребовалось нашему игроку для прохождения уровня.

Дважды продублируйте TimeCompleted ColorRect, чтобы у нас были элементы для отображения окончательного результата и рейтинга.

К вашему узлу Меню добавьте два узла Кнопка. Эти кнопки будут использоваться для продолжения или перезапуска до определенных нами уровней. Назовите один RestartButton, а другой ContinueButton.

Снова выберите узел Menu и добавьте узел Label и узел ColorRect. Переименуйте узел ColorRect в «Container» и переместите свои кнопки и три других ColorRect в этот узел.

К узлу Container добавьте узел Label и переместите его наверх. Добавьте еще один ColorRect и измените его имя на «Граница».

Затем к узлу Menu добавьте узел Tilemap с двумя узлами AnimatedSprite2D. Назовите один узел AnimatedSprite2D «AnimatedKing», а другой — «AnimatedKing». Это просто визуальные эффекты.

Пока это должно выглядеть так:

1. Меню

Я проведу вас сверху вниз. Выберите узел меню и измените его свойство цвета на #232f2b. Измените предустановку привязки на полный прямоугольник.

2. Контейнер

Затем выберите узел Container и измените его свойство цвета на #30403a. Измените его предустановку привязки на полный прямоугольник, а также его размер (x: 1078, y: 590) и положение (x: 35, y: 27).

3. Ярлык

Выберите узел «Ярлык» и отправьте текст «Уровень завершен!». Измените предустановку привязки на center-top, а также его размер (x: 504, y: 39) и положение (x: 269, y: 80). Также измените его шрифт на «QuinqueFive» и размер на «30».

4. Граница

Выберите узел Border и измените его свойство цвета на #455851. Измените его привязку на центр, а также его размер (x: 600, y: 350) и положение (x: 239, y: 145).

5. Время выполнения

Выберите узел TimeCompleted и измените его свойство цвета на #ffffff00. Измените его предустановку привязки на h-center-wide, а также его размер (x: 1078, y: 0) и положение (x: 50, y: 200).

6. TimeCompleted › Метка

Выберите узел Label внутри узла TimeCompleted и измените его текст на «Time:». Измените его предустановку привязки на центральное левое, а также его размер (x: 336, y: 27) и положение (x: 210, y: 0). Также измените его шрифт на «QuinqueFive» и размер на «20».

7. TimeCompleted › Значение

Выберите узел Value внутри узла TimeCompleted и измените его текст на «0». Измените его предустановку привязки на центр справа, а также его размер (x: 400, y: 27) и положение (x: 410, y: 0). Также измените его шрифт на «QuinqueFive» и размер на «20». Вы также хотите изменить цвет шрифта на #0000005a (под Theme Overrides › Colors › Font Colors).

8. Оценка

Выберите узел Score и измените его свойство цвета на #ffffff00. Измените его предустановку привязки на h-center-wide, а также его размер (x: 1078, y: 0) и положение (x: 50, y: 220).

9. Оценка › Метка

Выберите узел «Ярлык» внутри узла «Оценка» и измените его текст на «Оценка:». Измените его предустановку привязки на центральное левое, а также его размер (x: 144, y: 27) и положение (x: 210, y: 40). Также измените его шрифт на «QuinqueFive» и размер на «20».

10. Оценка › Ценность

Выберите узел «Значение» внутри узла «Оценка» и измените его текст на «0». Измените его предустановку привязки на центр справа, а также его размер (x: 400, y: 27) и положение (x: 410, y: 40). Также измените его шрифт на «QuinqueFive» и размер на «20». Вы также хотите изменить цвет шрифта на #0000005a (под Theme Overrides › Colors › Font Colors).

11. Рейтинг

Выберите свой узел рейтинга и измените его свойство цвета на #ffffff00. Измените его предустановку привязки на h-center-wide, а также его размер (x: 1078, y: 0) и положение (x: 50, y: 240).

12. Рейтинг › Ярлык

Выберите узел «Ярлык» внутри узла «Рейтинг» и измените его текст на «Рейтинг:». Измените его предустановку привязки на центральное левое, а также его размер (x: 144, y: 27) и положение (x: 210, y: 80). Также измените его шрифт на «QuinqueFive» и размер на «20».

13. Рейтинг › Ценность

Выберите узел «Значение» внутри узла «Рейтинг» и измените его текст на «0». Измените его предустановку привязки на центр справа, а также его размер (x: 400, y: 27) и положение (x: 410, y: 80). Также измените его шрифт на «QuinqueFive» и размер на «20». Вы также хотите изменить цвет шрифта на #0000005a (под Theme Overrides › Colors › Font Colors).

14. Кнопка "Продолжить"

Выберите узел ContinueButton и измените его текст на «Продолжить». Измените предустановку якоря на нижний левый, а также его размер (x: 260, y: 90) и положение (x: 265, y: 385). Также измените его шрифт на «QuinqueFive» и размер на «25».

15. Кнопка перезапуска

Выберите узел RestartButton и измените его текст на «Restart». Измените предустановку якоря на нижний левый, а также его размер (x: 270, y: 90) и положение (x: 545, y: 385). Также измените его шрифт на «QuinqueFive» и размер на «25».

16. TileMap

Теперь для вашего узла TileMap назначьте лист тайлов «Terrain.png» вашему ресурсу TileSet и нарисуйте формы башни.

Это может выглядеть примерно так:

17. AnimatedPig

Создайте для него новую анимацию с ресурсом «res://Assets/Kings and Pigs/Sprites/04-Pig Throwing a Box/Idle (26x30).png». Добавьте все кадры из таблицы спрайтов. Измените значение FPS на 8 и оставьте цикл включенным. Щелкните значок рядом с корзиной, чтобы включить эту анимацию при запуске игры. Это будет воспроизводить эту анимацию на свинье, когда отображается меню.

18. AnimatedKing

Создайте для него новую анимацию с ресурсом «res://Assets/Kings and Pigs/Sprites/01-King Human/Idle (78x58)2.png». Добавьте все кадры из таблицы спрайтов. Измените значение FPS на 11 и оставьте цикл включенным. Щелкните значок рядом с корзиной, чтобы включить эту анимацию при запуске игры. Это будет воспроизводить эту анимацию на свинье, когда отображается меню.

Полный слой пользовательского интерфейса теперь должен выглядеть примерно так:

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

К этому узлу AnimationPlayer добавьте новую анимацию с именем «ui_visibility».

Добавьте новое «Свойство дорожки» и назначьте его узлу «Меню».

Мы хотим, чтобы этот узел меню превратился из невидимого в видимый, и поэтому нам нужно изменить его значение modulate. Значение модуляции относится к цвету, применяемому к текстурам в этом CanvasItem. Мы изменим альфа-значение модуляции, которое является прозрачностью узла.

Добавьте к этой анимации два ключевых кадра — один в точке 0 и один в точке 0,5.

Измените значение цвета ключевого кадра в точке 0 на «ffffff00». Это сделает его прозрачным.

Измените значение цвета ключевого кадра в точке 0,5 на «ffffff». Это сделает его видимым.

Также мы хотим изменить режим обновления с дискретного на непрерывный. Наш режим обновления сообщает нашему треку анимации, как часто он должен обновлять наше свойство анимации. Непрерывный режим обновляет свойство в каждом кадре. Это заставит наш узел медленно переходить от невидимого к видимому.

Наконец, измените длину анимации на 0,5.

Теперь, если вы воспроизведете свою анимацию, ваше меню должно стать видимым.

ШАГ 3: СМЕНА СЦЕНЫ

Настроив наш пользовательский интерфейс, мы можем прикрепить новый скрипт к нашей сцене LevelDoor. Сохраните его в папке Scripts.

Мы также хотим назначить сигнал body_entered() нашего корневого узла (Area2D) нашему сценарию. Это вызовет всплывающее меню, когда наш игрок входит в тело столкновения.

Наконец, присоедините к вашему сценарию сигналы pressed() узла Button (как ButtonContinue, так и ButtonRestart). Это говорит нашей игре, что делать, когда кнопка нажата.

Прежде чем мы продолжим, нам нужно изменить режим обработки нашей сцены. Каждый узел в Godot имеет Режим обработки, который определяет, когда он обрабатывается. Это означает, что узел или сцена будут работать только тогда, когда ей назначен определенный режим обработки. Режимы можно найти и изменить в свойствах узла Узел в инспекторе.

Вот что каждый режим говорит узлу:

  • Наследовать: узел будет работать или обрабатываться в зависимости от состояния родителя. Если режим родительского процесса можно приостановить, он будет приостановлен и т. д.
  • Пауза: узел будет работать или обрабатываться только тогда, когда игра не поставлена ​​на паузу.
  • При паузе: узел будет работать или обрабатываться только тогда, когда игра приостановлена.
  • Всегда: узел будет работать или обрабатываться независимо от того, поставлена ​​игра на паузу или нет.
  • Отключено: узел не будет работать и не будет обрабатываться.

Нам нужно, чтобы наша сцена LevelDoor работала, даже если игра поставлена ​​на паузу. По умолчанию установлен режим процесса «Наследовать». Это означает, что он будет обрабатывать то же, что и его родительский узел. Нам нужно изменить это значение на «Всегда», так как нам нужно, чтобы этот узел работал, когда наша игра запущена, потому что ему нужно зафиксировать нашего игрока, входящего в столкновение, и нам также нужно, чтобы он работал, когда игра приостановлена, то есть когда наше меню станет видимым. Давайте изменим режим обработки корневого узла LevelDoor на Всегда. Вы можете найти эту опцию в Node › Process › Mode.

Теперь наша сцена LevelDoor будет обрабатывать и выполнять свои функции как при запущенной игре, так и при ее паузе. В нашем скрипте мы приостанавливаем нашу игру, когда тело игрока входит в наше тело столкновения дверей. Когда мы нажимаем кнопку перезапуска или продолжения, мы останавливаем игру, чтобы продолжить игру на следующем уровне. Если мы хотим поставить игру на паузу, мы просто используем метод SceneTree.paused. Если игра поставлена ​​на паузу, ввод данных от игрока не будет принят, потому что все поставлено на паузу. Это если мы не изменим режим процесса нашего узла на Пауза или Всегда, что мы уже сделали!

### LevelDoor.gd

extends Area2D

func _on_body_entered(body):
    if body.name == "Player":
        # pause game
        get_tree().paused = true

func _on_continue_button_pressed():
    #unpause scene
    get_tree().paused = false

func _on_restart_button_pressed():
    #unpause scene
    get_tree().paused = false

Мы также не хотим, чтобы наш узел меню был виден по умолчанию или когда игрок нажимает наши кнопки. Другими словами, когда игра не находится в состоянии паузы, мы хотим, чтобы узел меню был скрыт. Если игра поставлена ​​на паузу, мы хотим показать узел меню. Мы сделаем этот узел видимым, изменив его значение visibility в коде, и когда наш игрок войдет в дверь, мы воспроизведем нашу анимацию ui_visibility, чтобы появилось меню.

### LevelDoor.gd

extends Area2D

func _ready():
    #hide menu on load
    $UI/Menu.visible = false
    
func _on_body_entered(body):
    if body.name == "Player":
        # pause game
        get_tree().paused = true
        # show menu
        $UI/Menu.visible = true
        # animation to make menu's modular value visible
        $AnimationPlayer.play("ui_visibility")      
    
func _on_continue_button_pressed():
    #unpause scene
    get_tree().paused = false
    #hide menu
    $UI/Menu.visible = false
    
func _on_restart_button_pressed():
    #unpause scene
    get_tree().paused = false
    #hide menu
    $UI/Menu.visible = false

Когда мы нажимаем кнопку Продолжить, мы хотим, чтобы наш игрок перешел на следующий уровень. Следующий уровень будет зависеть от текущего уровня, на котором мы находимся. На нашем основном уровне следующим уровнем будет Main_2, а его следующим уровнем будет Main_3 и так далее. Мы не хотим создавать гигантский условный оператор для назначения next_level на основе текущего уровня, так как это будет утомительно и ненужно. Вместо этого мы можем экспортировать переменную, которая будет содержать ресурс PackedScene. Это создает ресурс со ссылкой на файл сцены, например res://Scenes/Main.tscn.

### LevelDoor.gd

# Allows us to change the scene (level) we want to go to in the editor properties
@export var next_level: PackedScene

Экспортируя этот ресурс, мы можем вернуться к нашим сценам уровня, где мы разместили нашу сцену LevelDoor, и выбрать следующий уровень на панели инспектора! Чтобы назначить новую сцену, выберите Instanced LevelDoor в основной сцене и щелкните свойство PackedScene на панели инспектора. Выберите опцию «Быстрая загрузка». В моей основной сцене я выберу ресурс следующего уровня, который будет Main_2, и так далее.

Если вы запустите свою сцену сейчас, и ваш игрок сталкивается с дверью, меню должно отображаться, но пользовательский интерфейс игрока будет отображаться поверх него. Давайте исправим эту проблему, скрыв слой пользовательского интерфейса игрока on_body_entered().

### LevelDoor.gd

#older code

func _on_body_entered(body):
    if body.name == "Player":
        # pause game
        get_tree().paused = true
        # show menu
        $UI/Menu.visible = true
        # make modular value visible
        $AnimationPlayer.play("ui_visibility")  
        #hide the player's UI 
        body.get_node("UI").visible = false

Теперь вернемся к нашей функции ButtonContinue. Если наш игрок нажимает кнопку Продолжить, мы хотим изменить нашу текущую сцену на нашу следующую сцену. Мы будем использовать метод change_scene_to_packed(), который изменяет текущую сцену на новый экземпляр заданной PackedScene (который должен быть действительным). Проще говоря, это изменит нашу текущую сцену (Main) на нашу упакованную сцену, выбранную в редакторе (Main_2).

### LevelDoor.gd

#older code

func _on_continue_button_pressed():
    #unpause scene
    get_tree().paused = false
    #hide menu
    $UI/Menu.visible = false
    # Change to the next scene
    get_tree().change_scene_to_packed(next_level)

Поскольку мы сохранили имя нашей текущей сцены в нашем глобальном скрипте, нам придется проделать некоторую магию, чтобы получить имя нашей упакованной сцены. Это связано с тем, что наша текущая_сцена сохраняется как Main или Main_2, а наша next_level сохраняется как ‹PackedScene#-9223371970517660679›. Это потому, что он сохраняет его как ресурс сцены, а не как файл сцены. Чтобы получить путь к файлу сцены из нашего ресурса сцены, нам нужно извлечь наш путь с помощью метода resource_path. Этот путь может быть абсолютным или относительным путем к файлу.

### LevelDoor.gd

#older code

func _on_continue_button_pressed():
    #unpause scene
    get_tree().paused = false
    #hide menu
    $UI/Menu.visible = false
    # Change to the next scene
    get_tree().change_scene_to_packed(next_level)
    # gets the path of our packed scene resource
    var path = next_level.resource_path

Из этого пути мы можем получить имя файла пути. Для этого мы будем использовать метод get_file(), который возвращает имя файла и расширение нашего пути. Например, если путь res://folder/file.png, path.get_file() даст вам file.png.

Затем нам нужно разделить наше имя файла на массив, который разделит наше имя файла, чтобы мы могли получить значение имени из его расширения. Например, если имя файла «file.png», функция split(«.») даст вам такой массив: [«file», «png»].

Затем мы получим доступ к этому имени файла через индекс массива [0]. Все это вместе превратит ‹PackedScene#…› в "res://Scenes/Main_2.tscn" в "Main_2", что затем мы можем назначить нашу переменную current_scene.

### LevelDoor.gd
#older code

func _on_continue_button_pressed():
    #unpause scene
    get_tree().paused = false
    #hide menu
    $UI/Menu.visible = false
    # Change to the next scene
    get_tree().change_scene_to_packed(next_level)
    # Extract the name of the packed scene file and update the current scene's name in the Global script
    var path = next_level.resource_path
    var scene_name = path.get_file().split(".")[0]
    Global.current_scene_name = scene_name

Теперь, если вы запустите свою сцену и войдете в свою дверь сверху — должно появиться меню. Если вы нажмете кнопку «Продолжить», ваш уровень должен измениться на следующий — и так далее.

С помощью кнопки перезапуска мы хотим перезагрузить current_scene, в которой находится игрок — в нашем случае это наша основная сцена. Мы можем просто использовать метод reload_current_scene(), который перезагружает текущую активную сцену.

### LevelDoor.gd

#older code

func _on_restart_button_pressed():
    #unpause scene
    get_tree().paused = false
    #hide menu
    $UI/Menu.visible = false
    # Restart current scene
    get_tree().reload_current_scene()

На моих скриншотах выше вы увидите, что мой плеер возвращает счет, время и значение рейтинга. Когда наш уровень заканчивается, мы хотим отобразить время в секундах, которое игрок потратил на прохождение уровня. Мы также хотим показать их окончательный результат. Затем наша система рейтинга будет определяться на основе их окончательного результата и окончательного времени. Если они потратили менее 1 минуты и набрали 10000 баллов, они получают звание Мастер. Эта система ранжирования совсем не похожа на систему Donkey Kong, в которой используется система темпа. Подробнее об этом можно прочитать здесь.

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

### Global.gd

#older code

#final level results
var final_score
var final_rating
var final_time

В нашем скрипте LevelDoor мы будем отображать значения этих глобальных переменных в наших узлах UI Value. Они не будут показывать ничего, кроме «0», поскольку мы еще не создали функцию, которая будет захватывать значения этих переменных.

### LevelDoor.gd

#older code

func _on_body_entered(body):
    if body.name == "Player":
        # pause game
        get_tree().paused = true
        # show menu
        $UI/Menu.visible = true
        # make modular value visible
        $AnimationPlayer.play("ui_visibility")  
        #hide the player's UI 
        body.get_node("UI").visible = false 
        # show player values
        $UI/Menu/Container/TimeCompleted/Value.text = str(Global.final_time)
        $UI/Menu/Container/Score/Value.text = str(Global.final_score)
        $UI/Menu/Container/Ranking/Value.text = str(Global.final_rating)

Чтобы получить окончательный счет, мы сохраним значение счета нашего игрока в новой функции, которая вернет счет, рейтинг и время.

### Player.gd

#older code

func final_score_time_and_rating():
    # Final results
    Global.final_score = score

Чтобы получить затраченное время, нам сначала нужно создать новую переменную, которая будет фиксировать время системы, когда мы запускали игру. Мы можем получить время с помощью объекта Time, у которого есть метод get_ticks_msec(). Этот метод возвращает время, прошедшее в миллисекундах с момента запуска двигателя.

### Player.gd

#older code

#time we started the level
var level_start_time = Time.get_ticks_msec()

В нашей функции final_score_time_and_rating() мы снова воспользуемся методом Time, чтобы получить время окончания уровня, которое мы вычтем из нашего времени начала, чтобы получить время завершения. Из этого значения времени завершения мы будем измерять его на 1000, чтобы преобразовать его в значение в секундах.

### Player.gd

#older code

func final_score_time_and_rating():
    # Time to complete in seconds
    var time_taken = (Time.get_ticks_msec() - level_start_time) / 1000.0 # Convert to seconds
    
    # Final results
    Global.final_score = score

Поскольку мы не хотим, чтобы время отображалось с несколькими десятичными знаками (например, 4,123974918371), мы округлим это значение, прежде чем сохранить его в качестве окончательного значения времени.

### Player.gd

#older code

func final_score_time_and_rating():
    # Time to complete in seconds
    var time_taken = (Time.get_ticks_msec() - level_start_time) / 1000.0 # Convert to seconds
    var time_rounded = str(roundf(time_taken)) + " secs"
    
    # Final results
    Global.final_score = score
    Global.final_time = time_rounded

Чтобы получить рейтинг, мы создадим временную переменную, которая будет возвращать строковое значение в зависимости от времени и полученного результата. Изображение ниже дает хорошую демонстрацию того, как работает наша система ранжирования. Вы можете изменить эти значения на любое количество, которое вы хотите, но просто убедитесь, что вы даете своему игроку достаточно возможностей (например, получение очков и усиление атаки, а также препятствия, которые нужно перепрыгнуть), чтобы получить x количество очков. за nрейтинг!

#older code

func final_score_time_and_rating():
    # Time to complete in seconds
    var time_taken = (Time.get_ticks_msec() - level_start_time) / 1000.0 # Convert to seconds
    var time_rounded = str(roundf(time_taken)) + " secs"
    # Rating based on time and score
    var rating = ""
    
    if time_taken <= 60 and score >= 10000:
        rating = "Master" # Exceptionally high score and fast completion
    elif time_taken <= 120 and score >= 5000:
        rating = "Pro" # Very high score and fast completion
    elif time_taken <= 180 and score >= 3000:
        rating = "Expert" # High score and moderately fast completion
    elif time_taken <= 240 and score >= 2000:
        rating = "Intermediate" # Good score and completion time
    elif time_taken <= 300 and score >= 1000:
        rating = "Amateur" # Decent score, but not very fast
    else:
        rating = "Beginner" # All other cases
    
    # Final results
    Global.final_score = score
    Global.final_time = time_rounded
    Global.final_rating = rating

Теперь, если мы запустим нашу сцену, наши окончательные значения по-прежнему будут отображаться как «0», поскольку мы еще не вызвали нашу функцию final_score_time_and_rating() в нашей сцене LevelDoor — следовательно, никакие значения не были захвачены. !

### LevelDoor.gd

#older code

func _on_body_entered(body):
    if body.name == "Player":
        # pause game
        get_tree().paused = true
        # show menu
        $UI/Menu.visible = true
        # make modular value visible
        $AnimationPlayer.play("ui_visibility")  
        #hide the player's UI 
        body.get_node("UI").visible = false 
        #get final values
        body.final_score_time_and_rating()
        # show player values
        $UI/Menu/Container/TimeCompleted/Value.text = str(Global.final_time)
        $UI/Menu/Container/Score/Value.text = str(Global.final_score)
        $UI/Menu/Container/Ranking/Value.text = str(Global.final_rating)

Ваш код должен выглядеть как это.

Теперь, если вы запустите свою сцену и доберетесь до вершины, ваш счет, время и значение рейтинга должны отображаться! Вам также должно быть разрешено перезапустить игру и перейти на следующий уровень. Обратите внимание, что я обманул значение своего счета в самом коде для целей тестирования, поэтому я никоим образом не являюсь игроком рейтинга «Мастер»!

Легко перейти на следующий уровень и получить x очков, если ваш игрок никогда не умрет. В настоящее время жизни нашего игрока могут быть исчерпаны, но мы все еще можем идти вперед и набирать очки. Нам нужно остановить это дурачество, предоставив нашим игрокам экран смерти/конца игры! В следующей части мы реализуем эту функцию, чтобы остановить нашу игру, если у нашего игрока закончится здоровье, что заставит нашего игрока переиграть текущий уровень.

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

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

Следующая часть серии руководств

Учебная серия состоит из 24 глав. Я буду публиковать все главы в отдельных ежедневных частях в течение следующих нескольких недель. Вы можете найти обновленный список ссылок на учебники для всех 24 частей этой серии на моем GitBook. Если вы еще не видите ссылку, добавленную к части, это означает, что она еще не опубликована. Кроме того, если будут какие-либо будущие обновления серии, мой GitBook будет местом, где вы сможете быть в курсе всего!

Поддержите серию и получите ранний доступ!

Если вам нравится эта серия и вы хотите поддержать меня, вы можете пожертвовать любую сумму моему магазину KoFi или купить оффлайн PDF, в котором вся серия собрана в одном буклете, который можно взять с собой!

Брошюра дает вам пожизненный доступ к полной автономной версии брошюры «Изучайте Godot 4, создавая двухмерный платформер» в формате PDF. Это 451-страничный документ, содержащий все учебные пособия из этой серии в последовательном формате, а также вы получите от меня специальную помощь, если вы когда-нибудь застрянете или вам понадобится совет. Это означает, что вам не нужно ждать, пока я опубликую следующую часть серии руководств на Dev.to или Medium. Вы можете просто двигаться дальше и продолжать обучение в своем собственном темпе — в любое время и в любом месте!