콘텐츠로 이동

6차시: 중력을 눈으로 보다 - 가속도와 포물선 운동 시뮬레이션

⏰ 50분 · 가속도 · 중력 벡터 · 포물선 운동 · 궤적 시각화 · 난이도 ●●●○○

학습목표: 가속도 벡터(gravity)를 정의하고, velocity += gravity * dt 패턴으로 포물선 운동을 시뮬레이션할 수 있다.

오늘의 질문: "농구공을 던지면 왜 직선이 아니라 곡선으로 날아갈까요? 그 곡선을 코드 한 줄로 만들 수 있다면?"


🧠 가속도와 중력 벡터

속도를 매 순간 바꾸는 힘, gravity = vector(0, -9.8, 0) 한 줄의 마법

💻 포물선 운동 시뮬레이션

등속 운동 코드에 딱 한 줄을 추가해서 공이 곡선을 그리게 만들기

🔬 발사 각도 실험

초기 속도의 방향과 크기를 바꾸며 궤적 변화를 탐구하기

🎯 바닥 충돌 & 궤적

make_trail로 아름다운 포물선을 남기고, 바닥에 닿으면 멈추기


🔥 도입: 등속 vs 가속, 한 줄의 차이 (5분)

5차시 등속 운동 코드를 떠올리며, "속도가 변한다"는 것이 무엇인지 체감합니다. 농구공을 위로 던졌을 때의 경험을 떠올려봅니다.

📚 개념: 가속도 벡터와 velocity 갱신 패턴 (10분)

가속도의 정의를 일상 비유로 이해하고, velocity += gravity * dt 패턴이 왜 작동하는지 코드-개념 매핑으로 학습합니다.

💻 실습: 포물선 운동 시뮬레이션 만들기 (20분)

v1(자유낙하) → v2(포물선 운동) → v3(궤적 + 바닥 충돌)으로 점진적으로 코드를 완성합니다. 에러 경험도 포함됩니다.

🔬 탐구: 발사 각도와 속력 실험 (10분)

초기 속도의 각도와 크기를 바꿔가며 궤적이 어떻게 달라지는지 실험하고 결과를 정리합니다.

📝 정리 & 평가 (5분)

핵심 개념을 복습하고, 형성 평가와 자기점검으로 학습을 마무리합니다.


🔥 도입: 등속 운동에서 빠진 한 가지

여러분, 5차시에서 공이 일직선으로 쭉 날아가는 등속 운동을 만들었습니다. 기억나시나요?

ball.pos = ball.pos + velocity * dt

이 코드에서 velocity절대 변하지 않았습니다. 공은 마치 우주 공간에서 날아가듯, 한 방향으로 영원히 직진했습니다.

그런데 현실에서 농구공을 던지면 어떻게 되나요?

  • 처음에는 위로 올라가다가...
  • 점점 느려지면서...
  • 꼭대기에서 잠깐 멈췄다가...
  • 아래로 점점 빨라지면서 떨어집니다

이 모든 현상의 원인은 딱 하나, 중력이 속도를 매 순간 바꾸고 있기 때문입니다.

놀라운 사실을 알려드릴게요. 이 복잡해 보이는 현상을 코드 딱 한 줄을 추가하는 것만으로 재현할 수 있습니다!


📚 핵심 개념 1: 가속도(Acceleration)란?

🎢 일상 비유: 자동차의 가속 페달

자동차를 생각해보세요.

상황 속도 변화 가속도
가속 페달을 밟는다 속도가 점점 빨라진다 양(+)의 가속도
브레이크를 밟는다 속도가 점점 느려진다 음(-)의 가속도
크루즈 컨트롤 (일정 속도) 속도가 변하지 않는다 가속도 = 0

가속도는 "속도가 얼마나 빠르게 변하는지"를 나타내는 값입니다.

  • 속도(velocity): 위치가 시간에 따라 변하는 비율 → 위치 변화량 / 시간
  • 가속도(acceleration): 속도가 시간에 따라 변하는 비율 → 속도 변화량 / 시간

🍎 중력 가속도: 지구가 당기는 힘

지구 위의 모든 물체는 아래 방향으로 초당 9.8 m/s씩 속도가 증가합니다. 이것이 바로 중력 가속도 g = 9.8 m/s²입니다.

