콘텐츠로 이동

8차시: 객체가 많아지면? - 리스트로 여러 3D 객체 한꺼번에 다루기

⏰ 50분 · 리스트+3D객체 · for 루프 생성 · 사용자 정의 속성 · 난이도 ●●●○○

학습목표: 파이썬 리스트와 for 루프를 활용하여 다수의 VPython 3D 객체를 효율적으로 생성하고, 각 객체에 개별 속성을 부여하여 동시에 업데이트할 수 있다.

오늘의 질문: "공 100개가 동시에 화면에서 튕겨 다니는 장면을 만들려면, 변수를 100개 만들어야 할까요? 🤔"


🎯 핵심 주제 카드

🧠 리스트로 객체 관리

for 루프 한 번이면 3D 객체 100개도 뚝딱! 리스트에 담아서 한꺼번에 다루는 패턴을 익힙니다.

💻 사용자 정의 속성

ball.velocity = vector(...) — 각 객체에 나만의 데이터를 붙이는 테크닉으로 개별 움직임을 구현합니다.

💬 for-in-리스트 업데이트

for ball in balls: 패턴으로 while 루프 안에서 모든 객체를 동시에 움직이는 핵심 구조를 완성합니다.

🚀 랜덤 색상 · 속도

random 모듈과 결합해 각 객체를 개성 있게 만들어 시각적으로 풍성한 장면을 연출합니다.


⏱️ 수업 흐름

1단계: 문제 인식 — 변수 10개의 고통 (5분)

변수를 하나하나 만들어 공 10개를 생성하는 "나쁜 코드"를 직접 보고, 왜 리스트가 필요한지 체감합니다.

2단계: 핵심 개념 — 리스트 + for 루프로 객체 생성 (10분)

리스트에 VPython 객체를 담는 방법과 사용자 정의 속성(.velocity) 개념을 배웁니다.

3단계: 따라하기 실습 — 공 10개 동시에 움직이기 (20분)

v1(리스트 생성) → v2(속성 추가) → v3(while 루프에서 업데이트) → v4(벽 반사) 순서로 코드를 점진적으로 완성합니다.

4단계: 에러 경험 & 연습 문제 (10분)

흔히 발생하는 에러를 직접 만나보고, 3단계 연습 문제로 응용력을 키웁니다.

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

오늘 배운 패턴을 정리하고, 다음 차시 충돌 감지와의 연결 고리를 확인합니다.


📚 1단계: 문제 인식 — 변수 10개의 고통 😫

변수를 하나하나 만들면 어떻게 될까요?

여러분이 학교 운동장에서 축구공 10개를 동시에 굴린다고 상상해 보세요. 공마다 이름표를 붙여야 한다면 — "공1", "공2", "공3" … "공10" — 이름 붙이는 것만으로도 지칩니다. 그런데 공이 100개라면요? 😱

프로그래밍에서도 똑같습니다. 아래 코드를 한번 보세요.

from vpython import *

# 공 10개를 각각 변수에 담는 "나쁜 코드" 😰
ball0 = sphere(pos=vector(-4, 0, 0), radius=0.3, color=color.red)
ball1 = sphere(pos=vector(-3, 0, 0), radius=0.3, color=color.blue)
ball2 = sphere(pos=vector(-2, 0, 0), radius=0.3, color=color.green)
ball3 = sphere(pos=vector(-1, 0, 0), radius=0.3, color=color.yellow)
ball4 = sphere(pos=vector(0, 0, 0), radius=0.3, color=color.orange)
ball5 = sphere(pos=vector(1, 0, 0), radius=0.3, color=color.cyan)
ball6 = sphere(pos=vector(2, 0, 0), radius=0.3, color=color.magenta)
ball7 = sphere(pos=vector(3, 0, 0), radius=0.3, color=color.white)
ball8 = sphere(pos=vector(4, 0, 0), radius=0.3, color=color.red)
ball9 = sphere(pos=vector(5, 0, 0), radius=0.3, color=color.blue)

