9차시: 부딪히면 반응하라! - 충돌 감지와 에러 디버깅 실전¶
🎯 핵심 주제 카드¶
🧠 충돌 감지 원리¶
두 객체 사이 거리를 mag()로 계산하여
반지름의 합과 비교하는 조건식
💻 충돌 반응 구현¶
색상 변경, 속도 반전, 객체 소멸 등
충돌 시 다양한 시각적 반응 코딩
🐛 에러 디버깅 실전¶
AttributeError, 무한루프, rate 누락 등
대표 에러 5가지 원인 파악과 해결
💬 트러블슈팅 훈련¶
에러 메시지를 읽고 원인을 추론하는
디버깅 습관 기르기
⏱️ 수업 흐름¶
1단계: 도입 — 충돌이란 무엇인가? (5분)¶
일상 속 '부딪힘'의 원리를 떠올리고, 컴퓨터가 충돌을 판단하는 방식을 비유로 이해합니다.
2단계: 핵심 개념 — mag()와 거리 계산 (10분)¶
두 객체 사이 거리를 mag() 함수로 구하고, 반지름의 합과 비교하는 충돌 조건식을 학습합니다.
3단계: 따라하기 실습 — 충돌 감지 & 반응 구현 (20분)¶
v1(기본 충돌 감지) → v2(색상 변경 반응) → v3(다중 객체 충돌) → 최종(복합 반응)까지 점진적으로 코드를 완성합니다.
4단계: 에러 디버깅 실전 훈련 (10분)¶
VPython에서 자주 발생하는 대표 에러 5가지를 직접 만나보고, 원인을 분석하여 수정하는 훈련을 합니다.
5단계: 마무리 — 정리 & 성찰 (5분)¶
배운 내용을 정리하고, 형성 평가와 자기점검으로 학습을 마무리합니다.
📚 핵심 개념 설명¶
🔑 개념 1: 충돌 감지 — "두 물체 사이의 거리"로 판단한다¶
🎳 일상 비유: 볼링공과 핀¶
볼링을 칠 때, 공이 핀에 닿는 순간 핀이 쓰러집니다. 사람은 눈으로 "아, 부딪혔네!" 하고 알 수 있지만, 컴퓨터에게는 눈이 없습니다. 그래서 컴퓨터는 이렇게 판단합니다:
"두 물체의 중심 사이 거리가, 두 물체의 크기(반지름)의 합보다 작아지면 → 부딪힌 것이다!"
이것은 마치 두 사람이 양팔을 벌리고 걸어가다가, 팔이 닿으면 "부딪혔다!"고 판정하는 것과 같습니다.
📐 정확한 정의¶
충돌 감지(Collision Detection) 란 프로그램에서 두 객체가 겹치거나 접촉했는지를 계산으로 판별하는 것입니다. 구(sphere) 형태의 충돌 감지 공식은 다음과 같습니다:
| 항목 | 의미 | VPython 코드 |
|---|---|---|
| 두 객체 사이 거리 | 중심점과 중심점 사이의 직선 거리 | mag(ball1.pos - ball2.pos) |
| 반지름의 합 | 두 구의 반지름을 더한 값 | ball1.radius + ball2.radius |
| 충돌 조건 | 거리 < 반지름의 합 | if distance < r_sum: |
충돌 판정 다이어그램:
🧮 mag() 함수란?¶
VPython의 mag() 함수는 벡터의 크기(magnitude)를 구합니다. 쉽게 말하면, 3차원 공간에서 두 점 사이의 거리를 한 번에 계산해주는 함수입니다.
수학적으로는 피타고라스 정리의 3D 확장입니다:
| 수학 공식 | VPython 코드 | 의미 |
|---|---|---|
| √((x₂-x₁)² + (y₂-y₁)² + (z₂-z₁)²) | mag(ball1.pos - ball2.pos) |
두 위치 벡터의 차이의 크기 |
ball1.pos - ball2.pos는 두 위치의 차이 벡터를 만들고, mag()가 그 벡터의 길이(= 거리)를 숫자로 반환합니다. 복잡한 수학을 한 줄로 해결해주는 고마운 함수입니다!
💡 왜 직접 거리를 계산하지 않나요?
((x2-x1)**2 + (y2-y1)**2 + (z2-z1)**2)**0.5처럼 직접 쓸 수도 있지만, 코드가 길고 실수하기 쉽습니다.mag()는 VPython이 제공하는 편리한 도구이고, 실무에서도 이런 유틸리티 함수를 적극 활용합니다.
🔑 개념 2: 에러 디버깅 — "에러 메시지는 컴퓨터가 보내는 도움 요청"¶
📬 일상 비유: 택배 배송 실패 문자¶
택배가 배달 실패했을 때, 문자가 옵니다: "주소 불명으로 반송되었습니다." 이 문자를 무시하면 택배를 영영 못 받지만, 읽고 주소를 수정하면 다시 받을 수 있습니다.
에러 메시지도 마찬가지입니다. 컴퓨터가 "이런 문제가 있어서 실행을 못 하겠어요"라고 알려주는 것인데, 대부분의 초보자는 빨간 글씨만 보고 당황합니다. 하지만 에러 메시지를 차분히 읽으면, 어디서 무엇이 잘못됐는지 거의 다 알려줍니다.
🔍 에러 메시지 읽는 법¶
에러 메시지는 보통 세 부분으로 이루어져 있습니다:
| 부분 | 의미 | 예시 |
|---|---|---|
| 위치 (Traceback) | 에러가 발생한 파일과 줄 번호 | line 5, in <module> |
| 종류 (Error Type) | 어떤 유형의 에러인지 | AttributeError |
| 설명 (Message) | 구체적으로 무엇이 문제인지 | 'sphere' object has no attribute 'colour' |
💻 따라하기 실습: 충돌 감지 & 반응 구현¶
실행 환경: VPython 3.2 (GlowScript 또는 로컬 VPython 환경)
🏗️ v1: 가장 기본 — 두 공의 거리 확인하기¶
먼저 두 개의 공을 만들고, 하나를 움직여서 거리를 측정하는 것부터 시작합니다. 아직 충돌 판정은 하지 않습니다. "일단 거리를 재보자!"가 목표입니다.
from vpython import *
# 두 개의 공 생성 — 서로 떨어져 있는 상태
ball1 = sphere(pos=vector(-3, 0, 0), radius=0.5, color=color.red)
ball2 = sphere(pos=vector(3, 0, 0), radius=0.5, color=color.blue)
# ball1이 오른쪽으로 이동하는 속도
ball1.velocity = vector(0.05, 0, 0)
while True:
rate(60) # <- 초당 60프레임으로 애니메이션 제어
ball1.pos = ball1.pos + ball1.velocity
# 두 공 사이의 거리를 계산하여 출력
distance = mag(ball1.pos - ball2.pos) # <- 여기가 mag() 거리 계산
print(f"거리: {distance:.2f}")
예상 실행 결과 (콘솔에 거리가 계속 출력됩니다):
개념-코드 매핑:
- 위 코드의 14번째 줄
distance = mag(ball1.pos - ball2.pos)가 바로 두 객체 사이 거리 계산입니다.ball1.pos - ball2.pos로 차이 벡터를 구하고,mag()로 그 크기를 숫자로 변환합니다. - 10번째 줄
rate(60)은 애니메이션 속도를 제어합니다. 이 줄이 없으면 컴퓨터가 최대 속도로 돌려서 화면이 멈추는 것처럼 보입니다.
거리 숫자가 점점 줄어드는 것이 보이시나요? 공이 다가가고 있다는 증거입니다! 하지만 아직 공이 겹쳐도 아무 일도 일어나지 않습니다.
🏗️ v2: 충돌 감지 추가 — "부딪히면 색이 바뀐다!"¶
v1에서 달라진 점: if 조건문으로 충돌을 감지하고, 충돌 시 두 공의 색상을 변경합니다.
from vpython import *
ball1 = sphere(pos=vector(-3, 0, 0), radius=0.5, color=color.red)
ball2 = sphere(pos=vector(3, 0, 0), radius=0.5, color=color.blue)
ball1.velocity = vector(0.05, 0, 0)
while True:
rate(60)
ball1.pos = ball1.pos + ball1.velocity
distance = mag(ball1.pos - ball2.pos)
# ▼▼▼ [v2에서 추가된 부분] 충돌 판정 ▼▼▼
r_sum = ball1.radius + ball2.radius # <- 반지름의 합 계산
if distance < r_sum: # <- 여기가 충돌 조건식
ball1.color = color.yellow # 충돌 시 색상 변경
ball2.color = color.yellow
print("💥 충돌 발생!")
# ▲▲▲ [v2에서 추가된 부분] 끝 ▲▲▲
예상 실행 결과:
3D 화면에서 빨간 공이 오른쪽으로 이동하다가, 파란 공에 닿는 순간 두 공 모두 노란색으로 변합니다! 콘솔에는:
개념-코드 매핑:
- 15번째 줄
r_sum = ball1.radius + ball2.radius— 두 공의 반지름을 더합니다. 반지름이 각각 0.5이므로r_sum = 1.0입니다. - 16번째 줄
if distance < r_sum:— 핵심 충돌 조건입니다! 두 공의 중심 거리가 1.0보다 작아지면, 두 공의 표면이 겹치는 것이므로 "충돌"로 판정합니다.
🤔 잠깐, "충돌 발생!"이 계속 출력되는 이유가 뭘까요? 공이 충돌해도 멈추지 않고 계속 이동하기 때문입니다. 겹쳐 있는 동안 매 프레임마다 충돌 조건이 참이 됩니다. 다음 버전에서 이 문제를 해결해봅시다!
🏗️ v3: 충돌 반응 개선 — 방향 반전(튕겨나가기)¶
v2에서 달라진 점: 충돌 시 공이 반대 방향으로 튕겨나가게 속도를 반전시킵니다. 두 번째 공도 움직이도록 합니다.
from vpython import *
ball1 = sphere(pos=vector(-3, 0, 0), radius=0.5, color=color.red)
ball2 = sphere(pos=vector(3, 0, 0), radius=0.5, color=color.blue)
# ▼▼▼ [v3에서 변경] 두 공 모두 서로를 향해 이동 ▼▼▼
ball1.velocity = vector(0.05, 0, 0) # 오른쪽으로
ball2.velocity = vector(-0.03, 0, 0) # 왼쪽으로
# ▲▲▲ [v3에서 변경] 끝 ▲▲▲
while True:
rate(60)
# 두 공 모두 이동
ball1.pos = ball1.pos + ball1.velocity
ball2.pos = ball2.pos + ball2.velocity # <- v3 추가: ball2도 이동
distance = mag(ball1.pos - ball2.pos)
r_sum = ball1.radius + ball2.radius
if distance < r_sum:
# ▼▼▼ [v3에서 변경] 속도 반전으로 튕겨나가기 ▼▼▼
ball1.velocity = -ball1.velocity # <- 여기가 방향 반전
ball2.velocity = -ball2.velocity # <- 여기가 방향 반전
ball1.color = color.yellow
ball2.color = color.yellow
# ▲▲▲ [v3에서 변경] 끝 ▲▲▲
예상 실행 결과:
두 공이 서로를 향해 다가가다가, 만나는 순간 노란색으로 바뀌면서 반대 방향으로 튕겨나갑니다! 마치 당구공이 부딪히는 것 같은 느낌입니다.
개념-코드 매핑:
- 23~24번째 줄
ball1.velocity = -ball1.velocity— 속도 벡터에 마이너스(-)를 붙이면 방향이 정반대로 바뀝니다.vector(0.05, 0, 0)이vector(-0.05, 0, 0)이 되는 것입니다. 이것이 방향 반전입니다.
⚠️ 에러 경험: 이 코드를 실행하면 어떤 에러가 날까요?¶
v3 코드를 조금 수정해봅시다. 아래 코드에는 일부러 넣은 실수가 있습니다. 찾아보세요!
from vpython import *
ball1 = sphere(pos=vector(-3, 0, 0), radius=0.5, color=color.red)
ball2 = sphere(pos=vector(3, 0, 0), radius=0.5, color=color.blue)
ball1.velocity = vector(0.05, 0, 0)
while True:
rate(60)
ball1.pos = ball1.pos + ball1.velocity
# 🐛 버그가 숨어있는 줄!
distance = mag(ball1.position - ball2.pos)
r_sum = ball1.radius + ball2.radius
if distance < r_sum:
ball1.color = color.yellow
에러 메시지:
원인 분석:
VPython의 sphere 객체에서 위치를 나타내는 속성은 position이 아니라 pos입니다. ball1.position이라고 적었기 때문에, "sphere 객체에는 position이라는 속성이 없다"는 AttributeError가 발생합니다.
| 잘못된 코드 | 올바른 코드 | 설명 |
|---|---|---|
ball1.position |
ball1.pos |
VPython은 pos가 위치 속성 |
ball1.colour |
ball1.color |
미국식 철자 color 사용 |
ball1.rad |
ball1.radius |
반지름은 radius |
수정 코드:
💡 AttributeError를 만나면? "이 객체에 그런 이름의 속성은 없다"는 뜻입니다. 속성 이름의 오타를 의심하세요! VPython에서는
pos,color,radius,velocity등의 정확한 이름을 사용해야 합니다.
🏗️ 최종 완성: 다중 공 충돌 + 복합 반응¶
이제 리스트를 활용하여 여러 개의 공을 만들고, 모든 쌍의 충돌을 검사하는 최종 코드를 완성합니다. 8차시에서 배운 리스트 활용이 빛을 발하는 순간입니다!
v3에서 달라진 점: 2개가 아닌 여러 개의 공을 리스트로 관리하고, 이중 for 루프로 모든 쌍을 검사합니다. 충돌 시 색상 변경 + 방향 반전을 동시에 적용합니다.