티스토리 뷰

3. 충돌(탄성)

이번 3절은 충돌을 다루는 장이다.
여기서 충돌은 게임 속 물체가 화면 벽이나 다른 물체에 닿았는지 판단하고, 그 결과를 처리하는 것을 의미한다.

예를 들어 공이 벽에 닿으면 튕겨 나가고, 캐릭터가 적과 닿으면 생명이 줄어들 수 있다.
벽돌 게임에서는 공이 벽돌에 닿으면 벽돌이 사라지고 점수가 올라간다.

실습의 목표

마우스 클릭
→ 공 생성
→ 공 이동
→ 벽과 충돌
→ 속도 방향 반전
→ 탄성 계수 적용
→ 다시 튕김
 

게임 업데이트: 화면 창과 충돌 처리


게임 업데이트 화면 창과 충돌 처리

공이 움직이다가 화면의 벽에 닿으면 그냥 밖으로 나가면 안 된다. 벽에 닿았는지 확인하고, 닿았다면 방향을 바꾸어야 한다.
예를 들어 공이 왼쪽 또는 오른쪽 벽에 닿으면 x방향 속도를 반대로 바꾼다.

if circle_pos[0] <= circle_radius or circle_pos[0] >= WIDTH - circle_radius:
    circle_vel[0] = -circle_vel[0]
공이 위쪽 또는 아래쪽 벽에 닿으면 y방향 속도를 반대로 바꾼다.
if circle_pos[1] <= circle_radius or circle_pos[1] >= HEIGHT - circle_radius:
    circle_vel[1] = -circle_vel[1]
여기서 핵심은 - 이다.
circle_vel[0] = -circle_vel[0]
속도에 -를 붙이면 방향이 반대로 바뀐다.
오른쪽으로 가던 공 → 왼쪽으로 이동
아래로 가던 공 → 위로 이동
 

게임 업데이트: 탄성 운동 코드 추가

탄성 운동(elastic motion)은 물체가 벽에 닿았을 때 튕겨 나가는 움직임이다. 공이 벽에 부딪혔을 때 속도 방향을 반대로 바꾸면, 화면에서는 공이 튕기는 것처럼 보인다. 

circle_vel[0] = -circle_vel[0]
circle_vel[1] = -circle_vel[1]
 

출력 처리: 마우스 클릭 위치에서 원 발사

 

출력 처리에서는 현재 원의 위치를 화면에 그린다.

pygame.draw.circle(screen, WHITE, circle_pos, circle_radius)
 

마우스를 클릭한 위치에서 원이 생성되고, update_game()에서 위치가 계속 바뀌면 화면에서는 원이 움직이는 것처럼 보인다.

즉, 전체 흐름은 다음과 같다.

마우스 클릭 → 원 생성
게임 업데이트 → 원 이동
게임 업데이트 → 벽과 충돌하면 방향 변경
출력 처리 → 바뀐 위치에 원 그리기

마우스를 클릭한 위치에 공을 생성하고
x축 500, y축 400 속도로 움직이게 해줘.
그리고 벽에 충돌하면 탄성 계수 0.7을 곱해서 공의 속도를 줄여줘.
공의 궤적을 노란색으로 표시해줘

최종 코드 (ex03_elastic.py)

import pygame
import sys


# 전역 변수
WIDTH, HEIGHT = 800, 800
FPS = 60

screen = None
clock = None
running = True

# 색상
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
YELLOW = (255, 255, 0)  # ADDED 궤적 색상

# 공 정보
circle_pos = None            # ADDED 공의 위치 [x, y]
circle_vel = [0.0, 0.0]      # ADDED 공의 속도 [vx, vy]
trails = []                  # ADDED 공의 궤적 저장
fired = False                # ADDED 공이 움직이는지 확인

# 속도와 반지름
speed_x = 500                # ADDED x방향 속도
speed_y = 400                # ADDED y방향 속도
CIRCLE_RADIUS = 5            # ADDED 공의 반지름

# 탄성 계수
ELASTICITY = 0.7             # ADDED 탄성 계수


# 1. 게임 초기화
def init():
    global screen, clock

    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("충돌 탄성 운동")
    clock = pygame.time.Clock()


# 2. 입력 처리
def handle_input():
    global running, circle_pos, circle_vel, trails, fired

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

        # ADDED 마우스 왼쪽 버튼을 클릭하면 공 생성
        elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            circle_pos = list(event.pos)   # ADDED 클릭한 위치를 공의 위치로 저장
            circle_vel[0] = speed_x        # ADDED x방향 속도 설정
            circle_vel[1] = speed_y        # ADDED y방향 속도 설정
            trails = []                    # ADDED 이전 궤적 삭제
            fired = True                   # ADDED 공 움직임 시작


