
충돌 방향 정확히 계산하기
이번 강의에서는 공이 벽돌이나 패들과 충돌했을 때
어느 방향에서 충돌했는지에 따라 반사 방향을 다르게 처리합니다.
이 작업을 통해
공의 움직임이 훨씬 자연스럽고 예측 가능해집니다.
이번 강의의 핵심은 다음과 같습니다.
- 충돌 방향을 계산합니다
- 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()
'⚙️ Python > 🎮 Pygame 실전' 카테고리의 다른 글
| [Pygame] 🧱 벽돌 깨기 게임 만들기 10강 | 아이템 추가 (0) | 2026.02.24 |
|---|---|
| [Pygame] 🧱 벽돌 깨기 게임 만들기 9강 | 점수 & 라이프 시스템 (0) | 2026.02.24 |
| [Pygame] 🧱 벽돌 깨기 게임 만들기 7강 | 공과 벽돌 충돌 (0) | 2026.02.23 |
| [Pygame] 🧱 벽돌 깨기 게임 만들기 6강 | 벽돌(Brick) 생성 구조 (0) | 2026.02.22 |
| [Pygame] 🧱 벽돌 깨기 게임 만들기 5강 | 패들과 공 충돌 처리 (0) | 2026.02.22 |