# 이제 각 공을 움직이려면...
ball0.pos.x += 0.01
ball1.pos.x += 0.01
ball2.pos.x += 0.01
# ... 이것을 10번 반복? 100개면 100번?! 😵

💡 실행 결과: 공 10개가 일렬로 나타나지만, 움직이는 코드를 작성하려면 같은 줄을 10번씩 복사해야 합니다.

이 방식의 문제를 정리하면:

문제점 설명
코드 중복 거의 같은 줄을 계속 복사-붙여넣기
수정 어려움 색상을 바꾸려면 10줄을 다 고쳐야 함
확장 불가 100개로 늘리려면 90줄을 추가해야 함
실수 위험 ball7ball8로 오타 내면 찾기 어려움

🎯 핵심 깨달음: "같은 종류의 것이 여러 개"일 때는 리스트에 담고 반복문으로 다루는 것이 정답입니다!


📚 2단계: 핵심 개념 — 리스트 + 객체 + 사용자 정의 속성

🧠 개념 1: 리스트에 3D 객체 담기

여러분은 이미 파이썬에서 리스트를 배웠습니다:

numbers = [1, 2, 3, 4, 5]

리스트에는 숫자뿐 아니라 뭐든지 담을 수 있습니다. VPython의 sphere 객체도 마찬가지입니다!

마치 축구팀 감독이 선수 명단(리스트)을 가지고 있으면, "전원 앞으로 이동!" 한마디로 모든 선수를 움직일 수 있는 것과 같습니다. 변수를 하나하나 부르는 대신, 리스트 이름 하나로 전체를 제어하는 것이죠.

리스트로 객체를 관리하는 핵심 구조:

flowchart TD A["빈 리스트 생성<br/>balls = []"] --> B["for 루프로 객체 생성"] B --> C["sphere() 만들기"] C --> D["리스트에 추가<br/>balls.append(ball)"] D --> E{반복 완료?} E -- 아니오 --> B E -- 예 --> F["리스트 완성!<br/>balls에 객체 N개"] F --> G["for ball in balls:<br/>로 전체 제어"]

리스트 기반 객체 관리 흐름도

🧠 개념 2: 사용자 정의 속성 — 객체에 나만의 데이터 붙이기

VPython의 sphere 객체에는 기본적으로 pos, radius, color 같은 속성이 있습니다. 그런데 놀라운 점은 — 원하는 속성을 마음대로 추가할 수 있다는 것입니다!

ball = sphere(pos=vector(0,0,0), radius=0.3, color=color.red)
ball.velocity = vector(1, 2, 0)  # 내가 만든 속성!
ball.mass = 5.0                   # 이것도 내가 만든 속성!

이것은 마치 학생증에 기본 정보(이름, 학번)가 있는데, 뒷면에 메모를 적어 붙이는 것과 같습니다. "이 학생은 달리기가 빠르다"라고 메모를 붙이면 나중에 참고할 수 있는 것처럼, 객체에 .velocity를 붙이면 나중에 ball.velocity로 꺼내 쓸 수 있습니다.

⚠️ 주의: .velocity는 VPython이 원래 알고 있는 속성이 아닙니다! 파이썬이 동적으로 속성을 추가할 수 있기 때문에 가능한 것입니다. 이름은 자유롭게 정할 수 있어요 (.vel, .speed, .my_data 등).

기본 속성 vs 사용자 정의 속성 비교:

구분 기본 속성 사용자 정의 속성
예시 ball.pos, ball.radius, ball.color ball.velocity, ball.mass, ball.score
누가 만듦? VPython이 자동 생성 프로그래머가 직접 추가
화면 반영 pos 변경 시 객체가 이동 화면에 직접 영향 없음 (코드로 연결해야 함)
용도 객체의 시각적 상태 객체의 물리적/논리적 데이터 저장

