Menu
CoddyTech

Thêm đợt địch

Thuộc phần Sprite và hình ảnh trong hành trình Pygame của Coddy. Bài học 38 trên 39.

Một đợt kẻ địch thì chưa đủ thú vị để tạo thành trò chơi. Hãy mô tả các đợt bằng dữ liệu, mỗi đợt là một tuple gồm hình ảnh, số lượng kẻ địch và tốc độ của chúng.

WAVES = [(red_image, 4, 1), (green_image, 5, 2)]

wave là chỉ số của đợt đang diễn ra. Bắt đầu đợt đầu tiên bằng image, count, speed = WAVES[wave], câu lệnh này giải nén tuple thành ba tên, sau đó gọi spawn_wave(image, count, speed). Khi enemies rỗng, bạn đã vượt qua đợt đó:

if not enemies:
    wave += 1
    if wave < len(WAVES):
        image, count, speed = WAVES[wave]
        spawn_wave(image, count, speed)

Một nhóm rỗng được xem là false, vì vậy not enemies là true khi kẻ địch cuối cùng biến mất. Muốn thêm một đợt mới, bạn chỉ cần thêm một tuple vào danh sách.

challenge icon

Thử thách

Trung bình

Cho các đợt xuất hiện lần lượt.

  1. Tải enemy_green.png và enemy_yellow.png, rồi tạo WAVES = [(red_image, 6, 2), (green_image, 8, 2), (yellow_image, 10, 3)].
  2. Đặt wave = 0 và tạo đợt đầu tiên từ WAVES[wave].
  3. Khi enemies trống, chuyển sang đợt tiếp theo và tạo đợt đó nếu còn đợt.
  4. Khi wave < len(WAVES), vẽ f"Wave {wave + 1}" với điểm giữa phía trên ở (320, 10).

Nhấn “Chơi” và vượt qua một đợt: Wave 2 có 8 kẻ địch màu xanh lá. 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()
explosion_frames = []
for number in range(1, 5):
    explosion_frames.append(pygame.image.load(f"assets/images/explosion_{number}.png").convert_alpha())


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 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()
last_shot = 0
score = 0
lives = 3
spawn_wave(red_image, 6, 2)

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    if lives > 0:
        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
        lasers.update()
        enemies.update()
        for enemy in enemies:
            if enemy.rect.top > 480:
                enemy.kill()
                lives -= 1
        hits = pygame.sprite.groupcollide(enemies, lasers, True, True, pygame.sprite.collide_mask)
        for enemy in hits:
            score += 10
            explosions.add(Explosion(enemy.rect.center))
        crashes = pygame.sprite.spritecollide(player, enemies, True, pygame.sprite.collide_mask)
        for enemy in crashes:
            lives -= 1
            explosions.add(Explosion(enemy.rect.center))
    explosions.update()

    screen.fill((12, 14, 30))
    enemies.draw(screen)
    lasers.draw(screen)
    screen.blit(player.image, player.rect)
    explosions.draw(screen)
    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 lives <= 0:
        message = font.render("Game over", 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 Sprite và hình ảnh

Tự luyện tập: Playground Pygame