시간 (초) 속도 (m/s) 설명
0 0 가만히 놓은 순간
1 9.8 1초 후
2 19.6 2초 후
3 29.4 3초 후

매 초마다 속도가 9.8씩 증가합니다! 이것이 "가속"입니다.

🖥️ 코드로 표현하면?

5차시의 등속 운동 패턴을 기억해보세요:

매 프레임마다:
    위치 += 속도 × dt

여기에 가속도를 추가하면:

매 프레임마다:
    속도 += 가속도 × dt    ← ✨ 새로 추가된 한 줄!
    위치 += 속도 × dt

순서가 중요합니다! 먼저 속도를 갱신하고, 갱신된 속도로 위치를 갱신합니다.

아래 다이어그램은 등속 운동과 가속 운동의 루프 차이를 보여줍니다:

flowchart TD A[시뮬레이션 시작] --> B{매 프레임 반복} B --> C["① 속도 갱신<br/>velocity += gravity × dt"] C --> D["② 위치 갱신<br/>ball.pos += velocity × dt"] D --> E[화면에 공 그리기] E --> B style C fill:#ff9800,color:#fff style D fill:#4caf50,color:#fff

위 다이어그램에서 주황색 ① 속도 갱신 단계가 5차시에는 없었던 새로운 한 줄입니다. 이 한 줄이 등속 운동을 가속 운동으로 바꿔줍니다!


📚 핵심 개념 2: VPython에서 중력 벡터 정의하기

중력은 아래 방향으로 작용합니다. VPython에서 y축이 위아래이므로:

gravity = vector(0, -9.8, 0)
성분 의미
x 0 좌우로는 힘이 없음
y -9.8 아래 방향으로 9.8 m/s²
z 0 앞뒤로는 힘이 없음

마이너스(-) 인 이유는 VPython에서 y축 위쪽이 +, 아래쪽이 -이기 때문입니다. 중력은 아래로 당기니까 -9.8이 됩니다.


💻 실습: 포물선 운동 시뮬레이션 만들기

실행 환경: VPython 3.2 (GlowScript 또는 로컬 vpython 패키지)

🏗️ v1: 자유낙하 — 공을 높은 곳에서 놓아보기

가장 간단한 가속 운동부터 시작합시다. 공을 높은 곳에 놓으면 중력에 의해 아래로 떨어지는 자유낙하입니다.

from vpython import *

# 장면 설정
scene = canvas(title="v1: 자유낙하", width=600, height=400)

# 바닥
ground = box(pos=vector(0, -0.05, 0), size=vector(20, 0.1, 5), color=color.green)

# 공: 높은 곳(y=10)에서 시작
ball = sphere(pos=vector(0, 10, 0), radius=0.5, color=color.red)

# 초기 속도: 정지 상태에서 출발
velocity = vector(0, 0, 0)

# 중력 가속도 벡터  # <- 여기가 [가속도 벡터 정의]
gravity = vector(0, -9.8, 0)

dt = 0.01  # 시간 간격

while ball.pos.y >= ball.radius:  # 바닥에 닿을 때까지
    rate(100)  # 초당 100프레임

    velocity = velocity + gravity * dt    # <- 여기가 [속도 갱신]
    ball.pos = ball.pos + velocity * dt   # <- 여기가 [위치 갱신]

실행 결과: 빨간 공이 높이 10m에서 시작해 점점 빨라지면서 아래로 떨어집니다. 바닥(y=0 근처)에 닿으면 멈춥니다.

🔍 코드-개념 매핑

  • 7번째 줄 gravity = vector(0, -9.8, 0) — 이것이 바로 중력 가속도 벡터입니다. y 방향으로 -9.8을 넣어 "아래로 당기는 힘"을 표현합니다.
  • 12번째 줄 velocity = velocity + gravity * dt — 이것이 바로 속도 갱신 패턴입니다. 매 프레임마다 중력이 속도를 조금씩 바꿉니다. gravity * dt는 아주 짧은 시간(0.01초) 동안 속도가 변하는 양입니다.
  • 13번째 줄 ball.pos = ball.pos + velocity * dt — 5차시에서 배운 위치 갱신 패턴과 동일합니다. 다만 이제 velocity가 매번 바뀌므로 공이 가속됩니다!
  • 10번째 줄 while ball.pos.y >= ball.radius — 공의 중심이 반지름만큼 내려오면 바닥에 닿은 것으로 판단합니다. 이것이 바닥 충돌 조건입니다.

