티스토리 뷰

배경화면, BGM, 효과음 추가 하기

이번 단계에서는 기존 벽돌깨기 게임에 배경화면과 사운드 기능을 추가한다. 지금까지의 게임은 검은색 배경 위에 벽돌, 공, 패들만 출력되는 단순한 형태였다. 여기에 배경 이미지를 넣으면 게임 화면이 더 완성도 있게 보이고, BGM과 효과음을 추가하면 사용자가 게임 상황을 더 생생하게 느낄 수 있다.

추가 기능의 핵심은 다음과 같다.

배경화면 → render()에서 가장 먼저 출력
BGM → init()에서 불러오고 반복 재생
효과음 → update_game()에서 사건이 발생할 때 재생

우선, 파일은 다음처럼 준비하면 된다.

asset/background.png
asset/bgm.mp3
asset/hit.wav
asset/paddle.wav
asset/miss.wav
asset/game_over.wav
asset/clear.wav

brick_breaker_assets.zip
0.51MB


배경이미지 넣기

background.png는 어떤 것이 좋은가?

벽돌깨기 게임에서는 어두운 배경 이미지(dark background image) 가 가장 좋다.
현재 게임 요소가 대부분 흰색과 빨간색이기 때문이다.
예: 어두운 우주 배경(space background)

파일 위치는 다음처럼 둔다.

step07.py
asset/
└─ background.png

단계별 설명

1. 전역변수에 추가

font = None 아래에 배경 이미지를 저장할 변수를 추가한다.

# 글꼴 정보
font = None

# ADDED 배경 이미지
background_img = None
  • background_img는 불러온 배경 이미지를 저장하는 변수이다.

2. 초기화 init()에 추가

  • 먼저 init() 함수의 global 부분에 background_img를 추가한다.
def init():
    global screen, clock
    global font
    global background_img  # ADDED
    global ball_pos, ball_vel, paddle_rect, bricks
    global score, life, block_count
    global game_state, end_message
    global bonus_bricks
 
  • 그리고 font = pygame.font.SysFont(None, 30) 아래에 배경 이미지 불러오기 코드를 추가한다.
    font = pygame.font.SysFont(None, 30)

    # ADDED 배경 이미지 불러오기
    try:
        background_img = pygame.image.load("asset/background.png").convert()
        background_img = pygame.transform.scale(background_img, (WIDTH, HEIGHT))
    except pygame.error as e:
        print("배경 이미지 로드 실패:", e)
        background_img = None
 
  • 이 코드는 asset/background.png 파일을 불러온다. 그리고 게임 창 크기인 600 × 800에 맞게 이미지 크기를 조정한다.
background_img = pygame.transform.scale(background_img, (WIDTH, HEIGHT))

3. 입력

입력 단계는 수정하지 않는다. 배경 이미지는 키보드 입력과 관련이 없기 때문이다.

4. 업데이트

업데이트 단계도 수정하지 않는다. 배경 이미지는 움직이거나 충돌하지 않는다.

5. 출력 render() 수정

기존 코드에는 다음 부분이 있다.

screen.fill(BLACK)
 
  • 이 부분을 아래 코드로 바꾼다.
    # CHANGED 검은 배경 대신 배경 이미지 출력
    if background_img is not None:
        screen.blit(background_img, (0, 0))
    else:
        screen.fill(BLACK)
 
  • 즉, render() 함수의 앞부분은 이렇게 바뀐다.
def render():
    # 게임 종료 상태이면 종료 화면 출력
    if game_state == "game_over" or game_state == "clear":
        render_end_screen()
        pygame.display.flip()
        return

    # CHANGED 검은 배경 대신 배경 이미지 출력
    if background_img is not None:
        screen.blit(background_img, (0, 0))
    else:
        screen.fill(BLACK)
 
  • screen.blit(background_img, (0, 0))은 배경 이미지를 화면 왼쪽 위부터 출력하라는 뜻이다.
  • 배경 이미지는 반드시 가장 먼저 출력하여야 한다. 그래야 그 위에 점수, 벽돌, 공, 패들이 보인다.

출력 순서는 다음과 같다.

배경 이미지
→ 점수, 생명, 남은 벽돌 수
→ 벽돌
→ 공
→ 패들
→ 시작 안내 문구

6. 종료 화면 render_end_screen()도 수정

게임 오버 화면이나 클리어 화면에서도 배경 이미지를 보이게 하려면 render_end_screen()의 첫 줄도 수정한다.

기존 코드:

def render_end_screen():
    screen.fill(BLACK)
 

수정 코드:

def render_end_screen():
    # CHANGED 종료 화면에도 배경 이미지 출력
    if background_img is not None:
        screen.blit(background_img, (0, 0))
    else:
        screen.fill(BLACK)
 

이렇게 하면 게임 중 화면뿐만 아니라 Game Over, You Win 화면에도 배경 이미지가 출력된다.

7. 실행

실행 단계는 수정하지 않는다.

핵심코드

가장 중요한 코드는 다음 한 줄이다.

