Rung khi va chạm
Thuộc phần Âm thanh và cảm giác chơi trong hành trình Pygame của Coddy. Bài học 22 trên 23.
Giờ đến hiệu ứng rung. Một phát bắn trúng bằng laser sẽ làm tăng nhẹ giá trị, shake = min(shake + 6, 15), nhờ vậy bạn vẫn dễ theo dõi khi tiêu diệt liên tiếp nhiều kẻ địch. Một cú va chạm làm bạn mất một mạng và cần tạo cảm giác mạnh hơn: hiệu ứng đặt shake = 15.
Để tạo hiệu ứng rung, trò chơi vẽ lên Surface world rồi blit Surface đó ở một độ lệch, như trong ví dụ thùng hàng:
world.fill((12, 14, 30))
enemies.draw(world)
# ... cả tàu, tia laser, vụ nổ và tia lửa trên world nữa
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)Chỉ khu vực chơi được vẽ lên world. Điểm số, số mạng và đợt được vẽ lên màn hình sau khi blit, nên chúng vẫn ở nguyên vị trí và dễ đọc trong khi mọi thứ khác rung lắc.
Thử thách
Trung bìnhLàm rung khung hình mỗi khi có va chạm.
- Tạo
world = pygame.Surface((640, 480))vàshake = 0trước vòng lặp. - Khi laser tiêu diệt kẻ địch, đặt
shake = min(shake + 6, 15); khi kẻ địch va vào tàu, đặtshake = 15. - Vẽ mọi thứ trừ văn bản lên
world; sau đó tô màn hình màu đen và blitworldtại độ lệch như hướng dẫn.
Nhấn Chơi rồi bắn: khung hình rung, điểm số thì không. Sau đó nhấn Kiểm tra.
Tự làm thử
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()
particles = []
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)
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)
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]
screen.fill((12, 14, 30))
enemies.draw(screen)
lasers.draw(screen)
screen.blit(player.image, player.rect)
explosions.draw(screen)
for p in particles:
pygame.draw.circle(screen, (255, 170, 60), (p["x"], p["y"]), p["life"] // 10 + 1)
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()
Tất cả bài học trong Âm thanh và cảm giác chơi
4Rung màn hình và chớp sáng khi trúng đòn
Rung màn hìnhChớp sáng khi trúng đònKhựng hình khi trúng đònGiữ hiệu ứng tinh tế6Hoàn thiện đội tuần tra sao
Âm thanh đội tuần tra saoTia lửa mỗi lần va chạmTự luyện tập: Playground Pygame