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

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

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

  • Как создавать собственные сигналы.
  • Как вызывать функции с помощью метода body.

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

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

### Player.gd

#health stats
var max_lives = 3
var lives = 3

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

В верхней части скрипта Player создайте новый сигнал с именем update_lives. Сигнал может дополнительно объявлять один или несколько параметров. Параметр сигнала отображается в доке узлов редактора, и Godot может использовать их для создания для вас функций обратного вызова. Тем не менее, вы все равно можете передавать любое количество параметров при передаче сигналов. Мы хотим, чтобы наш сигнал объявлял два параметра: lives и max_lives.

### Player.gd

#custom signals
signal update_lives(lives, max_lives)

# health stats
var max_lives = 3
var lives = 3

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

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

### Player.gd

#older code

# takes damage
func take_damage():
    #deduct and update lives    
    if lives > 0:
        lives = lives - 1
        update_lives.emit(lives, max_lives)
        print(lives)
        #play damage animation
        $AnimatedSprite2D.play("damage")

В настоящее время наша анимация повреждений не воспроизводится. Это связано с тем, что мы обрабатываем наши анимации в нашей функции physics_process(), которая имеет приоритет над нашей функцией take_damage(), поскольку она обрабатывает каждую в каждом кадре. Чтобы наша анимация повреждений воспроизводилась, нам нужно временно отключить обработку физики. Мы можем сделать это с помощью метода set_physics_process(). Этот метод включает или отключает обработку физики (т. е. с фиксированной частотой кадров).

### Player.gd

#older code

# takes damage
func take_damage():
    #deduct and update lives    
    if lives > 0:
        lives = lives - 1
        update_lives.emit(lives, max_lives)
        print(lives)
        #play damage animation
        $AnimatedSprite2D.play("damage")
        #allows animation to play
        set_physics_process(false)

Теперь наша анимация будет воспроизводиться, но анимации, определенные в нашей функции physics_process(), не будут воспроизводиться, поскольку мы не включили обработку физики. Мы можем установить значение обработки физики обратно в true после воспроизведения анимации в нашей функции _on_animated_sprite_2d_animation_finished().

### Player.gd

#older code

#reset our animation variables
func _on_animated_sprite_2d_animation_finished():
    Global.is_attacking = false
    set_physics_process(true)

Нам также нужно вызвать нашу функцию take_damage() в наших сценариях Bomb и Box, чтобы нашему игроку можно было нанести урон при попадании!

### Bomb.gd 

#older code

func _on_body_entered(body):
    #if the bomb collides with the player, play the explosion animation and start the timer
    if body.name == "Player":
        $AnimatedSprite2D.play("explode")
        $Timer.start()
        Global.is_bomb_moving = false
        #deal damage
        body.take_damage()
### Box.gd

#older code
    
func _on_body_entered(body):
    # If the bomb collides with the player, play the explosion animation and disable spawning
    if body.name == "Player":
        $AnimatedSprite2D.play("explode")
        # Disable spawning in BoxSpawner
        Global.disable_spawning()
        #deal damage
        body.take_damage()

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

Теперь, если вы запускаете свою сцену и сталкиваетесь со своими коробками/бомбами, ваши жизни должны вычитаться, и ваши анимации должны воспроизводиться соответственно.

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

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

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

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

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

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

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

Эта книга будет постоянно обновляться для исправления недавно обнаруженных ошибок или устранения проблем совместимости с более новыми версиями Godot 4.