💡 핵심 포인트: 5차시 등속 운동 코드와 비교하면, 정말로 한 줄(velocity = velocity + gravity * dt)만 추가되었습니다. 이 한 줄이 직선 운동을 가속 운동으로 바꿉니다!


🏗️ v2: 포물선 운동 — 비스듬히 던지기

자유낙하는 직선이었습니다. 이제 공에 비스듬한 초기 속도를 주어서 포물선을 만들어봅시다!

v1에서 바뀐 부분: 초기 속도를 vector(0, 0, 0)vector(5, 15, 0)으로 변경했습니다. 오른쪽으로 5 m/s, 위쪽으로 15 m/s의 속도로 던지는 것입니다.

from vpython import *

# 장면 설정
scene = canvas(title="v2: 포물선 운동", width=700, height=400)
scene.camera.pos = vector(8, 5, 15)  # 카메라 위치 조정

# 바닥
ground = box(pos=vector(8, -0.05, 0), size=vector(25, 0.1, 5), color=color.green)

# 공: 왼쪽 아래에서 시작
ball = sphere(pos=vector(0, 0.5, 0), radius=0.3, color=color.red)

# ✨ 비스듬한 초기 속도: 오른쪽(+x) + 위쪽(+y)  # <- 여기가 [초기 속도]
velocity = vector(5, 15, 0)

# 중력 가속도
gravity = vector(0, -9.8, 0)

dt = 0.01

while ball.pos.y >= ball.radius:
    rate(100)

    velocity = velocity + gravity * dt    # 속도 갱신: 중력이 매 프레임 속도를 바꿈
    ball.pos = ball.pos + velocity * dt   # 위치 갱신: 바뀐 속도로 이동

실행 결과: 공이 왼쪽 아래에서 출발하여 오른쪽 위로 올라갔다가, 꼭대기에서 잠시 느려지고, 다시 아래로 떨어지며 아름다운 포물선을 그립니다! 🎉

왜 포물선이 될까요?

방향 속도 변화 이유
x(가로) 변하지 않음 (계속 5 m/s) 중력의 x 성분이 0이므로
y(세로) 계속 줄어듦 (15 → 0 → -15...) 중력이 y 방향으로 -9.8씩 감소시킴

가로는 등속, 세로는 가속 → 이 두 운동이 합쳐져서 포물선이 됩니다!

flowchart LR A["가로 운동<br/>등속 (v_x 일정)"] --> C["합성 결과<br/>🏀 포물선 궤적"] B["세로 운동<br/>가속 (v_y 변화)"] --> C

🐛 에러 경험: 순서를 바꾸면 어떻게 될까?

여기서 잠깐! 다음 코드를 한번 살펴보세요. 뭔가 이상한 점이 있습니다.

from vpython import *

scene = canvas(title="에러 체험: 순서 실수", width=700, height=400)
ground = box(pos=vector(8, -0.05, 0), size=vector(25, 0.1, 5), color=color.green)
ball = sphere(pos=vector(0, 0.5, 0), radius=0.3, color=color.red)

velocity = vector(5, 15, 0)
gravity = vector(0, -9.8, 0)
dt = 0.01

while ball.pos.y >= ball.radius:
    rate(100)

    # ⚠️ 위치를 먼저 갱신하고, 속도를 나중에 갱신했습니다!
    ball.pos = ball.pos + velocity * dt   # 위치 먼저!
    velocity = velocity + gravity * dt    # 속도 나중에!

🤔 질문: 이 코드를 실행하면 에러가 날까요?

: 에러는 나지 않습니다! 하지만 결과가 미묘하게 달라집니다.

정상 순서 (v2) 잘못된 순서 (위 코드)
속도 먼저 갱신 → 위치 갱신 위치 먼저 갱신 → 속도 갱신
이번 프레임의 새 속도로 이동 이전 프레임의 옛 속도로 이동
더 정확한 시뮬레이션 약간의 오차가 누적됨

