Стрелялка с Pygame №9: Здоровье

Девятая часть проекта «Стрелялка с Pygame». Если пропустили, обязательно вернитесь и начните с первой части. В этот раз дадим игроку здоровье и добавим полоску, которая будет показывать его уровень.

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

Добавление здоровья

Пока что игрока уничтожает одно попадание астероида. Так играть не очень интересно, поэтому добавим свойство shield, которое будет всего лишь числом от 0 до 100.

class Player(pygame.sprite.Sprite):
    def __init__(self):
        self.speedx = 0
        self.shield = 100

Теперь, каждый раз, когда астероид будет попадать в игрока, можно отнимать определенный уровень здоровья. Когда уровень упадет до 0, игра заканчивается. Чтобы было интереснее, можно сделать, чтобы крупные астероиды наносили больше урона. Для этого стоит использовать свойство radius астероида.

Урон игроку

Пока что столкновение моб-против-игрока обрабатывается на простейшем уровне:

    # Проверка, не ударил ли моб игрока
    hits = pygame.sprite.spritecollide(player, mobs, False, pygame.sprite.collide_circle)
    if hits:
        running = False

Нужно добавить несколько изменений:

    # Проверка, не ударил ли моб игрока
    hits = pygame.sprite.spritecollide(player, mobs, True, pygame.sprite.collide_circle)
    for hit in hits:
        player.shield -= hit.radius * 2
        if player.shield <= 0:
            running = False

В spritecollide изменим значение с False на True, потому что нужно, чтобы астероид исчезал после попадания. Если этого не сделать, то по мере продвижения он будет снова сталкиваться с игроком в следующих кадрах. Возможно и то, что сразу несколько астероидов ударят по игроку, поэтому значений hit должно быть несколько. Необходимо перебирать hits и отнимать определенное количество уровня здоровья, основываясь на радиусе астероида. В конце концов, когда значение упадет до 0, игра закончится.

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

for i in range(8):
    m = Mob()
    all_sprites.add(m)
    mobs.add(m)

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

def newmob():
    m = Mob()
    all_sprites.add(m)
    mobs.add(m)

Теперь можно использовать эту же функцию в начале игры, когда астероиды только появляются и в том месте, где они пропадают после столкновения:

for i in range(8):
    newmob()

# проверка, попала ли пуля в моб
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
for hit in hits:
    score += 50 - hit.radius
    random.choice(expl_sounds).play()
    newmob()

# Проверка, не ударил ли моб игрока
hits = pygame.sprite.spritecollide(player, mobs, True, pygame.sprite.collide_circle)
for hit in hits:
    player.shield -= hit.radius * 2
    newmob()
    if player.shield <= 0:
        running = False

Полоска здоровья

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

def draw_shield_bar(surf, x, y, pct):
    if pct < 0:
        pct = 0
    BAR_LENGTH = 100
    BAR_HEIGHT = 10
    fill = (pct / 100) * BAR_LENGTH
    outline_rect = pygame.Rect(x, y, BAR_LENGTH, BAR_HEIGHT)
    fill_rect = pygame.Rect(x, y, fill, BAR_HEIGHT)
    pygame.draw.rect(surf, GREEN, fill_rect)
    pygame.draw.rect(surf, WHITE, outline_rect, 2)

Эта функция будет работать по аналогии с draw_text. Она создаст полоску с размерами BAR_LENGHT и BAR_HEIGHT, которая будет находиться в (x, y) и заполнена на следующее значение pct. Нарисуем два прямоугольника: первый — белый контур, второй — уровень здоровья. Добавим вызов этой функции в раздел отрисовки игрового цикла:

draw_text(screen, str(score), 18, WIDTH / 2, 10)
draw_shield_bar(screen, 5, 5, player.shield)

Теперь можно видеть, сколько здоровья у игрока, и как он меняется при попадании астероидов.

Код урока — shmup-9.py