screen.blit(background_img, (0, 0))
  • 이 코드는 배경 이미지를 게임 화면에 출력하는 코드이다.

배경이미지 추가 전체코드 (step07_Bg.py)

import pygame
import sys
import random  # ADDED 랜덤 보너스 벽돌 선택을 위해 추가


# 전역 변수
WIDTH, HEIGHT = 600, 800
screen = None
clock = None
running = True

# 게임 상태
game_state = "ready"  # ready, play, game_over, clear

# 종료 메시지
end_message = ""

# 색상
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)  # ADDED 보너스 벽돌 색상

# 글꼴 정보
font = None

# ADDED 배경 이미지
background_img = None

# 공 정보
BALL_RADIUS = 10
BALL_SPEED_X = 4
BALL_SPEED_Y = -4
ball_pos = None
ball_vel = None

# 패들 정보
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
PADDLE_SPEED = 7
paddle_rect = None
paddle_dx = 0

# 벽돌 정보
BRICK_ROWS = 5
BRICK_COLS = 8
BRICK_WIDTH = 60
BRICK_HEIGHT = 20
BRICK_GAP = 10
bricks = []

# ADDED 보너스 벽돌 정보
BONUS_BRICK_COUNT = 4
NORMAL_SCORE = 10
BONUS_SCORE = 20
bonus_bricks = []

# 게임 정보
score = 0
life = 3
block_count = 0


