5차시: 속도를 코드로 표현하다 - 등속 운동과 경계 조건 구현하기¶
🧠 등속 운동 패턴¶
pos += velocity * dt 한 줄로
물체를 일정 속도로 움직이는 핵심 공식
💻 경계 조건 & 반사¶
if 조건문으로 벽에 닿으면
속도 부호를 뒤집어 튕기기 구현
🔍 도입: 움직임의 비밀 (5분)¶
일상 속 등속 운동 사례를 살펴보고, "속도"를 코드로 표현하는 핵심 아이디어를 잡습니다.
📖 개념 탐구: 속도 벡터와 dt (10분)¶
velocity 벡터, dt(시간 간격), 그리고 pos += velocity * dt 패턴의 물리적 의미를 이해합니다.
🛠️ 실습: 등속 운동 → 벽 반사까지 (25분)¶
v1(정지 공) → v2(등속 운동) → v3(경계 조건 추가) → v4(완성) 순서로 코드를 점진적으로 빌드업합니다.
🧪 실험 & 탐구 (5분)¶
속도 값과 dt 값을 바꿔보며 운동 결과가 어떻게 달라지는지 직접 확인합니다.
📝 정리 & 평가 (5분)¶
핵심 패턴을 정리하고, 형성 평가와 자기점검으로 마무리합니다.
🔍 도입: 움직임의 비밀¶
여러분, 볼링장에서 공을 굴려본 적 있나요? 힘껏 밀어낸 볼링공은 레인 위를 일정한 속도로 쭉 굴러갑니다. 그리고 끝에 있는 핀(벽)에 부딪히면 방향이 바뀌죠.
이번 시간에 만들 것이 바로 이겁니다:
| 현실 세계 | VPython 코드 |
|---|---|
| 볼링공이 일정 속도로 굴러감 | ball.pos += velocity * dt |
| 벽에 부딪히면 튕겨 나옴 | if ball.pos.x > 벽: velocity.x = -velocity.x |
| 시간이 계속 흐름 | while True: 루프 + rate() |
4차시에서 우리는 벡터로 위치를 표현하는 법을 배웠습니다. 오늘은 거기에 속도를 더해서 물체를 실제로 움직여 보겠습니다! 🎯
📚 핵심 개념 1: 등속 운동 패턴 — pos += velocity * dt¶
🎯 일상 비유로 시작하기¶
자동차 내비게이션을 생각해 봅시다. 내비게이션은 이렇게 위치를 업데이트합니다:
"현재 위치 = 이전 위치 + (속도 × 지난 시간)"
예를 들어, 시속 60km로 달리는 자동차가 있다면: - 1시간 후 위치: 0 + 60 × 1 = 60km 지점 - 2시간 후 위치: 60 + 60 × 1 = 120km 지점
컴퓨터 시뮬레이션도 똑같습니다. 다만 "1시간"이 아니라 아주 작은 시간 간격(dt)마다 위치를 갱신하는 것뿐입니다.
📐 정확한 정의¶
| 용어 | 의미 | VPython 코드 | 비유 |
|---|---|---|---|
| velocity (속도 벡터) | 물체가 이동하는 방향과 빠르기 | velocity = vector(2, 0, 0) |
"오른쪽으로 초속 2m" |
| dt (시간 간격) | 한 프레임에서 다음 프레임까지의 시간 | dt = 0.01 |
스톱워치의 1틱 |
| pos += velocity * dt | 매 순간 위치를 속도만큼 갱신 | ball.pos += velocity * dt |
한 발짝씩 걷기 |
왜 dt가 필요할까요? 컴퓨터는 연속적인 움직임을 표현할 수 없습니다. 대신 아주 작은 시간 간격마다 위치를 조금씩 바꿔서 부드러운 움직임처럼 보이게 하는 것입니다. 영화가 초당 24장의 사진을 빠르게 보여주는 것과 같은 원리입니다! 🎬
등속 운동의 시뮬레이션 루프 흐름:
🔬 심화: velocity * dt가 만드는 "한 걸음"의 크기¶
velocity * dt는 한 프레임에서 물체가 이동하는 거리입니다.
velocity = vector(2, 0, 0),dt = 0.01이면 → 한 프레임에0.02만큼 이동velocity = vector(2, 0, 0),dt = 0.001이면 → 한 프레임에0.002만큼 이동
dt가 작을수록 한 걸음이 작아져서 더 정밀한 시뮬레이션이 됩니다. 대신 같은 거리를 이동하려면 더 많은 계산이 필요하겠죠?
📚 핵심 개념 2: 경계 조건 — 벽에 부딪히면 튕기기¶
🎯 일상 비유¶
탁구를 떠올려 보세요. 공이 테이블 끝에 닿으면 반대 방향으로 튕겨 나옵니다. 이때 공의 속도 크기(빠르기)는 그대로이지만, 방향(부호)만 반대가 됩니다.
코드로는 딱 한 줄입니다:
velocity.x = -velocity.x→ x방향 속도의 부호를 뒤집기!
📐 정확한 정의¶
경계 조건(Boundary Condition) 이란, 물체가 특정 영역의 경계에 도달했을 때 어떤 일이 일어나는지를 정의하는 규칙입니다.
| 경계 조건 유형 | 설명 | 코드 패턴 |
|---|---|---|
| 반사 (Reflect) | 벽에 부딪히면 튕김 | velocity.x = -velocity.x |
| 사라짐 (Destroy) | 벽 밖으로 나가면 제거 | ball.visible = False |
| 순환 (Wrap) | 한쪽 끝에서 나가면 반대쪽에서 나타남 | ball.pos.x = -wall_x |
오늘은 가장 직관적인 반사(Reflect) 를 구현합니다. 나머지는 나중에 프로젝트에서 필요할 때 도전해 볼 수 있습니다.
🔬 반사 로직의 구조¶
반사 로직은 항상 이 패턴을 따릅니다:
핵심은 "경계 확인 → 속도 반전" 이 두 단계입니다!
🛠️ 실습: 등속 운동에서 벽 반사까지 점진적 빌드업¶
실행 환경: VPython 3.2 (GlowScript 또는 로컬 설치)
🏗️ v1: 정지한 공 — 시작점 만들기 (3줄)¶
먼저 화면에 공 하나를 띄워 봅시다. 아직 움직이지 않습니다.
from vpython import *
# 씬(무대)을 설정합니다
scene = canvas(width=800, height=400, background=color.white)
# 빨간 공 하나를 원점에 생성합니다
ball = sphere(pos=vector(0, 0, 0), radius=0.5, color=color.red)
예상 결과: 화면 중앙에 빨간 공이 하나 나타납니다. 아무 움직임도 없습니다.
📌 위 코드의 3번째 줄
scene = canvas(...)가 바로 시뮬레이션 무대 설정입니다. 지난 시간에 배운 것과 같은 패턴이죠?
🏗️ v2: 공을 움직여 보자 — 등속 운동 구현¶
이제 핵심입니다! v1에 속도 벡터와 애니메이션 루프를 추가합니다.
v1에서 바뀐 점: 🆕 표시된 줄이 새로 추가된 코드입니다.
from vpython import *
scene = canvas(width=800, height=400, background=color.white)
ball = sphere(pos=vector(-5, 0, 0), radius=0.5, color=color.red) # 🆕 시작 위치를 왼쪽으로
velocity = vector(2, 0, 0) # 🆕 속도 벡터: 오른쪽으로 초속 2 <- 여기가 [속도 벡터 정의]
dt = 0.01 # 🆕 시간 간격: 0.01초 <- 여기가 [시간 간격 정의]
while True: # 🆕 무한 반복 (애니메이션 루프)
rate(100) # 🆕 초당 100번 루프 실행 (부드러운 애니메이션)
ball.pos += velocity * dt # 🆕 매 프레임마다 위치 갱신 <- 여기가 [등속 운동 패턴]
예상 결과: 빨간 공이 왼쪽에서 오른쪽으로 일정한 속도로 이동합니다. 그런데... 화면 밖으로 사라져 버립니다! 😱
개념-코드 매핑:
- 7번째 줄
velocity = vector(2, 0, 0)이 바로 속도 벡터 정의입니다. x방향으로 초속 2의 속력을 의미합니다. - 8번째 줄
dt = 0.01이 바로 시간 간격입니다. 한 프레임에 해당하는 시간입니다. - 11번째 줄
ball.pos += velocity * dt가 바로 등속 운동의 핵심 패턴입니다. 매 프레임마다vector(2,0,0) * 0.01 = vector(0.02, 0, 0)만큼 위치가 바뀝니다. - 10번째 줄
rate(100)은 1초에 루프를 100번 실행하라는 의미입니다. 100번 × dt(0.01) = 1초에 실시간 1초가 흐르도록 맞춘 것입니다.
💡
rate(100)과dt = 0.01의 관계:rate(N)은 1초에 N번 루프를 실행합니다.dt는 한 번의 루프가 "시뮬레이션 세계에서 몇 초에 해당하는지"를 정합니다.rate(100) × dt(0.01) = 1이면 실시간과 시뮬레이션 시간이 1:1로 흘러갑니다.
⚠️ 에러 경험: 공이 사라지는 문제¶
v2를 실행하면 공이 오른쪽으로 계속 나아가다 화면 밖으로 사라집니다. 이건 에러는 아니지만 원치 않는 동작입니다.
❓ 질문: "공이 화면 밖으로 나가지 않게 하려면 어떤 조건을 추가해야 할까요?"
정답은 경계 조건입니다! "공의 x 위치가 일정 값을 넘으면 방향을 바꿔라"라는 규칙을 추가해야 합니다.
🏗️ v3: 오른쪽 벽에서 튕기기 — 경계 조건 추가¶
v2에서 바뀐 점: while 루프 안에 🆕 if 조건문이 추가됩니다.
from vpython import *
scene = canvas(width=800, height=400, background=color.white)
ball = sphere(pos=vector(-5, 0, 0), radius=0.5, color=color.red)
velocity = vector(2, 0, 0)
dt = 0.01
wall_right = 6 # 🆕 오른쪽 벽의 x 좌표를 정의
while True:
rate(100)
ball.pos += velocity * dt
# 🆕 경계 조건: 오른쪽 벽에 닿으면 속도를 반전 <- 여기가 [경계 조건]
if ball.pos.x > wall_right: # 🆕 공이 오른쪽 벽을 넘었는가?
velocity.x = -velocity.x # 🆕 x방향 속도의 부호를 뒤집기 <- 여기가 [반사 로직]
예상 결과: 공이 오른쪽 벽(x = 6)에 닿으면 왼쪽으로 방향을 바꿉니다! 하지만... 왼쪽으로 계속 가다가 또 사라집니다. 😅
개념-코드 매핑:
- 10번째 줄
wall_right = 6이 바로 경계(벽) 위치 정의입니다. 벽의 위치를 변수로 저장하면 나중에 값을 쉽게 바꿀 수 있습니다. - 17번째 줄
if ball.pos.x > wall_right:가 바로 경계 도달 확인입니다. - 18번째 줄
velocity.x = -velocity.x가 바로 반사(Reflect) 입니다. 양수(→)가 음수(←)로, 음수가 양수로 바뀝니다.
⚠️ 에러 경험: 흔한 실수 — velocity를 새로 만들기¶
초보자가 자주 하는 실수를 한번 살펴볼까요?
from vpython import *
scene = canvas(width=800, height=400, background=color.white)
ball = sphere(pos=vector(-5, 0, 0), radius=0.5, color=color.red)
velocity = vector(2, 0, 0)
dt = 0.01
wall_right = 6
while True:
rate(100)
ball.pos += velocity * dt
if ball.pos.x > wall_right:
velocity = vector(-2, 0, 0) # ← 이렇게 쓰면 작동은 하지만...
❓ 질문: "이 코드는 에러가 나지는 않지만, 왜 좋지 않은 방법일까요?"
문제 분석:
이 코드는 실행됩니다. 하지만 velocity = vector(-2, 0, 0)처럼 속도 값을 직접 하드코딩하면 문제가 생깁니다:
- 속도가
vector(3, 1, 0)같은 대각선 방향이면?-velocity.x만 바꿔야 하는데 전체를 새로 써야 합니다. - 속도가 프로그램 중간에 바뀌면? 항상 원래 값을 기억하고 있어야 합니다.
- 코드 수정이 번거롭고 실수하기 쉽습니다.
올바른 방법:
이 한 줄은 속도가 2이든 100이든 -3이든 항상 정확하게 반대 방향으로 바꿔줍니다. 이것이 더 범용적(general)인 코드입니다.
🏗️ v4 (최종): 양쪽 벽에서 튕기는 완성 코드¶
v3에서 바뀐 점: 🆕 왼쪽 벽 조건과 시각적 벽 객체가 추가됩니다.
from vpython import *
# --- 무대 설정 ---
scene = canvas(width=800, height=400, background=color.white)
scene.caption = "🏓 등속 운동 + 벽 반사 시뮬레이션"
# --- 벽 만들기 (시각적 효과) ---
wall_x = 6 # 벽의 x 좌표 (좌우 대칭)
wall_left = box(pos=vector(-wall_x, 0, 0), size=vector(0.2, 4, 4),
color=color.blue, opacity=0.3) # 🆕 왼쪽 벽
wall_right_obj = box(pos=vector(wall_x, 0, 0), size=vector(0.2, 4, 4),
color=color.blue, opacity=0.3) # 🆕 오른쪽 벽
# --- 공 생성 ---
ball = sphere(pos=vector(0, 0, 0), radius=0.5, color=color.red,
make_trail=True, trail_color=color.orange) # 🆕 궤적 표시
# --- 물리량 설정 ---
velocity = vector(2, 0, 0) # 속도 벡터: 오른쪽으로 초속 2 <- 여기가 [속도 벡터]
dt = 0.01 # 시간 간격 <- 여기가 [dt]
# --- 애니메이션 루프 ---
while True:
rate(100) # 초당 100프레임
ball.pos += velocity * dt # <- 여기가 [등속 운동 핵심 패턴]
# 오른쪽 벽 반사
if ball.pos.x > wall_x: # <- 여기가 [오른쪽 경계 조건]
velocity.x = -velocity.x # <- 여기가 [반사 로직]
# 왼쪽 벽 반사 🆕 왼쪽 벽도 추가!
if ball.pos.x < -wall_x: # <- 여기가 [왼쪽 경계 조건]
velocity.x = -velocity.x # 같은 반사 로직 적용
예상 결과: 빨간 공이 양쪽 파란 벽 사이를 왔다 갔다 하며, 주황색 궤적이 남습니다! 🎉
전체 코드 구조 요약:
🧪 실험 & 탐구: 값을 바꿔보면 어떤 일이?¶
실험 1: 속도 바꾸기¶
v4 완성 코드에서 velocity 값만 바꿔보세요.