Menu
CoddyTech

Goyang saat Benturan

Termasuk bagian Suara dan Sensasi Bermain dalam perjalanan Pygame di Coddy. Pelajaran 22 dari 23.

Sekarang efek guncangannya. Laser yang mengenai target menambah sedikit nilai, shake = min(shake + 6, 15), sehingga rangkaian eliminasi tetap mudah dilihat. Tabrakan mengurangi satu nyawa dan harus terasa lebih kuat: efeknya menetapkan shake = 15.

Untuk membuat efek guncangan, game menggambar pada Surface world, lalu menampilkannya dengan offset, seperti pada demo peti:

world.fill((12, 14, 30))
enemies.draw(world)
# ... kapal, laser, ledakan, dan percikan juga ada di world

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)

Hanya area permainan yang digambar pada world. Skor, nyawa, dan gelombang digambar pada screen setelah blit, sehingga tetap di tempat dan mudah dibaca saat semua elemen lainnya berguncang.

challenge icon

Tantangan

Sedang
  1. Sebelum perulangan, buat world = pygame.Surface((640, 480)) dan shake = 0.
  2. Saat laser menghancurkan musuh, tambahkan shake = min(shake + 6, 15). Saat musuh menabrak kapal, atur shake = 15.
  3. Gambar isian latar belakang, sprite, ledakan, dan percikan pada world, bukan screen.
  4. Kemudian tentukan offset seperti yang ditunjukkan, isi layar dengan warna hitam, lalu blit world pada offset tersebut. Biarkan teks setelahnya tetap berada di layar.

Coba sendiri

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()

Semua pelajaran di Suara dan Sensasi Bermain

Latihan sendiri: Playground Pygame