티스토리 뷰

1. 이번 단원에서 배우는 핵심

이번 단원에서는 벽돌깨기 게임(breakout game)을 만들기 위한 기본 구조를 학습한다. 벽돌깨기 게임은 화면 위쪽에 여러 개의 벽돌(block)을 배치하고, 아래쪽의 패들(paddle)을 움직여 공(ball)을 튕기면서 벽돌을 없애는 게임이다. 초보자에게는 단순해 보이지만, 실제로는 게임 프로그램의 핵심 원리가 모두 들어 있는 좋은 예제이다.

이번 학습에서는 벽돌깨기 게임을 바로 완성하는 것이 아니라, 게임을 만들기 위해 어떤 정보가 필요하고, 각 단계에서 어떤 기능이 실행되는지를 먼저 이해하는 것이 중요하다.

  1. 게임 초기화(initialization)
    게임 창의 크기, 색상, 점수, 생명, 공, 패들, 벽돌의 초기 위치를 정하는 단계이다.
  2. 입력 처리(input handling)
    사용자가 키보드의 왼쪽·오른쪽 방향키를 눌렀는지 확인하고, 패들을 움직이는 단계이다.
  3. 게임 업데이트(game update)
    공의 위치를 바꾸고, 공이 벽이나 패들, 벽돌과 충돌하였는지 판단하는 단계이다.
  4. 출력 처리(rendering)
    현재 점수, 생명, 남은 벽돌 개수, 공, 패들, 벽돌을 화면에 다시 그리는 단계이다.
  5. 게임 종료(game over)
    생명이 0이 되거나 모든 벽돌을 제거하였을 때 게임을 끝내는 단계이다.

필요한 정보

벽돌깨기 게임을 만들기 위해서는 먼저 게임에 필요한 정보를 정하여야 한다.

예를 들어 화면 크기(width, height), 벽돌의 개수(block count), 공의 위치(ball position), 공의 속도(ball speed), 패들의 위치(paddle position), 점수(score), 생명(life) 같은 정보가 필요하다. 이 정보들은 게임이 실행되는 동안 계속 바뀐다. 공은 계속 움직이고, 벽돌은 충돌하면 사라지고, 점수는 증가하며, 생명은 줄어들 수 있다. 따라서 게임 프로그램에서는 이러한 값을 변수(variable)로 저장하여 관리한다.

함수 역할
init() 게임 초기화
handle_input() 키보드와 마우스 입력 처리
update_game() 공 이동, 충돌, 점수, 생명 처리
render() 화면 출력
run_game() 전체 게임 실행

1.초기화면 구성

프롬프트

기본 게임 구조에서 아직 공을 움직이지 말고, 벽돌깨기 게임의 초기 화면만 구성한다.
벽돌(brick)을 화면 상단에 5행 8열로,
공(ball)을 반지름 10으로 화면 중앙에,
패들(paddle)을 화면 하단 중앙에 배치해줘

자세히

기본 pygame 게임 뼈대에서 시작하여 벽돌깨기 게임의 초기 화면을 만들어라.

현재 단계에서는 화면에 다음 요소만 출력한다.

- 공: 화면 중앙
- 패들: 화면 하단 중앙
- 벽돌: 화면 상단 5행 8열

조건:
- 기존 5개 함수 구조 유지
- 변수명 영어 사용
- 추가/수정 코드는 #ADDED 표시
- 공 이동과 충돌 처리는 아직 작성하지 않음
- 코드 아래에 초보자용 설명 작성

벽돌깨기 초기 화면 구성 코드(step01.py)

import pygame
import sys


# 전역 변수
WIDTH, HEIGHT = 600, 800  #ADDED 화면 크기 600x800으로 수정
screen = None
clock = None
running = True

# 색상
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

#ADDED 공 정보
BALL_RADIUS = 10
ball_pos = None

#ADDED 패들 정보
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
paddle_rect = None

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

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


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

    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("벽돌깨기 초기 화면")
    clock = pygame.time.Clock()

    #ADDED 초기 점수, 생명, 남은 블록 개수
    score = 0
    life = 3
    block_count = BRICK_ROWS * BRICK_COLS

    #ADDED 공 초기 위치: 화면 중앙
    ball_pos = (WIDTH // 2, HEIGHT // 2)

    #ADDED 패들 초기 위치: 화면 하단 중앙
    paddle_x = WIDTH // 2 - PADDLE_WIDTH // 2
    paddle_y = HEIGHT - 50
    paddle_rect = pygame.Rect(
        paddle_x,
        paddle_y,
        PADDLE_WIDTH,
        PADDLE_HEIGHT
    )

    #ADDED 벽돌 초기 생성: 화면 상단 5행 8열
    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)


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

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


# 3. 게임 업데이트
def update_game():
    pass  # 현재 단계에서는 공 이동과 충돌 처리를 하지 않음


