Search
Duplicate

파이썬_물리엔진

1.파이썬 물리엔진 시각화를 위한 라이브러리들

vpython vs pymunk 핵심 비교

항목
vpython
pymunk
차원
3D
2D
주 용도
물리 개념 시각화 (교육용)
실제 물리 엔진 시뮬레이션 (게임/연구)
물리 기능
단순 운동 (속도, 위치, 중력)
충돌, 회전, 마찰, 반발력 등 고급 물리
시각화
자체 3D 렌더링 내장
별도 필요 (예: pygame)
학습 난이도
쉬움 (직관적)
중간 ~ 어려움 (구조적 접근)
설치
pip install vpython
pip install pymunk

사용 권장 상황

vpython → 중고등학생, 대학 기초 물리 교육 시각화
pymunk → 2D 게임 물리 구현, 충돌/역학 기반 시뮬레이션

1-1. 낙하 운동 시뮬레이션(vpython)

[주의]
해당 코드는 .ipynb이 아닌 파이썬 스크립트 형태로 실행해야 함
# vpython 자유 낙하 시뮬레이션 from vpython import sphere, vector, rate, color, scene, box g = 9.8 # 중력 가속도 dt = 0.01 # 시간 간격 v = 0 # 초기 속도 y = 5 # 초기 높이 # 바닥 추가 - 위치를 더 아래로 조정 (-0.1 → -2.0) floor = box(pos=vector(0, -2.0, 0), size=vector(10, 0.2, 10), color=color.green) ball = sphere(pos=vector(0, y, 0), radius=0.2, color=color.red, make_trail=True) # 공이 실제 바닥에 닿을 때까지 계속 # 바닥 위치 + 바닥 높이의 절반 + 공의 반지름 = 공이 바닥에 닿는 위치 floor_top = floor.pos.y + floor.size.y/2 # 바닥 윗면의 y좌표 while ball.pos.y > floor_top + ball.radius: rate(100) # 1초에 100프레임 v = v - g * dt y = y + v * dt ball.pos.y = y # 시뮬레이션이 끝난 후 사용자 클릭을 기다림 scene.append_to_caption("\n시뮬레이션 완료. 클릭하면 종료됩니다.") scene.waitfor('click')
Python
복사

1-2. 낙하 운동 시뮬레이션(pymunk활용 2D)

import pygame, pymunk, pymunk.pygame_util # pip install pygame으로 pygame 설치, pip install pymunk로 pymunk 설치# 1. 게임 초기화 (game initialize) pygame.init() # 2. 게임창 옵션 설정 (window option) size = (400, 400) # 게임창 크기 (window size) screen = pygame.display.set_mode(size) title = "Physics Simulator" # 창 제목 (window title) pygame.display.set_caption(title)
Python
복사

2. pymunk라이브러리

1.
’munk’이름의 유래: Chipmunk라는 2D 물리 엔진에서 유래
2.
Chipmunk2D :C 언어로 작성된 경량 2D 물리 엔진

2-1. 물리엔진

1.
물리 엔진이란?
게임 개발, 시뮬레이션 등 다양한 분야에서 현실적인 물리 현상을 구현하는 데 필수
정확히는 ‘물리 법칙을 컴퓨터 시뮬레이션으로 구현한 프로그램’
예를 들어, 공이 바닥에 닿아 튕겨 오르는 효과를 자연스럽게 표현가능
만약, 물리 엔진 없이 구현하려면 충돌 후 속도와 방향 등을 직접 계산해야 하는 어려움 존재재
[참고 사이트]
https://blog.naver.com/jsk6824/223031525485

2-2. pymunk의 특징

1.
pymunk는 물리 문제를 해결하기 위해 각 물체의 위치를 시간 별로 계산함
2.
pygame은 pymunk에서 계산된 물체 위치를 화면에 표시하는 역할을 하며, 따라서 물체는 실시간으로 위치가 변하게 됨
주로 파이게임과 같이 불러와서 사용
pygame창에서는 왼쪽 상단이 (0,0)임
1.
pymknk 활용을 위한 기초
space: 시뮬레이션 공간을 의미
body: 물체를 의미
물체의의 질량, 속도, 위치, 특성을 포함
shape: 물체의 형태를 나타내고 주로 원, 삼각형, 사각형 등
충돌 검사 기능을 가짐

