본문 바로가기

[Pygame] 🧱 벽돌 깨기 게임 만들기 15강 | 배경음악 추가

@도마22026. 2. 27. 19:00
728x90


배경음악 추가

이번 강의에서는 게임에 배경음악(BGM) 을 추가합니다.
특히 상태에 따라 음악이 바뀌도록 구성하여
게임의 분위기 전환을 명확하게 만듭니다.

이번 강의의 핵심은 다음과 같습니다.

  • 타이틀 화면 전용 배경음악 추가
  • 게임 플레이 중 배경음악 추가
  • 게임 상태 변화에 따라 음악 자동 전환

이제 게임은
시각 + 사운드가 함께 완성 단계에 들어갑니다.


왜 배경음악을 상태별로 나눠야 하는가

배경음악은 단순한 소리가 아니라
플레이어에게 현재 상태를 알려주는 신호입니다.

  • 타이틀 음악은 기다림과 시작을 표현합니다
  • 게임 음악은 집중과 플레이를 유도합니다

같은 음악을 계속 사용하는 것보다
상태에 맞는 음악 분리가 훨씬 자연스럽습니다.


사용할 음악 파일

이번 강의에서는 다음 두 개의 음악 파일을 사용합니다.

  • title_bgm.mp3
  • game_bgm.mp3

파일은 파이썬 코드와 같은 폴더에 둡니다.


배경음악 로드

배경음악은 pygame.mixer.music 을 사용합니다.

pygame.mixer.music.load("title_bgm.mp3")
pygame.mixer.music.play(-1)

-1 은 무한 반복 재생을 의미합니다.


상태 전환 시 음악 변경

게임 상태가 변경될 때
현재 재생 중인 음악을 바꾸도록 처리합니다.

  • TITLE 진입 → 타이틀 음악
  • PLAY 진입 → 게임 음악

음악은 중복 재생되지 않도록
상태 전환 시에만 변경합니다.


현재 단계의 결과

이번 강의까지 구현된 결과는 다음과 같습니다.

  • 타이틀 화면에서 전용 배경음악이 재생됩니다
  • 게임 시작 시 음악이 자연스럽게 전환됩니다
  • 게임이 훨씬 완성된 느낌을 줍니다

다음 강의에서는
게임의 시각적 인상을 바꾸는 게임 디자인 작업을 진행합니다.


예제에 사용된 음악

title_bgm.mp3
1.86MB
game_bgm.mp3
2.01MB


전체 코드

더보기
import pygame
import sys
import random

pygame.init()
pygame.mixer.init()

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Breakout Game")

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 200, 0)
EFFECT_COLOR = (255, 200, 200)

clock = pygame.time.Clock()
FPS = 60

font = pygame.font.SysFont(None, 30)
title_font = pygame.font.SysFont(None, 64)
info_font = pygame.font.SysFont(None, 28)

brick_sound = pygame.mixer.Sound("brick.wav")
paddle_sound = pygame.mixer.Sound("paddle.wav")
item_sound = pygame.mixer.Sound("item.wav")

game_state = "TITLE"
prev_state = None
ball_active = False

score = 0
lives = 3

paddle_width = 100
paddle_height = 15
paddle_speed = 7

paddle_rect = pygame.Rect(
    (SCREEN_WIDTH - paddle_width) // 2,
    SCREEN_HEIGHT - 40,
    paddle_width,
    paddle_height
)

ball_radius = 8
ball_x = paddle_rect.centerx
ball_y = paddle_rect.top - ball_radius
ball_speed_x = 5
ball_speed_y = -5

ball_rect = pygame.Rect(
    ball_x - ball_radius,
    ball_y - ball_radius,
    ball_radius * 2,
    ball_radius * 2
)

brick_width = 60
brick_height = 20
brick_padding = 10
rows = 4
cols = 10

bricks = []
items = []
effects = []
item_speed = 2

def create_bricks():
    bricks.clear()
    for row in range(rows):
        for col in range(cols):
            brick_x = col * (brick_width + brick_padding) + 35
            brick_y = row * (brick_height + brick_padding) + 50
            bricks.append(pygame.Rect(brick_x, brick_y, brick_width, brick_height))

def create_item(x, y):
    items.append(pygame.Rect(x, y, 20, 20))

def reset_game():
    global score, lives, ball_active, ball_speed_x, ball_speed_y
    score = 0
    lives = 3
    ball_active = False
    ball_speed_x = 5
    ball_speed_y = -5
    paddle_rect.width = paddle_width
    paddle_rect.x = (SCREEN_WIDTH - paddle_width) // 2
    create_bricks()
    items.clear()
    effects.clear()

create_bricks()

pygame.mixer.music.load("title_bgm.mp3")
pygame.mixer.music.play(-1)

