Стрелялка с Pygame №10: Взрывы

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

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

Авто-огонь

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

Для этого игроку нужно добавить несколько свойств:

self.shoot_delay = 250
self.last_shot = pygame.time.get_ticks()

shmup_delay будет измерять, сколько времени должно пройти между появлением пуль (в миллисекундах). last_shot — отслеживать, сколько прошло с момента последней пули так, чтобы в нужный момент выстрелить снова.

Теперь нужно добавить кнопку огня к проверке нажатий на клавиатуре. Она находится в функции обновления игрока:

def update(self):
    self.speedx = 0
    keystate = pygame.key.get_pressed()
    if keystate[pygame.K_LEFT]:
        self.speedx = -8
    if keystate[pygame.K_RIGHT]:
        self.speedx = 8
    if keystate[pygame.K_SPACE]:
        self.shoot()

Всю механику стрельбы также можно перенести в отдельный метод:

def shoot(self):
now = pygame.time.get_ticks()
if now - self.last_shot > self.shoot_delay:
    self.last_shot = now
    bullet = Bullet(self.rect.centerx, self.rect.top)
    all_sprites.add(bullet)
    bullets.add(bullet)

Теперь, если пробел нажат, игра будет проверять, сколько времени прошло после вылета последней пули. Если прошло shoot_delay миллисекунд, тогда вылетит следующая пуля, а параметр last_shot обновится. В конце концов, можно удалить следующие строки из игрового цикла.

elif event.type == pygame.KEYDOWN:
    if event.key == pygame.K_SPACE:
        player.shoot()

Анимированные взрывы

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

набор кадров анимации

Нажмите здесь, чтобы загрузить архив с этими изображениями.

Сначала нужно загрузить графику в игру и добавить ее в список. Как и в случае со спрайтом игрока, необходимо поменять размер этих изображений, сделав две версии. Крупная будет воспроизводиться в месте подрыва астероида игроком, а маленькая — там, где астероид врезается в корабль игрока. Для этого используется словарь explosion_anim c двумя списками lg и sm. Поскольку все файлы называются одинаково (с номерами от 0 до 8), можно использовать цикл для их загрузки, изменения размера и добавления в списки:

explosion_anim = {}
explosion_anim['lg'] = []
explosion_anim['sm'] = []
for i in range(9):
    filename = 'regularExplosion0{}.png'.format(i)
    img = pygame.image.load(path.join(img_dir, filename)).convert()
    img.set_colorkey(BLACK)
    img_lg = pygame.transform.scale(img, (75, 75))
    explosion_anim['lg'].append(img_lg)
    img_sm = pygame.transform.scale(img, (32, 32))
    explosion_anim['sm'].append(img_sm)

Спрайт взрыва

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

При его появлении спрайту нужно указать, где появляться (местоположение астероида) и какого быть размера. Как и в случае с функцией авто-огня, здесь можно использовать параметр frame_rate, который будет контролировать скорость воспроизведения анимации — если менять изображение с каждым обновлением игрового цикла (1/60 секунды), то взрыв произойдет очень быстро (1/10 секунды). Поэтому вот код спрайта Explosion:

class Explosion(pygame.sprite.Sprite):
    def __init__(self, center, size):
        pygame.sprite.Sprite.__init__(self)
        self.size = size
        self.image = explosion_anim[self.size][0]
        self.rect = self.image.get_rect()
        self.rect.center = center
        self.frame = 0
        self.last_update = pygame.time.get_ticks()
        self.frame_rate = 50

    def update(self):
        now = pygame.time.get_ticks()
        if now - self.last_update > self.frame_rate:
            self.last_update = now
            self.frame += 1
            if self.frame == len(explosion_anim[self.size]):
                self.kill()
            else:
                center = self.rect.center
                self.image = explosion_anim[self.size][self.frame]
                self.rect = self.image.get_rect()
                self.rect.center = center

Теперь нужно сделать так, чтобы этот спрайт появлялся при уничтожении астероида:

# проверьте, не попала ли пуля в моб
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
for hit in hits:
    score += 50 - hit.radius
    random.choice(expl_sounds).play()
    expl = Explosion(hit.rect.center, 'lg')
    all_sprites.add(expl)
    newmob()

и при попадании по игроку:

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

А вот и финальный результат:

Анимация для взрывов астероидов

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

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