3.기초

1.
초기화
화면 구성
2.
공간 만들기
space 생성
중력 설정
3.
물리객체 생성
바닥생성: 마찰력, 탄성 등을 설정 >바닥 (static body + segment)
설정 후 space에 등록
공생성: 모양, 마찰력, 탄성 등을 설정 >(dynamic body + circle)
설정 후 space에 등록
4.
루프처리
FPS 조절
시뮬레이션 업데이트
이벤트 감지 (QUIT)
화면 렌더링 및 업데이트
#바닥까지 만들어보기 import pygame, pymunk, pymunk.pygame_util # 1. 게임 초기화 (game initialize) pygame.init() # 2. 게임창 옵션 설정 (window option) size = (800, 800) # 게임창 크기 (window size) screen = pygame.display.set_mode(size) title = "Physics Simulator" # 창 제목 (window title) pygame.display.set_caption(title) # 3. 게임 내 필요한 설정 (option for game) clock = pygame.time.Clock() # 시계 (clock) # 공간 만들기 (space) space = pymunk.Space() # 중력의 방향과 세기를 설정 space.gravity = (0, 980) # pygame의 DrawOptions를 사용하여 화면에 그리기 위한 옵션 설정 draw_options = pymunk.pygame_util.DrawOptions(screen) # 바닥 만들기(고정된 type이므로 STATIC으로 설정) floor = pymunk.Body(body_type = pymunk.Body.STATIC) floor.position = (0, size[1]-50) floor_shape = pymunk.Segment(floor, (0,0), (size[0],-100), 1) floor_shape.elasticity = 1 # 탄성 계수 (elasticity) 설정 floor_shape.friction = 0.2 # 마찰 계수 (friction) 설정 # space에 바닥을 추가 space.add(floor, floor_shape) # 4. 메인 이벤트 (main event) running = True while running: # 4-1. FPS 설정 (frame per second) clock.tick(60) # 메인 이벤트 반복이 1초에 60회 (60 frames per 1 second) space.step(1/60) # 시뮬레이션 주기 # 4-2. 각종 입력 감지 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 4-3. 입력, 시간에 따른 변화 (change with event or time) # 4-4. 그리기 (drawing) screen.fill((0,0,0)) space.debug_draw(draw_options) # 4-5. 업데이트 (update) pygame.display.flip() # 5. 게임 종료 (quit) pygame.quit()
Python
복사

3-1.추가