# 4. 출력 처리
def render():
    screen.fill(BLACK)

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

    #ADDED 공 그리기
    pygame.draw.circle(screen, WHITE, ball_pos, BALL_RADIUS)

    #ADDED 패들 그리기
    pygame.draw.rect(screen, WHITE, paddle_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()

벽돌깨기 초기 화면 코드 단계별 설명

현재 코드는 벽돌깨기 게임의 초기 화면만 구성하는 단계이다.
아직 공은 움직이지 않고, 패들도 움직이지 않으며, 벽돌도 사라지지 않는다.
이번 단계의 목표는 화면에 벽돌(brick), 공(ball), 패들(paddle)을 정확한 위치에 배치하는 것이다.

1. 라이브러리(library)

import pygame
import sys

2. 전역변수와 초기값

WIDTH, HEIGHT = 600, 800
  • 게임 화면의 크기를 600 × 800으로 정한다.
    WIDTH는 화면의 너비(width), HEIGHT는 화면의 높이(height)이다.
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
  • 배경은 검은색, 공·패들·벽돌은 흰색으로 그리기 위한 색상값이다.
BALL_RADIUS = 10
ball_pos = None
  • 공의 반지름은 10으로 정한다.
  • 공의 실제 위치는 초기화 함수에서 화면 중앙으로 설정한다.
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
paddle_rect = None
  • 패들은 가로 100, 세로 15 크기의 사각형으로 만든다.
  • 화면 하단 중앙에 배치된다.
BRICK_ROWS = 5
BRICK_COLS = 8
bricks = []
  • 벽돌은 5행 8열로 만든다.
  • 전체 벽돌 개수는 40개이다.
  • bricks 리스트에는 생성된 벽돌들이 저장된다.

3. 초기화

def init():

이 단계에서 하는 일은 다음과 같다.

  • 화면을 만든다.
  • 게임 제목을 설정한다.
  • 공을 화면 중앙에 배치한다.
  • 패들을 화면 하단 중앙에 배치한다.
  • 벽돌을 화면 상단에 5행 8열로 생성한다.
  • 점수는 0, 생명은 3, 남은 블록 개수는 40으로 설정한다.
  • 초기화는 게임이 시작될 때 한 번 실행되는 준비 단계이다.

4. 입력

def handle_input():
 
  • 현재 단계에서는 패들을 움직이지 않는다. 따라서 키보드 입력은 아직 처리하지 않는다.

 

 

5. 업데이트

def update_game():
    pass
 

하지만 현재는 초기 화면만 구성하는 단계이므로 공이 움직이지 않는다.
그래서 pass를 사용하여 아무 작업도 하지 않도록 둔 것이다.

다음 단계에서 이 부분에 공 이동, 충돌 처리, 점수 증가, 생명 감소 기능이 추가된다.


6. 출력

def render():

먼저 화면 전체를 검은색으로 칠한다.

screen.fill(BLACK)
 

그다음 벽돌을 그린다.

for brick in bricks:
    pygame.draw.rect(screen, WHITE, brick)
 
  • 벽돌은 여러 개이므로 bricks 리스트에서 하나씩 꺼내어 그린다.
  • 공은 원으로 그린다.
pygame.draw.circle(screen, WHITE, ball_pos, BALL_RADIUS)
  • 패들은 사각형으로 그린다.
pygame.draw.rect(screen, WHITE, paddle_rect)
  • 마지막으로 화면을 갱신한다.
pygame.display.flip()
  • 이 코드가 실행되어야 실제 화면에 공, 패들, 벽돌이 보인다.

7. 실행

def run_game():
handle_input()
update_game()
render()
clock.tick(60)
  • 현재는 업데이트에서 아무 변화가 없기 때문에 화면은 움직이지 않는다.

2. 공의 움직임과 충돌 구현

이번 단계에서는 이전에 만든 초기 화면 코드에 다음 기능을 추가하면 된다.

1. 공의 속도 추가

  • 공이 움직이려면 위치만 있어서는 안 된다. 공이 어느 방향으로 얼마나 이동할지 저장하는 값이 필요하다.
ball_vel = [5, 5]  #ADDED
  • ball_vel은 공의 속도(ball velocity)이다.
  • ball_vel[0]은 x방향 이동값이다.
  • ball_vel[1]은 y방향 이동값이다.
  • 즉, 공은 매 프레임마다 오른쪽으로 5, 아래쪽으로 5만큼 이동한다.

2. 공 위치 업데이트

update_game() 함수에서 공의 위치를 계속 바꾼다.

ball_pos[0] += ball_vel[0]  #ADDED
ball_pos[1] += ball_vel[1]  #ADDED
  • 이 코드는 공의 현재 위치에 속도를 더하는 것이다.
  • 예를 들어 공의 위치가 [300, 400]이고 속도가 [5, 5]이면 다음 위치는 [305, 405]가 된다.

3. 벽과 충돌 처리

공이 왼쪽 벽이나 오른쪽 벽에 닿으면 x방향을 반대로 바꾼다.

if ball_pos[0] - BALL_RADIUS <= 0 or ball_pos[0] + BALL_RADIUS >= WIDTH:
    ball_vel[0] *= -1  #ADDED
  • 공이 위쪽 벽에 닿으면 y방향을 반대로 바꾼다.
if ball_pos[1] - BALL_RADIUS <= 0:
    ball_vel[1] *= -1  #ADDED
  • *= -1은 방향을 반대로 바꾸는 코드이다.

4. 공을 Rect로 변환

패들, 벽돌과 충돌하려면 공도 사각형 영역으로 잠시 변환하여 검사한다.

ball_rect = pygame.Rect(
    ball_pos[0] - BALL_RADIUS,
    ball_pos[1] - BALL_RADIUS,
    BALL_RADIUS * 2,
    BALL_RADIUS * 2
)  #ADDED
 
  • 공은 원이지만, 충돌 검사를 쉽게 하기 위해 공을 감싸는 사각형으로 생각하는 것이다.

5. 패들과 충돌 처리

공이 패들과 닿으면 위쪽으로 튕기도록 y방향을 반대로 바꾼다.

if ball_rect.colliderect(paddle_rect):
    ball_vel[1] *= -1  #ADDED
  • colliderect()는 두 사각형이 서로 닿았는지 검사하는 함수이다.

6. 벽돌과 충돌 처리

공이 벽돌에 닿으면 해당 벽돌을 제거하고, 공의 y방향을 반대로 바꾼다.

for brick in bricks[:]:
    if ball_rect.colliderect(brick):
        bricks.remove(brick)  #ADDED
        ball_vel[1] *= -1     #ADDED
        break
 
  • bricks[:]는 벽돌 리스트의 복사본이다.반복 중에 벽돌을 삭제할 때 오류를 줄이기 위해 사용한다.

7. 공이 바닥으로 떨어졌을 때

공이 화면 아래로 떨어지면 공을 다시 중앙으로 보낸다.

if ball_pos[1] + BALL_RADIUS >= HEIGHT:
    ball_pos[0] = WIDTH // 2      #ADDED
    ball_pos[1] = HEIGHT // 2     #ADDED
    ball_vel[1] = -5              #ADDED
  • 아직 생명 감소는 구현하지 않고, 공 위치만 리셋하는 단계로 두자.

이번 단계의 핵심 정리

공의 움직임은 공의 위치에 속도를 계속 더하는 방식으로 구현한다. 충돌은 공이 벽, 패들, 벽돌에 닿았는지 확인한 뒤 방향을 반대로 바꾸는 방식으로 처리한다. 즉, 이번 단계의 핵심은 다음 두 가지이다.

공 위치 = 공 위치 + 공 속도
충돌하면 속도의 방향을 반대로 변경

2단계 추가 전체 코드 (step02.py)

공 이동 + 벽 충돌 + 패들 충돌 + 벽돌 충돌

import pygame
import sys


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

# 색상
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

# 공 정보
BALL_RADIUS = 10
ball_pos = None
ball_vel = [5, 5]  #ADDED 공의 이동 속도

# 패들 정보
PADDLE_WIDTH = 100
PADDLE_HEIGHT = 15
paddle_rect = None
paddle_speed = 7  #ADDED 패들 이동 속도

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

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


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

    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("벽돌깨기 - 공 이동과 충돌")
    clock = pygame.time.Clock()

    score = 0
    life = 3
    block_count = BRICK_ROWS * BRICK_COLS

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

    # 패들 초기 위치
    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)