# 3. 게임 업데이트
def update_game():
    global circle_pos, circle_vel, fired, trails

    dt = 1 / FPS

    if fired and circle_pos is not None:
        # ADDED 공 위치 업데이트
        circle_pos[0] += circle_vel[0] * dt
        circle_pos[1] += circle_vel[1] * dt

        # ADDED 왼쪽 벽 충돌 처리
        if circle_pos[0] <= CIRCLE_RADIUS:
            circle_pos[0] = CIRCLE_RADIUS
            circle_vel[0] = -circle_vel[0] * ELASTICITY

        # ADDED 오른쪽 벽 충돌 처리
        elif circle_pos[0] >= WIDTH - CIRCLE_RADIUS:
            circle_pos[0] = WIDTH - CIRCLE_RADIUS
            circle_vel[0] = -circle_vel[0] * ELASTICITY

        # ADDED 위쪽 벽 충돌 처리
        if circle_pos[1] <= CIRCLE_RADIUS:
            circle_pos[1] = CIRCLE_RADIUS
            circle_vel[1] = -circle_vel[1] * ELASTICITY

        # ADDED 아래쪽 벽 충돌 처리
        elif circle_pos[1] >= HEIGHT - CIRCLE_RADIUS:
            circle_pos[1] = HEIGHT - CIRCLE_RADIUS
            circle_vel[1] = -circle_vel[1] * ELASTICITY

        # ADDED 공의 궤적 저장
        trails.append((int(circle_pos[0]), int(circle_pos[1])))


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

    # ADDED 궤적 출력
    for trail in trails:
        pygame.draw.circle(screen, YELLOW, trail, 2)

    # ADDED 공 출력
    if circle_pos is not None:
        pygame.draw.circle(
            screen,
            WHITE,
            (int(circle_pos[0]), int(circle_pos[1])),
            CIRCLE_RADIUS
        )

    pygame.display.flip()


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

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

    pygame.quit()
    sys.exit()


run_game()

충돌 탄성 운동 코드 단계별 설명

1. 필수 라이브러리

import pygame
import sys

 

2. 전역 변수 설정

화면 크기와 FPS

WIDTH, HEIGHT = 800, 800
FPS = 60
 
  • FPS = 60은 게임 화면을 1초에 60번 갱신하겠다는 뜻이다.

색상 설정

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
YELLOW = (255, 255, 0)

공 정보 변수

circle_pos = None
circle_vel = [0.0, 0.0]
trails = []
fired = False
  • circle_pos 공의 현재 위치
  • circle_vel 공의 속도: circle_vel[0]은 공이 좌우로 움직이는 속도이고, circle_vel[1]은 공이 위아래로 움직이는 속도이다.
  • trails 공이 지나간 위치 목
  • fired 공이 움직이고 있는지 확인

속도와 탄성 계수

speed_x = 500
speed_y = 400
CIRCLE_RADIUS = 5
ELASTICITY = 0.7
speed_x 공의 x방향 초기 속도
speed_y 공의 y방향 초기 속도
CIRCLE_RADIUS 공의 반지름
ELASTICITY 벽에 부딪힌 뒤 튕기는 정도
  • speed_x = 500은 공이 오른쪽 방향으로 움직이기 시작한다는 의미이다.
  • speed_y = 400은 공이 아래쪽 방향으로 움직이기 시작한다는 의미이다.

탄성 계수

ELASTICITY = 0.7
탄성 계수(elasticity)는 공이 벽에 부딪힌 뒤 속도가 얼마나 유지되는지를 나타낸다.
1.0 속도 손실 없이 튕김
0.7 속도가 조금 줄어들며 튕김
0.3 약하게 튕김
0.0 거의 튕기지 않음

3. 게임 초기화

def init():
    global screen, clock

    pygame.init()
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("충돌 탄성 운동")
    clock = pygame.time.Clock()
 
pygame.init() pygame 기능 시작
set_mode() 게임 창 생성
set_caption() 게임 창 제목 설정
Clock() 게임 속도 조절용 시계 생성

 


4. 입력 처리

 

 

마우스 클릭으로 공 생성

elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
    circle_pos = list(event.pos)
    circle_vel[0] = speed_x
    circle_vel[1] = speed_y
    trails = []
    fired = True
MOUSEBUTTONDOWN 마우스 버튼을 눌렀는지 확인
event.button == 1 왼쪽 마우스 버튼인지 확인
circle_pos = list(event.pos) 클릭한 위치를 공의 위치로 저장
circle_vel[0] = speed_x x방향 속도 설정
circle_vel[1] = speed_y y방향 속도 설정
trails = [] 이전 궤적 삭제
fired = True 공 움직임 시작

 