running = True
while running:
    clock.tick(FPS)

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

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE and game_state == "TITLE":
                game_state = "PLAY"
                ball_active = False

            elif event.key == pygame.K_SPACE and game_state == "PLAY" and not ball_active:
                ball_active = True

            if event.key == pygame.K_r and game_state in ("GAME_OVER", "GAME_CLEAR"):
                reset_game()
                game_state = "PLAY"

    if game_state != prev_state:
        if game_state == "TITLE":
            pygame.mixer.music.load("title_bgm.mp3")
            pygame.mixer.music.play(-1)
        elif game_state == "PLAY":
            pygame.mixer.music.load("game_bgm.mp3")
            pygame.mixer.music.play(-1)
        prev_state = game_state

    if game_state == "TITLE":
        screen.fill(BLACK)
        screen.blit(
            title_font.render("BREAKOUT GAME", True, WHITE),
            (SCREEN_WIDTH // 2 - 200, 200)
        )
        screen.blit(
            info_font.render("Press SPACE to Start", True, WHITE),
            (SCREEN_WIDTH // 2 - 110, 300)
        )
        pygame.display.flip()
        continue

    if game_state in ("GAME_OVER", "GAME_CLEAR"):
        screen.fill(BLACK)
        text = "GAME OVER" if game_state == "GAME_OVER" else "GAME CLEAR!"
        screen.blit(
            title_font.render(text, True, WHITE),
            (SCREEN_WIDTH // 2 - 130, 200)
        )
        screen.blit(
            info_font.render("Press R to Restart", True, WHITE),
            (SCREEN_WIDTH // 2 - 80, 300)
        )
        pygame.display.flip()
        continue

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        paddle_rect.x -= paddle_speed
    if keys[pygame.K_RIGHT]:
        paddle_rect.x += paddle_speed

    paddle_rect.left = max(paddle_rect.left, 0)
    paddle_rect.right = min(paddle_rect.right, SCREEN_WIDTH)

    if not ball_active:
        ball_x = paddle_rect.centerx
        ball_y = paddle_rect.top - ball_radius
    else:
        ball_x += ball_speed_x
        ball_y += ball_speed_y

    ball_rect.x = ball_x - ball_radius
    ball_rect.y = ball_y - ball_radius

    if ball_active:
        if ball_rect.left <= 0 or ball_rect.right >= SCREEN_WIDTH:
            ball_speed_x *= -1
        if ball_rect.top <= 0:
            ball_speed_y *= -1

        if ball_rect.colliderect(paddle_rect):
            ball_speed_y *= -1
            ball_rect.bottom = paddle_rect.top
            ball_y = ball_rect.centery
            paddle_sound.play()

    for brick in bricks:
        if ball_rect.colliderect(brick):
            overlap_left = ball_rect.right - brick.left
            overlap_right = brick.right - ball_rect.left
            overlap_top = ball_rect.bottom - brick.top
            overlap_bottom = brick.bottom - ball_rect.top

            min_overlap = min(overlap_left, overlap_right, overlap_top, overlap_bottom)

            if min_overlap == overlap_left or min_overlap == overlap_right:
                ball_speed_x *= -1
            else:
                ball_speed_y *= -1

            if random.random() < 0.3:
                create_item(brick.centerx - 10, brick.centery)

            effects.append({
                "rect": pygame.Rect(brick.x, brick.y, brick.width, brick.height),
                "timer": 10
            })

            brick_sound.play()
            bricks.remove(brick)
            score += 10
            break

    if len(bricks) == 0:
        game_state = "GAME_CLEAR"

    for item in items[:]:
        item.y += item_speed
        if item.colliderect(paddle_rect):
            paddle_rect.width += 30
            item_sound.play()
            items.remove(item)
        elif item.top > SCREEN_HEIGHT:
            items.remove(item)

    for effect in effects[:]:
        effect["timer"] -= 1
        if effect["timer"] <= 0:
            effects.remove(effect)

    if ball_rect.top > SCREEN_HEIGHT and ball_active:
        lives -= 1
        ball_active = False
        if lives <= 0:
            game_state = "GAME_OVER"
        else:
            paddle_rect.width = paddle_width
            paddle_rect.x = (SCREEN_WIDTH - paddle_width) // 2
            create_bricks()
            items.clear()
            effects.clear()

    screen.fill(BLACK)

    screen.blit(font.render(f"Score: {score}", True, WHITE), (10, 10))
    screen.blit(font.render(f"Lives: {lives}", True, WHITE), (SCREEN_WIDTH - 100, 10))

    pygame.draw.rect(screen, WHITE, paddle_rect)
    pygame.draw.circle(screen, WHITE, (ball_x, ball_y), ball_radius)

    for brick in bricks:
        pygame.draw.rect(screen, WHITE, brick)

    for item in items:
        pygame.draw.rect(screen, GREEN, item)

    for effect in effects:
        pygame.draw.rect(screen, EFFECT_COLOR, effect["rect"])

    pygame.display.flip()

pygame.quit()
sys.exit()

 


728x90
도마2
@도마2 :: 도마의 코드노트

초보자를 위한 코딩 강의를 정리합니다. 파이썬부터 C#, Unity 게임 제작까지 차근차근 기록합니다. — 도마

목차