콘텐츠로 이동

3차시: 코드에 생명 불어넣기 - while 루프로 애니메이션 만들기

⏰ 50분 · while 루프 · rate() 함수 · pos 속성 변경 · 등속 직선 운동 · 난이도 ●●○○○

학습목표: while 루프와 rate() 함수를 사용하여 3D 객체가 부드럽게 움직이는 애니메이션을 구현할 수 있다.

오늘의 질문: "영화는 사실 '사진'의 연속이라는 거 아시나요? 그렇다면 코드로 공을 움직이려면 사진을 몇 장이나 그려야 할까요?"


🧠 애니메이션 루프

while True + rate()가 만드는
무한 프레임의 원리

🧠 rate() 함수

프레임 속도를 제어하는
Vpython의 시간 조절기

💻 공 움직이기 실습

pos.x를 반복문에서 변경하여
등속 직선 운동 구현

🚀 도전: 나만의 애니메이션

속도·방향·여러 객체를
조합한 장면 만들기


⏱️ 수업 흐름

1단계: 도입 — 영화의 비밀 (5분)

영화·애니메이션이 정지 이미지의 빠른 연속이라는 원리를 떠올리며, 코드에서 이를 어떻게 구현하는지 핵심 질문으로 탐구합니다.

2단계: 핵심 개념 — while 루프와 rate() (10분)

while True 무한 루프가 애니메이션 엔진이 되는 원리, rate() 함수가 프레임 속도를 제어하는 역할을 비유와 함께 이해합니다.

3단계: 따라하기 실습 — 공을 움직여보자 (20분)

v1(기본 이동) → v2(속도 개념 도입) → v3(바닥과 함께 장면 꾸미기) → 최종(에러 수정 포함) 순서로 점진적으로 코드를 빌드업합니다.

4단계: 연습 문제 & 도전 과제 (10분)

기초(방향 변경) → 응용(여러 객체 이동) → 도전(2차시 장면에 애니메이션 추가) 3단계 문제를 풀어봅니다.

5단계: 평가 & 성찰 (5분)

퀴즈로 개념을 점검하고, 자기점검 체크리스트를 작성합니다. 다음 차시를 미리 엿봅니다.


1단계: 🎬 도입 — 영화의 비밀

여러분, 영화를 볼 때 화면 속 배우가 자연스럽게 움직이는 것처럼 보이죠? 하지만 실제로는 1초에 24장의 정지된 사진이 빠르게 넘어가는 것입니다. 우리 눈이 그 차이를 인식하지 못할 만큼 빠르기 때문에 "움직인다"고 느끼는 거예요.

게임도 마찬가지입니다. 1초에 60장(60fps)의 화면을 그려서 캐릭터가 부드럽게 달리는 것처럼 보이게 만듭니다.

🤔 그렇다면 질문!

Vpython에서 공 하나를 오른쪽으로 움직이려면, 우리는 무엇을 "반복"해야 할까요?

정답의 핵심: "공의 위치를 아주 조금 바꾼다" → "화면을 다시 그린다" → 이것을 수없이 반복한다!

바로 이 "반복"을 담당하는 것이 여러분이 이미 배운 while 반복문입니다. 오늘 우리는 while 루프에 새로운 역할을 부여할 겁니다 — 애니메이션 엔진이라는 역할이요.

flowchart LR A["위치를 조금 변경"] --> B["화면에 그리기"] --> C["잠깐 대기"] --> A style A fill:#ff9800,color:#fff style B fill:#4caf50,color:#fff style C fill:#2196f3,color:#fff

▲ 애니메이션의 핵심 원리: 위치 변경 → 화면 그리기 → 대기 → 반복

이 세 가지를 코드로 어떻게 표현하는지, 지금부터 하나씩 알아보겠습니다!


2단계: 📚 핵심 개념 — while 루프와 rate()

🧠 핵심 개념 1: while True — 무한 애니메이션 루프

일상 비유부터 시작합시다

시계의 초침을 생각해보세요. 초침은 "1초 전진 → 1초 전진 → 1초 전진 → ..." 을 영원히 반복합니다. 누가 "멈춰!"라고 하지 않는 한요.