dt가 0.01처럼 작으면 눈으로는 차이를 거의 못 느끼지만, dt를 0.1이나 0.5로 크게 하면 궤적 차이가 눈에 보입니다. 물리 시뮬레이션에서는 순서가 중요합니다!

기억하세요: 항상 속도 먼저, 위치 나중에!


🏗️ v3: 궤적 남기기 + 바닥 충돌 완성

포물선이 만들어지지만, 공이 지나간 자리가 보이지 않으니 아쉽습니다. 궤적(trail) 을 남겨서 포물선을 눈으로 확인해봅시다!

v2에서 바뀐 부분: 1. make_trail=True 옵션 추가 → 공이 지나간 경로를 선으로 표시 2. trail_color, trail_radius 추가 → 궤적을 예쁘게 꾸미기 3. while 조건 개선 → 바닥 충돌 후 공을 정확히 멈추기 4. 시작/착지 표시 추가

from vpython import *

# 장면 설정
scene = canvas(title="v3: 포물선 궤적 시각화", width=700, height=450)
scene.camera.pos = vector(8, 6, 18)

# 바닥
ground = box(pos=vector(8, -0.05, 0), size=vector(25, 0.1, 5), color=color.green)

# 공: make_trail로 궤적을 남김  # <- 여기가 [궤적 시각화]
ball = sphere(
    pos=vector(0, 0.5, 0),
    radius=0.3,
    color=color.red,
    make_trail=True,         # 궤적 남기기 ON
    trail_color=color.yellow, # 궤적 색상
    trail_radius=0.05        # 궤적 선 두께
)

# 시작점 표시
sphere(pos=vector(0, 0.5, 0), radius=0.15, color=color.cyan)

# 초기 속도와 중력
velocity = vector(5, 15, 0)
gravity = vector(0, -9.8, 0)
dt = 0.01

# 시뮬레이션 루프
while ball.pos.y >= ball.radius:  # <- 여기가 [바닥 충돌 조건]
    rate(100)

    velocity = velocity + gravity * dt    # ① 속도 갱신
    ball.pos = ball.pos + velocity * dt   # ② 위치 갱신

# 바닥에 닿으면 정확한 위치에 멈추기
ball.pos.y = ball.radius  # 공이 바닥에 살짝 얹힌 상태

# 착지점 표시
sphere(pos=ball.pos, radius=0.15, color=color.orange)

# 비행 거리 출력
print("착지 x 좌표:", round(ball.pos.x, 2), "m")
착지 x 좌표: 15.31 m

실행 결과: 빨간 공이 날아가면서 노란색 포물선 궤적이 화면에 남습니다. 시작점(하늘색)과 착지점(주황색)이 표시되어 비행 거리를 한눈에 볼 수 있습니다!

🔍 v3 코드-개념 매핑

  • 11~17번째 줄 make_trail=Truesphere를 만들 때 이 옵션을 켜면, 공이 이동한 경로를 자동으로 선으로 그려줍니다. 이것이 궤적 시각화 기능입니다.
  • 29번째 줄 while ball.pos.y >= ball.radius — 공의 중심 y좌표가 반지름 이하가 되면 반복을 멈춥니다. 이것이 바닥 충돌 판정입니다.
  • 35번째 줄 ball.pos.y = ball.radius — 루프가 끝난 후 공의 y좌표를 정확히 반지름 높이로 맞춰서, 공이 바닥을 뚫고 내려가지 않게 합니다.

🎉 여기까지가 포물선 시뮬레이션의 핵심입니다! 5차시 등속 운동 코드에서 실질적으로 추가된 것은 gravity 정의와 velocity += gravity * dt 한 줄뿐입니다.


🏗️ 최종 완성: 발사 각도 입력 + 다중 궤적 비교

마지막으로, 발사 각도와 속력을 입력받아 여러 궤적을 한 화면에 비교하는 완성 버전입니다.

v3에서 바뀐 부분: 1. math.cos, math.sin으로 각도 → 속도 벡터 변환 2. 함수(launch_ball)로 분리하여 재사용 가능 3. 여러 각도를 반복문