본문 바로가기

[Pygame] 🧱 벽돌 깨기 게임 만들기 8강 | 충돌 방향 정확히 계산하기

@도마22026. 2. 23. 22:00
728x90


충돌 방향 정확히 계산하기

이번 강의에서는 공이 벽돌이나 패들과 충돌했을 때
어느 방향에서 충돌했는지에 따라 반사 방향을 다르게 처리합니다.

이 작업을 통해
공의 움직임이 훨씬 자연스럽고 예측 가능해집니다.

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

  • 충돌 방향을 계산합니다
  • x축과 y축 반사를 구분합니다
  • 단순 반사가 아닌 정확한 반사를 구현합니다

왜 충돌 방향 계산이 필요한가

이전 강의까지는
벽돌이나 패들에 닿으면 무조건 y 방향 속도만 반전했습니다.

이 방식의 문제점은 다음과 같습니다.

  • 옆면에 맞아도 위아래로만 튕깁니다
  • 공의 이동이 부자연스럽습니다
  • 플레이어 조작의 영향이 줄어듭니다

따라서 충돌 지점을 기준으로
어느 면에 맞았는지를 판단해야 합니다.


충돌 방향 판단의 기본 아이디어

충돌 방향은
공과 대상(Rect)의 겹친 거리(overlap) 를 이용해 판단합니다.

  • 좌우 겹침이 더 작으면 좌우 충돌입니다
  • 상하 겹침이 더 작으면 상하 충돌입니다

즉,
가장 덜 겹친 방향이 충돌 방향입니다.


겹침 거리 계산하기

공과 벽돌이 충돌했을 때
다음 네 방향의 겹침 거리를 계산합니다.

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

이 처리로 인해
좌우에서 맞으면 좌우 반사,
위아래에서 맞으면 상하 반사가 발생합니다.


벽돌 충돌 코드에 적용하기

이제 기존의 벽돌 충돌 코드를
충돌 방향 계산 방식으로 교체합니다.

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

        bricks.remove(brick)
        break

이제 공은
벽돌의 어느 면에 맞았는지에 따라
정확하게 반사됩니다.


패들 충돌에도 동일한 방식 적용하기

패들 역시 사각형이므로
같은 방식의 충돌 방향 계산을 적용할 수 있습니다.

다만 패들은 아래에서만 맞는 구조이므로
이번 강의에서는 기존 y축 반사 방식을 유지합니다.

패들 반사 각도를 조절하는 작업은
다음 단계에서 다룹니다.


현재 단계의 결과

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

  • 공이 벽돌의 옆면에 맞으면 좌우로 튕깁니다
  • 위아래에 맞으면 위아래로 튕깁니다
  • 공의 이동이 훨씬 자연스러워집니다

이제 벽돌깨기 게임의
기본 물리 구조는 완성 단계에 도달했습니다.

다음 강의에서는
점수와 라이프 시스템을 추가해
게임 진행을 명확하게 만듭니다.



전체 코드

더보기
import pygame
import sys

pygame.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)

# FPS 설정
clock = pygame.time.Clock()
FPS = 60

# 패들 설정
paddle_width = 100
paddle_height = 15
paddle_speed = 7

paddle_x = (SCREEN_WIDTH - paddle_width) // 2
paddle_y = SCREEN_HEIGHT - 40

paddle_rect = pygame.Rect(
    paddle_x,
    paddle_y,
    paddle_width,
    paddle_height
)

# 공 설정
ball_radius = 8
ball_x = SCREEN_WIDTH // 2
ball_y = SCREEN_HEIGHT // 2

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 = []

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

        brick_rect = pygame.Rect(
            brick_x,
            brick_y,
            brick_width,
            brick_height
        )

        bricks.append(brick_rect)

# 게임 루프
running = True
while running:
    clock.tick(FPS)

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

    # 패들 입력 처리
    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

    if paddle_rect.left < 0:
        paddle_rect.left = 0
    if paddle_rect.right > SCREEN_WIDTH:
        paddle_rect.right = SCREEN_WIDTH

    # 공 이동
    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_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

    # 벽돌 충돌 처리 (방향 계산)
    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

            bricks.remove(brick)
            break

    # 화면 그리기
    screen.fill(BLACK)
    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)

    pygame.display.flip()

pygame.quit()
sys.exit()

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

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

목차