Vpython의 애니메이션도 똑같습니다. "위치 변경 → 위치 변경 → 위치 변경 → ..." 을 영원히 반복해야 공이 계속 움직입니다. 이것이 바로 while True:의 역할입니다.

정확한 정의

구분 일반 while 루프 애니메이션 while 루프
조건 while count < 10: (언젠가 끝남) while True: (영원히 반복)
용도 정해진 횟수만큼 작업 매 프레임마다 화면 갱신
종료 조건이 False가 되면 프로그램을 직접 종료할 때
비유 100m 달리기 (결승선 있음) 러닝머신 (멈출 때까지 계속)

여러분이 이전에 배운 while count < 10:은 10번 반복하고 멈추는 루프였죠. 하지만 애니메이션은 화면을 계속 갱신해야 하므로, 의도적으로 끝나지 않는 while True:를 사용합니다.

# 여러분이 이미 아는 while 루프
count = 0
while count < 5:
    print(count)
    count += 1
# → 0, 1, 2, 3, 4 출력 후 끝남

# 오늘 배울 애니메이션 루프 (개념만 미리 보기)
while True:       # <- 영원히 반복! (애니메이션 엔진)
    rate(60)      # <- 1초에 60번만 실행해! (속도 조절기)
    ball.pos.x += 0.01  # <- 공 위치를 조금 이동

위 코드의 1번째 줄 while True:가 바로 무한 애니메이션 루프입니다. True는 절대 False가 되지 않으므로, 이 루프는 프로그램을 종료하기 전까지 계속 돌아갑니다.


🧠 핵심 개념 2: rate() — 프레임 속도 조절기

일상 비유부터 시작합시다

여러분이 플립북(넘기면 그림이 움직이는 수첩) 을 만든다고 상상해보세요.

  • 너무 빨리 넘기면? → 그림이 눈에 안 보이고 휙 지나감
  • 너무 느리게 넘기면? → 뚝뚝 끊겨서 애니메이션처럼 안 보임
  • 적당한 속도로 넘기면? → 부드럽게 움직이는 것처럼 보임! ✨

rate() 함수가 바로 이 "넘기는 속도"를 조절하는 역할입니다.

정확한 정의

rate(n)"이 루프를 1초에 최대 n번 실행하라"는 명령입니다.

rate() 값 의미 체감
rate(10) 1초에 10프레임 뚝뚝 끊기는 느낌
rate(30) 1초에 30프레임 꽤 부드러움
rate(60) 1초에 60프레임 매우 부드러움 (게임 수준)
rate(200) 1초에 200프레임 눈에 차이 거의 없음
rate() 없이 CPU가 허용하는 최대 속도 컴퓨터 과부하 위험! ⚠️

⚠️ 중요! rate()를 빼먹으면 컴퓨터가 미친 듯이 루프를 돌려서 브라우저가 멈추거나 프로그램이 응답 불가 상태가 될 수 있습니다. while True 안에는 반드시 rate()를 넣으세요!

rate()가 하는 일을 그림으로 보면

sequenceDiagram participant 루프 as while 루프 participant rate as "rate(60)" participant 화면 as 3D 화면 루프->>rate: 실행 요청 rate->>rate: "1/60초 지났나?" alt 아직 시간 안 됨 rate->>루프: 잠깐 대기! else 시간 됨 rate->>루프: 진행해도 좋습니다 end 루프->>화면: 위치 업데이트, 화면 갱신 루프->>rate: 다시 실행 요청

▲ rate(60)은 매 반복마다 "1/60초(약 0.017초)가 지났는지" 확인하고, 아직이면 잠깐 기다리게 합니다.

심화: rate()가 없으면 왜 위험할까?

rate() 없이 while True:를 실행하면, 컴퓨터는 CPU 능력의 100%를 써서 초당 수만~수십만 번 루프를 돌립니다. 이러면:

  1. 공이 순식간에 화면 밖으로 사라짐 (너무 빨라서 못 봄)
  2. 브라우저/프로그램이 응답 불가 상태가 됨
  3. 컴퓨터 팬이 미친 듯이 돌아감 🔥