📚 3단계: 따라하기 실습 — 공 10개 동시에 움직이기 🏀

실행 환경: VPython 3.2 (GlowScript 또는 로컬 설치)

✅ v1: 리스트로 공 10개 한 번에 생성하기

먼저 가장 기본적인 것부터 — for 루프로 공 10개를 만들어 봅시다!

from vpython import *

# 빈 리스트 준비 — 여기에 공들을 담을 것입니다
balls = []  # <- 여기가 [리스트 초기화]

# for 루프로 공 10개 생성
for i in range(10):  # <- 여기가 [반복 생성 패턴]
    ball = sphere(
        pos=vector(-4 + i, 0, 0),  # i를 활용해 위치를 다르게!
        radius=0.3,
        color=color.red
    )
    balls.append(ball)  # <- 여기가 [리스트에 객체 추가]

# 확인: 리스트에 몇 개가 담겼는지 출력
print("공의 개수:", len(balls))
예상 출력:
공의 개수: 10

🖥️ 화면 결과: 빨간 공 10개가 x = -4부터 x = 5까지 일렬로 나타납니다.

개념-코드 매핑:

  • 1번째 줄의 balls = []가 바로 빈 리스트 초기화입니다. 공을 담을 빈 바구니를 준비한 것이죠.
  • 4번째 줄의 for i in range(10):이 바로 반복 생성 패턴입니다. i가 0, 1, 2, ..., 9로 변하면서 10번 반복합니다.
  • 5번째 줄의 pos=vector(-4 + i, 0, 0)에서 i를 좌표에 활용하여 공마다 서로 다른 위치를 만듭니다.
  • 9번째 줄의 balls.append(ball)이 바로 리스트에 객체 추가입니다. 공을 만들 때마다 바구니에 넣는 동작이에요.

✅ v2: 각 공에 랜덤 색상과 속도 부여하기

v1에서는 공이 전부 빨간색이고 움직이지도 않았습니다. 이제 사용자 정의 속성random 모듈을 추가합니다!

v1에서 바뀐 점: import random 추가, 색상을 랜덤으로, .velocity 사용자 정의 속성 추가

from vpython import *
import random  # <- 새로 추가! 랜덤 값 생성을 위해

balls = []

for i in range(10):
    ball = sphere(
        pos=vector(-4 + i, 0, 0),
        radius=0.3,
        # 랜덤 색상: RGB 각각 0~1 사이 랜덤 값
        color=vector(random.random(), random.random(), random.random())  # <- 변경!
    )
    # 사용자 정의 속성으로 속도 부여 — 각 공이 자기만의 속도를 가짐
    ball.velocity = vector(
        random.uniform(-3, 3),  # x 방향: -3 ~ 3 사이 랜덤
        random.uniform(-3, 3),  # y 방향: -3 ~ 3 사이 랜덤
        0                       # z 방향: 0 (2D 평면에서 움직이게)
    )  # <- 여기가 [사용자 정의 속성]
    balls.append(ball)

# 첫 번째 공의 속도를 확인해 봅시다
print("첫 번째 공의 속도:", balls[0].velocity)
print("세 번째 공의 속도:", balls[2].velocity)
예상 출력 (랜덤이라 매번 다름):
첫 번째 공의 속도: <1.234, -2.567, 0>
세 번째 공의 속도: <-0.891, 1.432, 0>

🖥️ 화면 결과: 알록달록한 공 10개가 일렬로 나타납니다. 아직 움직이진 않습니다.

개념-코드 매핑:

  • 10번째 줄 color=vector(random.random(), ...) — VPython에서 색상은 vector(R, G, B)로 표현합니다. 각 값이 0~1 사이이므로 random.random()이 딱 맞습니다.
  • 14~18번째 줄 ball.velocity = vector(...) — 이것이 바로 사용자 정의 속성입니다! VPython의 sphere에는 원래 .velocity가 없지만, 파이썬이 동적으로 추가해 줍니다.