먼저 공을 만들어 테스트
그 이후 공에 마찰계수와, 충돌계수를 넣었을때 비교해보기기
# 공, 바닥까지 만드는 코드 import pygame, pymunk, pymunk.pygame_util # 1. 게임 초기화 (game initialize) pygame.init() # 2. 게임창 옵션 설정 (window option) size = (1600, 1200) # 게임창 크기 (window size) screen = pygame.display.set_mode(size) title = "Physics Simulator" # 창 제목 (window title) pygame.display.set_caption(title) # 3. 게임 내 필요한 설정 (option for game) clock = pygame.time.Clock() # 시계 (clock) # 공간 만들기 (space) space = pymunk.Space() # 중력의 방향과 세기를 설정 space.gravity = (0, 980) # pygame의 DrawOptions를 사용하여 화면에 그리기 위한 옵션 설정 draw_options = pymunk.pygame_util.DrawOptions(screen) # 바닥 만들기(고정된 type이므로 STATIC으로 설정) floor = pymunk.Body(body_type = pymunk.Body.STATIC) floor.position = (0, size[1]-50) floor_shape = pymunk.Segment(floor, (0,0), (size[0],-100), 1) floor_shape.elasticity = 1 # 탄성 계수 (elasticity) 설정 floor_shape.friction = 0.2 # 마찰 계수 (friction) 설정 # space에 바닥을 추가 space.add(floor, floor_shape) # 공 만들기 (움직이는 type이므로 별도 설정필요없음) ball = pymunk.Body(1, 1) ball.position = (int(size[0]/2)+500, 50) ball_shape = pymunk.Circle(ball, 20) space.add(ball, ball_shape) # 4. 메인 이벤트 (main event) running = True while running: # 4-1. FPS 설정 (frame per second) clock.tick(60) # 메인 이벤트 반복이 1초에 60회 (60 frames per 1 second) space.step(1/60) # 시뮬레이션 주기 # 4-2. 각종 입력 감지 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 4-3. 입력, 시간에 따른 변화 (change with event or time) # 4-4. 그리기 (drawing) screen.fill((0,0,0)) space.debug_draw(draw_options) # 4-5. 업데이트 (update) pygame.display.flip() # 5. 게임 종료 (quit) pygame.quit()
Python
복사
# 충돌 계수 추가 import pygame, pymunk, pymunk.pygame_util # 1. 게임 초기화 (game initialize) pygame.init() # 2. 게임창 옵션 설정 (window option) size = (1600, 1200) # 게임창 크기 (window size) screen = pygame.display.set_mode(size) title = "Physics Simulator" # 창 제목 (window title) pygame.display.set_caption(title) # 3. 게임 내 필요한 설정 (option for game) clock = pygame.time.Clock() # 시계 (clock) # 공간 만들기 (space) space = pymunk.Space() # 중력의 방향과 세기를 설정 space.gravity = (0, 980) # pygame의 DrawOptions를 사용하여 화면에 그리기 위한 옵션 설정 draw_options = pymunk.pygame_util.DrawOptions(screen) # 바닥 만들기(고정된 type이므로 STATIC으로 설정) floor = pymunk.Body(body_type = pymunk.Body.STATIC) floor.position = (0, size[1]-50) floor_shape = pymunk.Segment(floor, (0,0), (size[0],-100), 1) floor_shape.elasticity = 1 # 탄성 계수 (elasticity) 설정 floor_shape.friction = 0.2 # 마찰 계수 (friction) 설정 # space에 바닥을 추가 space.add(floor, floor_shape) # 공 만들기 (움직이는 type이므로 별도 설정필요없음) ball = pymunk.Body(1, 1) ball.position = (int(size[0]/2)+500, 50) ball_shape = pymunk.Circle(ball, 20) ball_shape.elasticity = 0.5 # 탄성 계수 (elasticity) 설정 ball_shape.friction = 0.2 # 마찰 계수 (friction) 설정 space.add(ball, ball_shape) # 4. 메인 이벤트 (main event) running = True while running: # 4-1. FPS 설정 (frame per second) clock.tick(60) # 메인 이벤트 반복이 1초에 60회 (60 frames per 1 second) space.step(1/60) # 시뮬레이션 주기 # 4-2. 각종 입력 감지 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 4-3. 입력, 시간에 따른 변화 (change with event or time) # 4-4. 그리기 (drawing) screen.fill((0,0,0)) space.debug_draw(draw_options) # 4-5. 업데이트 (update) pygame.display.flip() # 5. 게임 종료 (quit) pygame.quit()
Python
복사
1. 실제 현상과 가장 가깝게 시뮬레이션 하기 위해서 추가할 것들은? - 실제 중력가속도에 가깝게 조정 - 탄성계수를 실제와 비슷하게 - 공기저항 반영? 2. 공의 갯수를 늘리고 3. 각각의 공 자유낙하 시간을 측정하여 시각화!! 취한다!!
# 공기저항만 추가 import pygame, pymunk, pymunk.pygame_util # 1. 게임 초기화 (game initialize) pygame.init() # 2. 게임창 옵션 설정 (window option) size = (1600, 1200) # 게임창 크기 (window size) screen = pygame.display.set_mode(size) title = "Physics Simulator" # 창 제목 (window title) pygame.display.set_caption(title) # 3. 게임 내 필요한 설정 (option for game) clock = pygame.time.Clock() # 시계 (clock) # 공간 만들기 (space) space = pymunk.Space() # 중력의 방향과 세기를 설정 space.gravity = (0, 980) # pygame의 DrawOptions를 사용하여 화면에 그리기 위한 옵션 설정 draw_options = pymunk.pygame_util.DrawOptions(screen) # 바닥 만들기(고정된 type이므로 STATIC으로 설정) floor = pymunk.Body(body_type = pymunk.Body.STATIC) floor.position = (0, size[1]-50) floor_shape = pymunk.Segment(floor, (0,0), (size[0],-100), 1) floor_shape.elasticity = 0.7 # 탄성 계수 (elasticity) 설정 floor_shape.friction = 0.4 # 마찰 계수 (friction) 설정 # space에 바닥을 추가 space.add(floor, floor_shape) # 공 만들기 (움직이는 type이므로 별도 설정필요없음) ball = pymunk.Body(1, 1) ball.position = (int(size[0]/2)+500, 50) ball.damping = 0.99 # ✅ 공기저항 추가: 속도가 점점 느려짐 ball_shape = pymunk.Circle(ball, 20) ball_shape.elasticity = 0.5 # 탄성 계수 (elasticity) 설정 ball_shape.friction = 0.2 # 마찰 계수 (friction) 설정 space.add(ball, ball_shape) # 4. 메인 이벤트 (main event) running = True while running: # 4-1. FPS 설정 (frame per second) clock.tick(60) # 메인 이벤트 반복이 1초에 60회 (60 frames per 1 second) space.step(1/60) # 시뮬레이션 주기 # 4-2. 각종 입력 감지 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 4-3. 입력, 시간에 따른 변화 (change with event or time) # 4-4. 그리기 (drawing) screen.fill((0,0,0)) space.debug_draw(draw_options) # 4-5. 업데이트 (update) pygame.display.flip() # 5. 게임 종료 (quit) pygame.quit()
Python
복사