rate()는 "쉬엄쉬엄 해!"라고 말해주는 속도 제한기입니다. 이것만 기억하세요!


3단계: 💻 따라하기 실습 — 공을 움직여보자!

실행 환경: Vpython 3.2 (Web VPython 또는 GlowScript에서 실행 가능)

자, 이제 직접 코드를 쳐볼 시간입니다! 🎉 한 줄씩 추가하면서 공에 생명을 불어넣어 봅시다.

📌 v1: 가장 기본 — 공 하나를 오른쪽으로 밀기

먼저 가장 단순한 코드부터 시작합니다. 딱 5줄이면 됩니다.

from vpython import *

ball = sphere(pos=vector(0, 0, 0), radius=0.5, color=color.red)

while True:              # <- 여기가 [무한 애니메이션 루프]
    rate(30)             # <- 여기가 [프레임 속도 조절기] - 1초에 30번
    ball.pos.x += 0.1    # <- 여기가 [위치 변경] - x좌표를 0.1씩 증가

실행 결과: 빨간 공이 오른쪽(+x 방향)으로 천천히 이동합니다.

🔍 개념-코드 매핑

  • 3번째 줄 ball = sphere(...): 2차시에서 배운 공 생성입니다.
  • 5번째 줄 while True:: 이 루프가 애니메이션 엔진입니다. 프로그램이 종료될 때까지 아래 코드를 반복합니다.
  • 6번째 줄 rate(30): 1초에 30번만 실행하라는 속도 제한기입니다.
  • 7번째 줄 ball.pos.x += 0.1: 매 프레임마다 공의 x좌표를 0.1만큼 증가시킵니다. 이것이 등속 직선 운동입니다!

💡 잠깐! ball.pos.x += 0.1이 뭔가요?

ball.pos는 공의 위치(vector)이고, .x는 그 중 x좌표만 가리킵니다. += 0.1은 "현재 값에 0.1을 더해라"라는 뜻이죠. 즉, 매 프레임마다 공이 오른쪽으로 0.1만큼 이동하는 겁니다.


🔬 rate() 값을 바꿔보는 실험

v1 코드에서 rate(30)의 숫자만 바꿔서 실행해보세요. 직접 해보는 것이 핵심입니다!

실험 코드 변경 예상 결과 왜 그럴까?
실험 A rate(5) 뚝뚝 끊기며 느리게 이동 1초에 5번만 위치를 바꾸니까
실험 B rate(30) 적당히 부드럽게 이동 1초에 30번 위치를 바꿈
실험 C rate(100) 매우 부드럽고 빠르게 이동 1초에 100번 위치를 바꿈
실험 D rate(1) 1초에 한 번 "뚝" 이동 1초에 딱 1번만!

🤔 여기서 질문! rate(5)와 rate(100)은 부드러움도 다르지만, 공의 이동 속도도 다릅니다. 왜 그럴까요?

: rate(5)는 1초에 5번 × 0.1 = 0.5만큼 이동하고, rate(100)은 1초에 100번 × 0.1 = 10만큼 이동합니다. rate 값이 클수록 같은 시간에 더 많이 이동하게 됩니다!

이 문제를 해결하는 방법은 v2에서 알아봅시다. 👇


📌 v2: 속도(velocity) 개념 도입 — rate와 분리하기

v1의 문제점은 rate를 바꾸면 공의 이동 속도까지 바뀐다는 것이었습니다. 실제 애니메이션에서는 프레임 속도와 객체 이동 속도를 분리해야 합니다.

이전 코드에서 뭐가 바뀌었나요? - ✅ dt 변수 추가 (시간 간격) - ✅ velocity 변수 추가 (속도) - ✅ 위치 변경 공식: pos.x += velocity * dt

from vpython import *

ball = sphere(pos=vector(-5, 0, 0), radius=0.5, color=color.red)