🚨 에러 경험: 흔한 실수를 만나봅시다!

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

from vpython import *
import random

balls = []

for i in range(10):
    ball = sphere(
        pos=vector(-4 + i, 0, 0),
        radius=0.3,
        color=vector(random.random(), random.random(), random.random())
    )
    ball.velocity = vector(random.uniform(-3, 3), random.uniform(-3, 3), 0)
    balls.append(ball)

# 실수! 리스트 전체에 .velocity를 호출하려고 함
print(balls.velocity)

에러 메시지:

AttributeError: 'list' object has no attribute 'velocity'

원인 분석:

balls리스트입니다. 리스트 자체에는 .velocity라는 속성이 없어요! .velocity는 리스트 안에 들어있는 각각의 sphere 객체에 붙어 있습니다.

비유하자면: 축구팀 선수 명단(리스트)에게 "네 등번호 뭐야?"라고 물어본 것과 같습니다. 명단 자체는 등번호가 없고, 명단 안의 각 선수에게 물어봐야 합니다!

수정 코드:

# 잘못된 코드:
# print(balls.velocity)  # ❌ 리스트 자체에는 velocity가 없다!

# 올바른 코드 — 리스트 안의 각 객체에 접근
print(balls[0].velocity)  # ✅ 첫 번째 공의 속도

# 또는 for 루프로 전체 순회
for ball in balls:  # ✅ 각 공에 하나씩 접근
    print(ball.velocity)

💡 핵심 정리: balls(리스트) ≠ ball(개별 객체). 리스트는 컨테이너이고, 속성은 안에 든 객체에 있습니다!


✅ v3: while 루프에서 모든 공 동시에 움직이기

이제 진짜 핵심입니다! while 루프 + for 루프 조합으로 모든 공을 동시에 업데이트합니다.

v2에서 바뀐 점: while True: 루프 안에 for ball in balls: 패턴 추가

from vpython import *
import random

balls = []

for i in range(10):
    ball = sphere(
        pos=vector(-4 + i, 0, 0),
        radius=0.3,
        color=vector(random.random(), random.random(), random.random())
    )
    ball.velocity = vector(
        random.uniform(-3, 3),
        random.uniform(-3, 3),
        0
    )
    balls.append(ball)

dt = 0.01  # 시간 간격

# 애니메이션 루프
while True:
    rate(100)  # 초당 100프레임

    # 핵심 패턴: for ball in balls — 모든 공을 순회하며 업데이트
    for ball in balls:  # <- 여기가 [리스트 순회 업데이트 패턴]
        ball.pos = ball.pos + ball.velocity * dt  # <- 여기가 [위치 업데이트]

🖥️ 화면 결과: 10개의 알록달록한 공이 각자의 속도로 사방으로 흩어지며 움직입니다! 🎉

개념-코드 매핑:

  • 25번째 줄 for ball in balls: — 이것이 바로 오늘의 핵심 패턴입니다. 리스트 balls에서 공을 하나씩 꺼내 ball이라는 변수에 넣고 아래 코드를 실행합니다.
  • 26번째 줄 ball.pos = ball.pos + ball.velocity * dt — 각 공의 현재 위치에 속도 × 시간을 더해 새 위치를 계산합니다. 모든 공에 대해 이 계산이 반복됩니다!

아래 다이어그램으로 while-for 이중 루프 구조를 확인해 봅시다:

flowchart TD A["while True: 시작"] --> B["rate(100)"] B --> C["for ball in balls:"] C --> D["ball.pos 업데이트"] D --> E{"balls의<br/>다음 공이<br/>있는가?"} E -- 예 --> C E -- 아니오 --> A

while-for 이중 루프 구조: while이 매 프레임을 만들고, for가 매 프레임마다 모든 공을 처리합니다

하지만 아직 문제가 있습니다 — 공이 화면 밖으로 날아가 버립니다! 😅


✅ v4 (최종): 벽 반사까지 추가 — 완성!