여러개의 공을 한번에 떨어뜨린다면?

import pygame, pymunk, pymunk.pygame_util import random # 1. 게임 초기화 pygame.init() size = (1600, 1200) screen = pygame.display.set_mode(size) pygame.display.set_caption("🌍 공기저항 + 튀는 공 10개") clock = pygame.time.Clock() # 2. 물리 공간 설정 space = pymunk.Space() space.gravity = (0, 980) draw_options = pymunk.pygame_util.DrawOptions(screen) # 3. 바닥 생성 floor = pymunk.Body(body_type=pymunk.Body.STATIC) floor.position = (0, size[1] - 50) floor_shape = pymunk.Segment(floor, (0, 0), (size[0], -100), 1) floor_shape.elasticity = 0.7 # 바닥도 튀도록 설정 floor_shape.friction = 0.4 space.add(floor, floor_shape) # 4. 공 여러 개 생성 balls = [] for i in range(10): mass = 1.0 radius = 20 moment = pymunk.moment_for_circle(mass, 0, radius) body = pymunk.Body(mass, moment) # 무작위 위치 설정 (X: 좌우, Y: 상단에서 일정 범위) x = random.randint(100, size[0] - 100) y = random.randint(50, 300) body.position = (x, y) body.damping = 0.99 # 공기저항 shape = pymunk.Circle(body, radius) shape.elasticity = 0.7 # 반발력 충분히 높게 (0.8 이상이면 잘 튐) shape.friction = 0.2 space.add(body, shape) balls.append((body, shape)) # 5. 메인 루프 running = True while running: clock.tick(60) space.step(1 / 60.0) for event in pygame.event.get(): if event.type == pygame.QUIT: running = False screen.fill((0, 0, 0)) space.debug_draw(draw_options) pygame.display.flip() pygame.quit()
Python
복사

5.공의 개별 낙하시간 측정