# 물리적 속도를 별도 변수로 관리 (rate와 분리!)
velocity = 2       # <- 1초에 2만큼 이동하겠다는 뜻
dt = 0.01          # <- 한 프레임의 시간 간격 (초)

while True:
    rate(100)                      # 1초에 100프레임
    ball.pos.x += velocity * dt    # <- 여기가 [등속 직선 운동 공식]

실행 결과: 빨간 공이 왼쪽(-5)에서 시작하여 오른쪽으로 부드럽게 이동합니다.

🔍 개념-코드 매핑

  • 6번째 줄 velocity = 2: 공의 속도입니다. "1초에 x좌표로 2만큼 이동"을 의미합니다.
  • 7번째 줄 dt = 0.01: 시간 간격(delta time)입니다. 한 프레임이 0.01초를 나타냅니다.
  • 11번째 줄 ball.pos.x += velocity * dt: 이것이 등속 직선 운동 공식 거리 = 속도 × 시간입니다!
  • velocity(2) × dt(0.01) = 0.02씩 이동
  • 1초에 100프레임 × 0.02 = 2만큼 이동 → velocity와 정확히 일치! ✅

왜 이렇게 하나요? 이제 rate()를 30으로 바꾸든 200으로 바꾸든, 공의 실제 이동 속도는 velocity가 결정합니다. 프레임 속도와 물리적 속도가 깔끔하게 분리된 것이죠!

방식 rate 변경 시 속도 제어
v1: pos.x += 0.1 공 속도도 같이 변함 ❌ 어려움
v2: pos.x += velocity * dt 부드러움만 변함 ✅ velocity로 쉽게 제어

🚨 에러 경험 — rate()를 빼먹으면?

자, 이제 일부러 잘못된 코드를 실행해봅시다. 에러를 직접 만나봐야 기억에 남으니까요! 😈

아래 코드에서 이상한 점을 찾아보세요:

from vpython import *

ball = sphere(pos=vector(-5, 0, 0), radius=0.5, color=color.red)

velocity = 2
dt = 0.01

while True:
    # rate()를 빼먹었습니다! 🚨
    ball.pos.x += velocity * dt

이 코드를 실행하면 어떤 일이 벌어질까요?

🔍 결과 확인하기 **증상**: 1. 공이 눈 깜짝할 사이에 화면 밖으로 사라집니다 2. 브라우저가 먹통이 되거나 극도로 느려집니다 3. "페이지가 응답하지 않습니다" 경고가 뜰 수 있습니다 **원인 분석**: - `rate()`가 없으면 while True 루프가 **CPU 최대 속도**로 돌아갑니다 - 1초에 수만~수십만 번 `pos.x += 0.02`가 실행됩니다 - 공이 순식간에 x = 수천~수만 좌표로 날아가버립니다 - 브라우저가 3D 렌더링을 따라가지 못해 멈춥니다 **수정 코드**: `while True:` 바로 다음 줄에 `rate(100)`을 추가합니다.
while True:
    rate(100)      # ← 이 한 줄이 없으면 큰일! 반드시 추가!
    ball.pos.x += velocity * dt

📌 외워두세요! while True: 다음 줄에는 반드시 rate()를 쓴다! 이건 Vpython 애니메이션의 철칙입니다.


🚨 에러 경험 2 — pos를 잘못 수정하면?

하나 더 실험해봅시다. 아래 코드를 실행하면 어떻게 될까요?

from vpython import *

ball = sphere(pos=vector(-5, 0, 0), radius=0.5, color=color.red)

velocity = 2
dt = 0.01

while True:
    rate(100)
    ball.pos.x = velocity * dt    # += 가 아니라 = 를 썼습니다!

이 코드를 실행하면 어떤 에러가 날까요?

🔍 결과 확인하기 **증상**: 에러 메시지는 나지 않지만, **공이 움직이지 않습니다!** 😱 **원인 분석**: - `ball.pos.x = velocity * dt`는 매 프레임마다 x좌표를 `2 × 0.01 = 0.02`로 **고정**합니다 - `+=`는 "기존 값에 더하기"이고, `=`는 "기존 값을 무시하고 새 값으로 덮어쓰기"입니다