# 1. 게임 초기화
def init():
    global screen, clock
    global font
    global background_img  # ADDED
    global ball_pos, ball_vel, paddle_rect, bricks
    global score, life, block_count
    global game_state, end_message
    global bonus_bricks

    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("벽돌깨기 6단계 - 배경 이미지")
    clock = pygame.time.Clock()

    font = pygame.font.SysFont(None, 30)

    # ADDED 배경 이미지 불러오기
    try:
        background_img = pygame.image.load("asset/background.png").convert()
        background_img = pygame.transform.scale(background_img, (WIDTH, HEIGHT))
    except pygame.error as e:
        print("배경 이미지 로드 실패:", e)
        background_img = None

    # 게임 상태 초기화
    game_state = "ready"
    end_message = ""

    # 게임 정보 초기화
    score = 0
    life = 3
    block_count = BRICK_ROWS * BRICK_COLS

    # 공 초기 위치와 속도
    ball_pos = [WIDTH // 2, HEIGHT // 2]
    ball_vel = [BALL_SPEED_X, BALL_SPEED_Y]

    # 패들 초기 위치
    paddle_x = WIDTH // 2 - PADDLE_WIDTH // 2
    paddle_y = HEIGHT - 50

    paddle_rect = pygame.Rect(
        paddle_x,
        paddle_y,
        PADDLE_WIDTH,
        PADDLE_HEIGHT
    )

    # 벽돌 생성
    bricks = []

    total_brick_width = BRICK_COLS * BRICK_WIDTH + (BRICK_COLS - 1) * BRICK_GAP
    start_x = WIDTH // 2 - total_brick_width // 2
    start_y = 80

    for row in range(BRICK_ROWS):
        for col in range(BRICK_COLS):
            brick_x = start_x + col * (BRICK_WIDTH + BRICK_GAP)
            brick_y = start_y + row * (BRICK_HEIGHT + BRICK_GAP)

            brick = pygame.Rect(
                brick_x,
                brick_y,
                BRICK_WIDTH,
                BRICK_HEIGHT
            )

            bricks.append(brick)

    # ADDED 전체 벽돌 중 랜덤하게 4개를 보너스 벽돌로 선택
    bonus_bricks = random.sample(bricks, BONUS_BRICK_COUNT)


# 2. 입력 처리
def handle_input():
    global running
    global paddle_dx
    global game_state

    paddle_dx = 0

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

        # ready 상태에서 SPACE BAR를 누르면 게임 시작
        if event.type == pygame.KEYDOWN:
            if game_state == "ready":
                if event.key == pygame.K_SPACE:
                    game_state = "play"

            # 게임 종료 상태에서 아무 키나 누르면 종료
            elif game_state == "game_over" or game_state == "clear":
                running = False

    # play 상태에서만 패들 이동
    if game_state == "play":
        keys = pygame.key.get_pressed()

        if keys[pygame.K_LEFT]:
            paddle_dx = -PADDLE_SPEED

        if keys[pygame.K_RIGHT]:
            paddle_dx = PADDLE_SPEED


# 3. 게임 업데이트
def update_game():
    global ball_pos, ball_vel, bricks
    global score, life, block_count
    global game_state, end_message
    global bonus_bricks

    # play 상태가 아니면 게임을 업데이트하지 않음
    if game_state != "play":
        return

    # 패들 위치 업데이트
    paddle_rect.x += paddle_dx

    # 패들이 화면 밖으로 나가지 않도록 제한
    if paddle_rect.left < 0:
        paddle_rect.left = 0

    if paddle_rect.right > WIDTH:
        paddle_rect.right = WIDTH

    # 공 위치 업데이트
    ball_pos[0] += ball_vel[0]
    ball_pos[1] += ball_vel[1]

    # 공을 충돌 검사용 사각형으로 변환
    ball_rect = pygame.Rect(
        ball_pos[0] - BALL_RADIUS,
        ball_pos[1] - BALL_RADIUS,
        BALL_RADIUS * 2,
        BALL_RADIUS * 2
    )

    # 왼쪽 벽 충돌
    if ball_pos[0] - BALL_RADIUS <= 0:
        ball_vel[0] = abs(ball_vel[0])

    # 오른쪽 벽 충돌
    if ball_pos[0] + BALL_RADIUS >= WIDTH:
        ball_vel[0] = -abs(ball_vel[0])

    # 위쪽 벽 충돌
    if ball_pos[1] - BALL_RADIUS <= 0:
        ball_vel[1] = abs(ball_vel[1])

    # 패들과 충돌
    if ball_rect.colliderect(paddle_rect):
        if ball_vel[1] > 0:
            ball_vel[1] = -abs(ball_vel[1])
            ball_pos[1] = paddle_rect.top - BALL_RADIUS

    # 벽돌과 충돌
    for brick in bricks[:]:
        if ball_rect.colliderect(brick):

            # 충돌한 벽돌이 보너스 벽돌인지 먼저 확인
            is_bonus = brick in bonus_bricks

            bricks.remove(brick)

            # 보너스 벽돌이면 보너스 목록에서도 제거
            if is_bonus:
                bonus_bricks.remove(brick)
                score += BONUS_SCORE
            else:
                score += NORMAL_SCORE

            ball_vel[1] *= -1
            block_count -= 1

            # 남은 벽돌 수가 0이면 게임 클리어
            if block_count <= 0:
                game_state = "clear"
                end_message = "You Win"
                return

            break

    # 공이 화면 아래로 떨어졌을 때
    if ball_pos[1] - BALL_RADIUS > HEIGHT:
        life -= 1

        # 생명이 0이면 게임 오버
        if life <= 0:
            game_state = "game_over"
            end_message = "Game Over"
            return

        # 생명이 남아 있으면 공을 중앙으로 되돌리고 ready 상태로 변경
        ball_pos = [WIDTH // 2, HEIGHT // 2]
        ball_vel = [BALL_SPEED_X, BALL_SPEED_Y]
        game_state = "ready"


# 종료 화면 출력
def render_end_screen():
    # CHANGED 종료 화면에도 배경 이미지 출력
    if background_img is not None:
        screen.blit(background_img, (0, 0))
    else:
        screen.fill(BLACK)

    message_text = font.render(end_message, True, WHITE)
    message_rect = message_text.get_rect(
        center=(WIDTH // 2, HEIGHT // 2 - 30)
    )
    screen.blit(message_text, message_rect)

    guide_text = font.render("Press any key to exit", True, WHITE)
    guide_rect = guide_text.get_rect(
        center=(WIDTH // 2, HEIGHT // 2 + 20)
    )
    screen.blit(guide_text, guide_rect)


# 4. 출력 처리
def render():
    # 게임 종료 상태이면 종료 화면 출력
    if game_state == "game_over" or game_state == "clear":
        render_end_screen()
        pygame.display.flip()
        return

    # CHANGED 검은 배경 대신 배경 이미지 출력
    if background_img is not None:
        screen.blit(background_img, (0, 0))
    else:
        screen.fill(BLACK)

    # 게임 정보 출력
    text_score = font.render(f"Score: {score}", True, WHITE)
    text_life = font.render(f"Life: {life}", True, WHITE)
    text_blocks = font.render(f"Blocks: {block_count}", True, WHITE)

    score_rect = text_score.get_rect(topleft=(10, 10))
    life_rect = text_life.get_rect(midtop=(WIDTH // 2, 10))
    blocks_rect = text_blocks.get_rect(topright=(WIDTH - 10, 10))

    screen.blit(text_score, score_rect)
    screen.blit(text_life, life_rect)
    screen.blit(text_blocks, blocks_rect)

    # 벽돌 그리기
    for brick in bricks:
        if brick in bonus_bricks:
            pygame.draw.rect(screen, RED, brick)
        else:
            pygame.draw.rect(screen, WHITE, brick)

    # 공 그리기
    pygame.draw.circle(
        screen,
        WHITE,
        (int(ball_pos[0]), int(ball_pos[1])),
        BALL_RADIUS
    )

    # 패들 그리기
    pygame.draw.rect(screen, WHITE, paddle_rect)

    # ready 상태에서 시작 안내 문구 출력
    if game_state == "ready":
        start_text = font.render("Press SPACE to Start", True, WHITE)
        start_rect = start_text.get_rect(
            center=(WIDTH // 2, HEIGHT // 2 + 60)
        )
        screen.blit(start_text, start_rect)

    pygame.display.flip()


# 5. 게임 실행
def run_game():
    init()

    while running:
        handle_input()
        update_game()
        render()
        clock.tick(60)

    pygame.quit()
    sys.exit()


run_game()

 

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함