[방법]
1.
10개의 공이 각각 바닥에 도달하는 데 걸린 낙하 시간(fall time)을 시각화
2.
따라서 개별공에 대한 추적(tracking)이 필요
공 생성시 초기 시간 기록 필요
공이 바닥에 충돌하는 순간 기록 필요
1.
핵심
pymunk의 collision handler 사용
space.add_collision_handler(ball_type, floor_type)
import pygame import pymunk import pymunk.pygame_util import random import time import csv from collections import defaultdict
Python
복사
# 1. Pygame 초기화 및 화면 설정 pygame.init() size = (1600, 1200) screen = pygame.display.set_mode(size) pygame.display.set_caption("🌍 공기저항 + 튀는 공 10개") clock = pygame.time.Clock() # 2. Pymunk 물리 공간 생성 및 중력 설정 space = pymunk.Space() space.gravity = (0, 980) draw_options = pymunk.pygame_util.DrawOptions(screen) # 3. 바닥 생성 floor = pymunk.Body(body_type=pymunk.Body.STATIC) floor.position = (0, size[1] - 50) floor_shape = pymunk.Segment(floor, (0, 0), (size[0], -100), 1) floor_shape.elasticity = 0.7 # 바닥 반발력 (공과의 충돌 시 반응) floor_shape.friction = 0.4 # 마찰 계수 floor_shape.collision_type = 2 # 바닥의 충돌 타입 ID space.add(floor, floor_shape) # 4. 공 생성 balls = [] spawn_times = {} # 공 생성 시간 기록용 fall_times = defaultdict(float) # 공의 낙하 시간 저장용 ball_type = 1 # 공의 충돌 타입 ID for i in range(10): mass = 1.0 radius = 20 moment = pymunk.moment_for_circle(mass, 0, radius) body = pymunk.Body(mass, moment) # 공 위치 무작위 지정 x = random.randint(100, size[0] - 100) y = random.randint(50, 300) body.position = (x, y) body.damping = 0.99 # 공기 저항 효과 shape = pymunk.Circle(body, radius) shape.elasticity = 0.7 shape.friction = 0.2 shape.collision_type = ball_type space.add(body, shape) balls.append((body, shape)) spawn_times[id(shape)] = time.time() # 생성 시각 저장 # 5. 충돌 감지 핸들러: 공이 바닥에 처음 닿은 순간 기록 def on_collision(arbiter, space, data): shape = arbiter.shapes[0] # 공 shape sid = id(shape) if fall_times[sid] == 0: fall_times[sid] = time.time() - spawn_times[sid] return True # 충돌 이벤트 등록 handler = space.add_collision_handler(ball_type, 2) handler.begin = on_collision # 6. 메인 루프 running = True done = False # 창을 닫을 때까지 기다리는 상태 while running: clock.tick(60) space.step(1 / 60.0) for event in pygame.event.get(): if event.type == pygame.QUIT: running = False done = True screen.fill((0, 0, 0)) space.debug_draw(draw_options) pygame.display.flip() # 모든 공의 낙하 시간이 기록되었는지 확인 if len(fall_times) == len(balls) and not done: print("✅ 모든 공이 바닥에 닿음. 결과 기록 준비 완료.") print("🔔 창을 닫아 결과를 저장하세요.") done = True # 측정 완료 후 창 유지 pygame.quit() # 7. 결과 CSV 저장 with open("fall_times.csv", "w", newline="") as csvfile: writer = csv.writer(csvfile) writer.writerow(["Ball", "FallTime"]) for i, (body, shape) in enumerate(balls): sid = id(shape) writer.writerow([f"Ball {i+1}", round(fall_times[sid], 4)]) print("✅ 낙하 시간 측정 완료. 결과 저장: fall_times.csv")
Python
복사
✅ 모든 공이 바닥에 닿음. 결과 기록 준비 완료. 🔔 창을 닫아 결과를 저장하세요. ✅ 낙하 시간 측정 완료. 결과 저장: fall_times.csv
Plain Text
복사
# 시각화하기 import matplotlib.pyplot as plt import csv ball_ids = [] fall_times = [] # CSV 파일에서 데이터 읽기 with open("fall_times.csv", "r") as csvfile: reader = csv.DictReader(csvfile) for row in reader: ball_ids.append(row["Ball"]) fall_times.append(float(row["FallTime"])) # 시각화 plt.figure(figsize=(10, 5)) plt.bar(ball_ids, fall_times, color='orange', edgecolor='black') plt.xlabel("Ball ID") plt.ylabel("Fall Time (seconds)") plt.title("Measured Fall Time for Each Ball") plt.grid(axis='y', linestyle='--', alpha=0.6) plt.tight_layout() plt.show()
Python
복사
[자유낙하시간 시뮬레이션 시각화 결과]