왜 list(event.pos)를 사용하는가?

event.pos는 (x, y) 형태의 튜플(tuple)이다. 하지만 공은 계속 움직이므로 x좌표와 y좌표가 바뀌어야 한다. 
튜플은 값을 직접 바꿀 수 없고, 리스트는 값을 바꿀 수 있다.
그래서 다음처럼 리스트로 변환한다.

circle_pos = list(event.pos)

5. 게임 업데이트

update_game() 함수는 공의 위치를 바꾸고, 벽에 닿았는지 검사한다.

한 프레임 시간

dt = 1 / FPS
FPS = 60이므로 dt는 1/60초이다.

즉, 게임 루프가 한 번 반복될 때마다 1/60초만큼 시간이 흐른다고 보고 계산한다.

공이있을 때만 업데이트

if fired and circle_pos is not None:
 

이동

circle_pos[0] += circle_vel[0] * dt
circle_pos[1] += circle_vel[1] * dt
 
circle_pos[0] 공의 x좌표
circle_pos[1] 공의 y좌표
circle_vel[0] x방향 속도
circle_vel[1] y방향 속도

 

새 위치 = 현재 위치 + 속도 × 시간
 

 

벽 충돌 처리

공이 화면 밖으로 나가지 않도록, 네 방향 벽과 충돌하는지 확인한다.

왼쪽 벽 충돌

if circle_pos[0] <= CIRCLE_RADIUS:
    circle_pos[0] = CIRCLE_RADIUS
    circle_vel[0] = -circle_vel[0] * ELASTICITY
공의 x좌표가 너무 작아지면 왼쪽 벽에 닿은 것이다.
circle_pos[0] = CIRCLE_RADIUS
이 코드는 공이 화면 밖으로 나가지 않도록 위치를 벽 안쪽에 고정한다.
circle_vel[0] = -circle_vel[0] * ELASTICITY
이 코드는 x방향 속도를 반대로 바꾸고, 속도를 70%로 줄인다.

 

오른쪽 벽 충돌

elif circle_pos[0] >= WIDTH - CIRCLE_RADIUS:
    circle_pos[0] = WIDTH - CIRCLE_RADIUS
    circle_vel[0] = -circle_vel[0] * ELASTICITY
공의 x좌표가 WIDTH - CIRCLE_RADIUS보다 커지면 오른쪽 벽에 닿은 것이다.공의 중심이 화면 끝까지 가면 공의 일부가 화면 밖으로 나간다.그래서 반지름만큼 빼서 처리한다.

 

위쪽 벽 충돌

if circle_pos[1] <= CIRCLE_RADIUS:
    circle_pos[1] = CIRCLE_RADIUS
    circle_vel[1] = -circle_vel[1] * ELASTICITY
공의 y좌표가 너무 작아지면 위쪽 벽에 닿은 것이다.이때 y방향 속도를 반대로 바꾸어 아래쪽으로 튕기게 한다.

 

아래쪽 벽 충돌

elif circle_pos[1] >= HEIGHT - CIRCLE_RADIUS:
    circle_pos[1] = HEIGHT - CIRCLE_RADIUS
    circle_vel[1] = -circle_vel[1] * ELASTICITY
공의 y좌표가 HEIGHT - CIRCLE_RADIUS보다 커지면 아래쪽 벽에 닿은 것이다.이때 y방향 속도를 반대로 바꾸어 위쪽으로 튕기게 한다.

속도 반전의 의미

 

 
여기서 -는 방향을 반대로 바꾸는 역할이다.
오른쪽으로 이동 → 왼쪽으로 이동
왼쪽으로 이동 → 오른쪽으로 이동
아래로 이동 → 위로 이동
위로 이동 → 아래로 이동
 

* ELASTICITY는 속도를 줄이는 역할이다.

속도 500 → 500 × 0.7 = 350
즉, 벽에 부딪힐 때마다 공의 속도가 조금씩 줄어든다.

 

궤적 저장

trails.append((int(circle_pos[0]), int(circle_pos[1])))
공이 움직일 때마다 현재 위치를 trails 리스트에 저장한다.int()를 사용하는 이유는 pygame에서 원을 그릴 때 좌표를 정수로 사용하는 것이 안전하기 때문이다.저장된 위치는 출력 처리에서 노란색 점으로 표시된다.

 

6. 출력 처리

 

 

 

궤적 출력

for trail in trails:
    pygame.draw.circle(screen, YELLOW, trail, 2)
 

trails에 저장된 위치들을 노란색 작은 점으로 그린다.
이 점들이 모이면 공이 지나간 길이 된다.

공 출력

