Вспышка и счёт вверх
Часть раздела Звук и игровые ощущения путешествия по Pygame на Coddy. Урок 23 из 23.
Ещё две последние детали. Когда игрок теряет жизнь, весь экран на мгновение вспыхивает красным, чтобы потерю невозможно было не заметить. Когда игрок набирает очки, число увеличивается постепенно, а не скачком.
Красная вспышка — это Surface размером с окно, залитая красным и сделанная полупрозрачной с помощью set_alpha(), от 0 (невидимая) до 255 (непрозрачная). Создайте её один раз, до цикла:
red = pygame.Surface((640, 480))
red.fill((255, 0, 0))
red.set_alpha(90)При потере жизни задайте flash = 12. Каждый кадр уменьшайте это значение, и пока оно больше 0, игра накладывает red на игровой мир перед текстом.
Для счёта заведите второе число, shown_score, и каждый кадр плавно приближайте его к настоящему счёту:
shown_score += (score - shown_score) * 0.2Выводите round(shown_score). Игра по-прежнему принимает все решения на основе score; плавно меняется только отображение. Это правило для всех косметических улучшений этой главы: они меняют то, что игрок видит и слышит, но никогда не меняют правила.
Задание
Средне- Перед циклом:
flash = 0, красная поверхность с альфой 90 иshown_score = 0. - Установи
flash = 12в обоих местах, где теряется жизнь. - Рядом с
explosions.update(): уменьшайflashна 1, пока он больше 0, и плавно приближайshown_scoreкscoreс шагом 0.2. - После вывода мира на экран выводи
redв точке(0, 0), покаflashбольше 0. - Показывай
Score: {round(shown_score)}вместо счёта.
Попробуйте сами
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("Star Patrol")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 32)
ship_image = pygame.image.load("assets/images/ship.png").convert_alpha()
laser_image = pygame.image.load("assets/images/laser.png").convert_alpha()
red_image = pygame.image.load("assets/images/enemy_red.png").convert_alpha()
green_image = pygame.image.load("assets/images/enemy_green.png").convert_alpha()
yellow_image = pygame.image.load("assets/images/enemy_yellow.png").convert_alpha()
explosion_frames = []
for number in range(1, 5):
explosion_frames.append(pygame.image.load(f"assets/images/explosion_{number}.png").convert_alpha())
laser_sound = pygame.mixer.Sound("assets/sounds/laser.ogg")
laser_sound.set_volume(0.4)
explosion_sound = pygame.mixer.Sound("assets/sounds/explosion.ogg")
hit_sound = pygame.mixer.Sound("assets/sounds/hit.ogg")
WAVES = [(red_image, 6, 2), (green_image, 8, 2), (yellow_image, 10, 3)]
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = ship_image
self.rect = self.image.get_rect(midbottom=(320, 470))
self.mask = pygame.mask.from_surface(self.image)
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= 6
if keys[pygame.K_RIGHT]:
self.rect.x += 6
self.rect.clamp_ip(screen.get_rect())
class Laser(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = laser_image
self.rect = self.image.get_rect(midbottom=(x, y))
self.mask = pygame.mask.from_surface(self.image)
def update(self):
self.rect.y -= 10
if self.rect.bottom < 0:
self.kill()
class Enemy(pygame.sprite.Sprite):
def __init__(self, image, x, y, speed):
super().__init__()
self.image = image
self.rect = self.image.get_rect(center=(x, y))
self.mask = pygame.mask.from_surface(self.image)
self.speed = speed
def update(self):
self.rect.y += self.speed
class Explosion(pygame.sprite.Sprite):
def __init__(self, center):
super().__init__()
self.index = 0
self.timer = 0
self.image = explosion_frames[0]
self.rect = self.image.get_rect(center=center)
def update(self):
self.timer += 1
if self.timer == 5:
self.timer = 0
self.index += 1
if self.index == len(explosion_frames):
self.kill()
else:
self.image = explosion_frames[self.index]
def play_music(path, loops=-1):
pygame.mixer.music.load(path)
pygame.mixer.music.play(loops)
def burst(center):
for i in range(16):
particles.append({
"x": center[0],
"y": center[1],
"vx": random.uniform(-4, 4),
"vy": random.uniform(-4, 4),
"life": random.randint(15, 30),
})
def spawn_wave(image, count, speed):
for i in range(count):
x = random.randint(40, 600)
enemies.add(Enemy(image, x, -40 - i * 120, speed))
player = Player()
lasers = pygame.sprite.Group()
enemies = pygame.sprite.Group()
explosions = pygame.sprite.Group()
world = pygame.Surface((640, 480))
particles = []
shake = 0
last_shot = 0
score = 0
lives = 3
wave = 0
image, count, speed = WAVES[wave]
spawn_wave(image, count, speed)
pygame.mixer.music.set_volume(0.5)
play_music("assets/music/level.ogg")
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if lives > 0 and wave < len(WAVES):
player.update()
keys = pygame.key.get_pressed()
now = pygame.time.get_ticks()
if keys[pygame.K_SPACE] and now - last_shot >= 250:
lasers.add(Laser(player.rect.centerx, player.rect.top))
last_shot = now
laser_sound.play()
lasers.update()
enemies.update()
for enemy in enemies:
if enemy.rect.top > 480:
enemy.kill()
lives -= 1
hit_sound.play()
hits = pygame.sprite.groupcollide(enemies, lasers, True, True, pygame.sprite.collide_mask)
for enemy in hits:
score += 10
explosion_sound.play()
burst(enemy.rect.center)
shake = min(shake + 6, 15)
explosions.add(Explosion(enemy.rect.center))
crashes = pygame.sprite.spritecollide(player, enemies, True, pygame.sprite.collide_mask)
for enemy in crashes:
lives -= 1
hit_sound.play()
explosions.add(Explosion(enemy.rect.center))
burst(enemy.rect.center)
shake = 15
if not enemies:
wave += 1
if wave < len(WAVES):
image, count, speed = WAVES[wave]
spawn_wave(image, count, speed)
else:
play_music("assets/music/win.ogg", 0)
if lives <= 0:
play_music("assets/music/lose.ogg", 0)
explosions.update()
for p in particles:
p["x"] += p["vx"]
p["y"] += p["vy"]
p["life"] -= 1
particles = [p for p in particles if p["life"] > 0]
world.fill((12, 14, 30))
enemies.draw(world)
lasers.draw(world)
world.blit(player.image, player.rect)
explosions.draw(world)
for p in particles:
pygame.draw.circle(world, (255, 170, 60), (p["x"], p["y"]), p["life"] // 10 + 1)
offset = (0, 0)
if shake > 0:
amount = shake // 3
offset = (random.randint(-amount, amount), random.randint(-amount, amount))
shake -= 1
screen.fill((0, 0, 0))
screen.blit(world, offset)
score_text = font.render(f"Score: {score}", True, (240, 240, 240))
screen.blit(score_text, (10, 10))
lives_text = font.render(f"Lives: {max(lives, 0)}", True, (240, 240, 240))
screen.blit(lives_text, lives_text.get_rect(topright=(630, 10)))
if wave < len(WAVES):
wave_text = font.render(f"Wave {wave + 1}", True, (240, 240, 240))
screen.blit(wave_text, wave_text.get_rect(midtop=(320, 10)))
if lives <= 0:
message = font.render("Game over", True, (240, 240, 240))
screen.blit(message, message.get_rect(center=(320, 240)))
elif wave == len(WAVES):
message = font.render("You win!", True, (240, 240, 240))
screen.blit(message, message.get_rect(center=(320, 240)))
pygame.display.flip()
clock.tick(60)
pygame.quit()
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Звук и игровые ощущения
4Тряска экрана и вспышка при попадании
Тряска экранаВспышка при попаданииОстановка при попаданииСоблюдайте умеренность6Доведём «Звёздный патруль» до блеска
Звуки «Звёздного патруля»Искры при каждом попаданииПотренируйтесь самостоятельно: Песочница Pygame