# 2. 입력 처리
def handle_input():
    global running, paddle_rect

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

    #ADDED 키보드 좌우 입력으로 패들 이동
    keys = pygame.key.get_pressed()

    if keys[pygame.K_LEFT] and paddle_rect.left > 0:
        paddle_rect.x -= paddle_speed

    if keys[pygame.K_RIGHT] and paddle_rect.right < WIDTH:
        paddle_rect.x += paddle_speed


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

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

    #ADDED 왼쪽 벽, 오른쪽 벽 충돌
    if ball_pos[0] - BALL_RADIUS <= 0 or ball_pos[0] + BALL_RADIUS >= WIDTH:
        ball_vel[0] *= -1

    #ADDED 위쪽 벽 충돌
    if ball_pos[1] - BALL_RADIUS <= 0:
        ball_vel[1] *= -1

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

    #ADDED 공과 패들 충돌
    if ball_rect.colliderect(paddle_rect):
        ball_vel[1] *= -1

    #ADDED 공과 벽돌 충돌
    for brick in bricks[:]:
        if ball_rect.colliderect(brick):
            bricks.remove(brick)
            ball_vel[1] *= -1
            score += 10
            block_count -= 1
            break

    #ADDED 공이 화면 아래로 떨어졌을 때
    if ball_pos[1] + BALL_RADIUS >= HEIGHT:
        life -= 1
        ball_pos = [WIDTH // 2, HEIGHT // 2]
        ball_vel = [5, -5]

        if life <= 0:
            running = False


# 4. 출력 처리
def render():
    screen.fill(BLACK)

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

    # 공 그리기
    pygame.draw.circle(screen, WHITE, ball_pos, BALL_RADIUS)

    # 패들 그리기
    pygame.draw.rect(screen, WHITE, paddle_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()

위 코드에서 새로 추가된 핵심은 ball_vel이다.
공의 위치에 ball_vel 값을 계속 더하여 공을 움직이고, 벽·패들·벽돌에 닿으면 속도 방향을 반대로 바꾸도록 하였다.

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
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
글 보관함