if circle_pos is not None:
    pygame.draw.circle(
        screen,
        WHITE,
        (int(circle_pos[0]), int(circle_pos[1])),
        CIRCLE_RADIUS
    )
공의 현재 위치에 흰색 원을 그린다.마우스를 클릭하기 전에는 circle_pos가 None이므로 공을 그리지 않는다.

 


7. 게임 실행

while running:
    handle_input()
    update_game()
    render()
    clock.tick(FPS)
입력 처리 → 게임 업데이트 → 출력 처리

 


전체 동작 정리

1. 게임 창이 열린다.
2. 사용자가 마우스 왼쪽 버튼을 클릭한다.
3. 클릭한 위치에 흰색 공이 생긴다.
4. 공은 x방향 500, y방향 400의 속도로 움직인다.
5. 공이 벽에 닿으면 방향이 반대로 바뀐다.
6. 탄성 계수 0.7 때문에 속도가 조금 줄어든다.
7. 공이 지나간 위치는 노란색 점으로 남는다

실습 3-1: 탄성 계수 변경하기1

이 실습은 공이 벽에 부딪힐 때 벽마다 다른 탄성계수(elasticity)를 적용하는 예제이다.
기존 코드에서는 모든 벽에 같은 탄성계수 0.7을 사용하였다.
이번에는 왼쪽, 오른쪽, 위쪽, 아래쪽 벽마다 서로 다른 값을 적용한다.

프롬프트

위 충돌 탄성 운동 코드에서 탄성계수 ELASTICITY 값을 키보드로 변경할 수 있게 해줘.
↑ 키를 누르면 탄성계수가 증가하고, ↓ 키를 누르면 탄성계수가 감소하도록 해줘.
탄성계수는 0.0부터 1.0 사이로 제한해줘.
현재 탄성계수 값은 화면 좌상단에 표시해줘.
기존 5개 함수 구조는 유지하고, 추가 코드는 #ADDED로 표시해줘.

추가코드

1.탄성계수 변수 설정

# 탄성 계수
LEFT_ELASTICITY = 0.5        # ADDED 왼쪽 벽 탄성 계수
RIGHT_ELASTICITY = 1.0       # ADDED 오른쪽 벽 탄성 계수
TOP_ELASTICITY = 0.7         # ADDED 위쪽 벽 탄성 계수
BOTTOM_ELASTICITY = 0.3      # ADDED 아래쪽 벽 탄성 계수

2. 충돌 수정 코드 (ex03_elastic2.py)

       # ADDED 왼쪽 벽 충돌 처리
        if circle_pos[0] <= CIRCLE_RADIUS:
            circle_pos[0] = CIRCLE_RADIUS
            circle_vel[0] = -circle_vel[0] * LEFT_ELASTICITY

        # ADDED 오른쪽 벽 충돌 처리
        elif circle_pos[0] >= WIDTH - CIRCLE_RADIUS:
            circle_pos[0] = WIDTH - CIRCLE_RADIUS
            circle_vel[0] = -circle_vel[0] * RIGHT_ELASTICITY

        # ADDED 위쪽 벽 충돌 처리
        if circle_pos[1] <= CIRCLE_RADIUS:
            circle_pos[1] = CIRCLE_RADIUS
            circle_vel[1] = -circle_vel[1] * TOP_ELASTICITY

        # ADDED 아래쪽 벽 충돌 처리
        elif circle_pos[1] >= HEIGHT - CIRCLE_RADIUS:
            circle_pos[1] = HEIGHT - CIRCLE_RADIUS
            circle_vel[1] = -circle_vel[1] * BOTTOM_ELASTICITY

실습 3-2: 탄성 계수 변경하기1

프롬프트

위 충돌 탄성 운동 코드에 이어서, 탄성계수 ELASTICITY 값을 1.2로 변경해줘. 
공이 벽에 충돌할 때 방향은 반대로 바뀌고, 
속도는 1.2배가 되어 점점 빨라지도록 해줘. 
기존 함수 구조는 유지하고, 수정한 부분에는 #ADDED를 붙여줘.

추가코드

파일 ex03_elastic.py에서 탄성계수만 변경

 

# 탄성 계수
ELASTICITY = 1.2  # ADDED 벽에 부딪힐 때마다 속도가 증가
탄성 계수 결과
0.7 부딪힐 때마다 속도가 줄어듦
1.0 거의 같은 속도로 튕김
1.2 부딪힐 때마다 속도가 빨라짐

탄성 계수가 1보다 작으면 속도가 줄어들고, 1보다 크면 속도가 증가한다.
1.2는 실제 물리라기보다 게임에서 공이 점점 빨라지는 효과를 만들기 위한 값이다.

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