코스 4: Three Ursina 3D - 3D 게임 개발
코스 개요
이 코스에서 학생들은 실제로 플레이 가능한 3D 게임을 만듭니다. 물리 엔진, 충돌 감지, 시간 제어 등 게임 개발의 핵심 기술을 배웁니다. 단순히 코드를 따라 치는 것을 넘어, 게임의 메커니즘을 이해하고 자신만의 게임을 설계할 수 있는 능력을 기릅니다.
예상 소요 시간: 12~24시간
소요 시간은 학생의 학년, 사전 경험, 학습 속도에 따라 크게 달라집니다. 또한 기본 개념만 익히고 넘어가는 경우와 추가 실습 문제나 창의적 프로젝트를 더 진행하는 경우에 따라서도 차이가 납니다. 위 시간은 평균적인 범위이며, 자녀의 페이스에 맞추어 유연하게 진행하시기 바랍니다.
어떤 능력을 키우게 되나요?
실시간 데이터 처리(Real-time Data Processing) 능력을 훈련합니다. 게임은 1초에 60번 데이터를 읽고, 계산하고, 화면에 반영합니다. 이는 주식 거래 시스템, 자율주행차, IoT 센서 등 실시간으로 데이터를 처리해야 하는 모든 시스템의 기본 원리입니다.
어떤 원리를 배우나요?
- (2차원 데이터) 중첩 반복문으로 행×열 구조를 만들면서, 엑셀 시트, 이미지 픽셀, 게임 맵 같은 2차원 데이터 구조를 이해합니다. 데이터 분석에서 가장 많이 다루는 테이블 형태입니다.
- (시계열 데이터) 시간에 따라 변하는 위치, 속도 데이터를 다루면서 시계열 데이터(Time-series Data)의 개념을 체험합니다. 주가, 기온, 심박수 등 시간에 따른 변화를 분석하는 원리와 같습니다.
AI 시대 사고력: 컴퓨팅 (문제해결) | 데이터 기반 | 확률적 사고력
컴퓨팅 사고력 (Computational Thinking)
| 스킬 | 설명 | 활동 예시 |
|---|---|---|
| 🔲 2D 구조 생성 | 중첩 반복문으로 격자 만들기 | 10×10 블록 그리드 생성 |
| ⚛️ 물리 시뮬레이션 | 현실 법칙을 코드로 표현 | 중력, 충돌, 마찰 구현 |
| ⏱️ 시간 제어 | 델타 타임으로 일정 속도 유지 | 프레임 독립적 움직임 |
| 💥 충돌 감지 | 객체 간 겹침 확인 | "적과 닿으면 게임 오버" |
수학적 연결고리
| 수학 개념 | 프로그래밍 적용 | 학습 효과 |
|---|---|---|
| 곱셈 원리 | 5행 × 4열 = 20개 블록 | 경우의 수, 배열 크기 계산 |
| 속도 공식 | 거리 = 속도 × 시간 |
position += velocity * time.dt |
| 중력 가속도 | velocity += gravity * dt |
등가속도 운동 |
| 좌표 인덱싱 | grid[row][col] |
2차원 배열 인덱스 |
중첩 반복문 시각화
flowchart LR
subgraph ROW0["row = 0"]
A0["(0,0)"] --> A1["(0,1)"] --> A2["(0,2)"] --> A3["(0,3)"]
end
subgraph ROW1["row = 1"]
B0["(1,0)"] --> B1["(1,1)"] --> B2["(1,2)"] --> B3["(1,3)"]
end
subgraph ROW2["row = 2"]
C0["(2,0)"] --> C1["(2,1)"] --> C2["(2,2)"] --> C3["(2,3)"]
end
ROW0 --> ROW1 --> ROW2
총 실행 횟수: 3행 × 4열 = 12번
물리 시뮬레이션 공식
| 물리 개념 | 수학 공식 | 코드 표현 |
|---|---|---|
| 등속 운동 | x = x₀ + v·t | x += speed * dt |
| 중력 낙하 | v = v₀ + g·t | vy += gravity * dt |
| 포물선 운동 | 수평 + 수직 결합 | x += vx*dt, y += vy*dt |
💻 코드 예시 & 시각화
예시 1: 중첩 반복문 - 3D 그리드
from ursina import *
app = Ursina()
# 5×5 그리드 생성
for row in range(5):
for col in range(5):
cube = Entity(
model='cube',
color=color.azure,
position=(col * 2, 0, row * 2),
scale=0.9
)
EditorCamera() # 카메라 조작 가능
app.run()
중첩 반복문 실행 순서:
row=0: col= 0 1 2 3 4 (5개)
row=1: col= 0 1 2 3 4 (5개)
row=2: col= 0 1 2 3 4 (5개)
row=3: col= 0 1 2 3 4 (5개)
row=4: col= 0 1 2 3 4 (5개)
────
총 25개 (5×5)
결과 (위에서 본 모습):
┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐
│■│ │■│ │■│ │■│ │■│ row=4
├─┤ ├─┤ ├─┤ ├─┤ ├─┤
│■│ │■│ │■│ │■│ │■│ row=3
├─┤ ├─┤ ├─┤ ├─┤ ├─┤
│■│ │■│ │■│ │■│ │■│ row=2
├─┤ ├─┤ ├─┤ ├─┤ ├─┤
│■│ │■│ │■│ │■│ │■│ row=1
├─┤ ├─┤ ├─┤ ├─┤ ├─┤
│■│ │■│ │■│ │■│ │■│ row=0
└─┘ └─┘ └─┘ └─┘ └─┘
col=0 col=1 col=2 col=3 col=4
예시 2: 물리 시뮬레이션 - 중력
from ursina import *
app = Ursina()
player = Entity(model='cube', color=color.red, position=(0, 5, 0))
ground = Entity(model='plane', color=color.green, scale=10)
velocity_y = 0
gravity = -9.8
def update():
global velocity_y
# 중력 적용: v = v + g × dt
velocity_y += gravity * time.dt
# 위치 업데이트: y = y + v × dt
player.y += velocity_y * time.dt
# 바닥 충돌 검사
if player.y <= 0.5:
player.y = 0.5
velocity_y = 0
app.run()
물리 시뮬레이션 과정:
시간 │ 속도(v) │ 위치(y) │ 상태
─────┼─────────┼─────────┼──────
0.0s │ 0.0 │ 5.0 │ 시작
0.1s │ -0.98 │ 4.9 │ 낙하중
0.2s │ -1.96 │ 4.7 │ 낙하중
0.3s │ -2.94 │ 4.4 │ 낙하중
... │ ... │ ... │ ...
1.0s │ -9.8 │ 0.5 │ 착지!
┌─┐
│■│ ← 시작 위치 (y=5)
└─┘
↓ 중력
↓
↓
┌─┐
═════│■│═════ ← 착지 (y=0.5)
└─┘
예시 3: 시간 제어 - 슬로우 모션
from ursina import *
app = Ursina()
cube = Entity(model='cube', color=color.orange)
speed = 5
def update():
# time.dt를 사용해 일정한 속도 유지
cube.x += speed * time.dt
# 스페이스바로 슬로우 모션 토글
if held_keys['space']:
application.time_scale = 0.2 # 20% 속도
else:
application.time_scale = 1.0 # 정상 속도
app.run()
델타 타임의 중요성:
time.dt 없이:
──────────────────────────────
60fps PC: ■→→→→→→→→→→→→ 빠름
30fps PC: ■→→→→→→ 느림
(프레임마다 고정값 이동)
time.dt 사용:
──────────────────────────────
60fps PC: ■→→→→→→→→→→→→ 같은 속도
30fps PC: ■→→→→→→→→→→→→ 같은 속도
(실제 경과 시간에 비례해서 이동)
예시 4: 월드 그리드 - 2D 배열로 맵 만들기
from ursina import *
app = Ursina()
# 맵 데이터 (2D 배열)
map_data = [
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 1],
[1, 0, 2, 0, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1]
]
# 0=빈공간, 1=벽, 2=플레이어
for row in range(5):
for col in range(5):
value = map_data[row][col]
if value == 1: # 벽
Entity(
model='cube',
color=color.gray,
position=(col, 0, row)
)
elif value == 2: # 플레이어
Entity(
model='cube',
color=color.red,
position=(col, 0, row)
)
EditorCamera()
app.run()
맵 데이터 → 3D 월드:
2D 배열: 3D 월드:
[1,1,1,1,1] ■ ■ ■ ■ ■
[1,0,0,0,1] → ■ ■
[1,0,2,0,1] → ■ ● ■
[1,0,0,0,1] → ■ ■
[1,1,1,1,1] ■ ■ ■ ■ ■
■ = 벽 (회색)
● = 플레이어 (빨강)
= 빈 공간
예시 5: 충돌 감지
from ursina import *
app = Ursina()
player = Entity(model='cube', color=color.red, collider='box')
coin = Entity(model='sphere', color=color.yellow,
position=(3, 0, 0), collider='sphere')
score = 0
def update():
global score
# 플레이어 이동
player.x += held_keys['d'] * 5 * time.dt
player.x -= held_keys['a'] * 5 * time.dt
# 충돌 검사
if player.intersects(coin).hit:
score += 1
coin.disable() # 코인 제거
print(f"점수: {score}")
app.run()
충돌 감지 원리:
챕터 01: 3D 기초
A. 3D란?
| 항목 | 내용 |
|---|---|
| 무엇을 배우나요? | 3차원 공간의 개념 |
| 핵심 개념 | 차원, 좌표계, 공간적 사고 |
3D는 세 개의 축(x, y, z)으로 공간을 표현합니다. 2D가 평면(종이 위)이라면, 3D는 입체(현실 세계)입니다. 학생들은 높이, 너비, 깊이의 개념을 프로그래밍적으로 이해합니다. 이것은 수학의 좌표 기하학과 직접 연결되며 공간 지각 능력을 향상시킵니다. 3D 게임, 건축 설계, 영화 제작 등 다양한 분야에서 동일한 원리가 사용됩니다.
B. Entity 기초
| 항목 | 내용 |
|---|---|
| 무엇을 배우나요? | 게임 객체의 기본 구조 |
| 핵심 개념 | 게임 오브젝트, 속성, 컴포넌트 |
Entity는 게임 세계에 존재하는 모든 것의 기본 단위입니다. 플레이어, 적, 바닥, 아이템 - 모두 Entity입니다. 학생들은 Entity가 위치, 크기, 회전, 외형 등 다양한 속성을 가진다는 것을 배웁니다. 이 통일된 구조 덕분에 일관된 방식으로 모든 게임 요소를 다룰 수 있습니다. 이것은 객체지향 프로그래밍의 기초 개념과 연결됩니다.
C. 카메라
| 항목 | 내용 |
|---|---|
| 무엇을 배우나요? | 플레이어 시점 제어 |
| 핵심 개념 | 뷰포트, 시점, 투영 |
카메라는 플레이어가 게임 세계를 보는 눈입니다. 1인칭, 3인칭, 탑다운 등 다양한 시점을 카메라 설정으로 구현합니다. 학생들은 같은 게임 세계도 카메라에 따라 완전히 다르게 느껴진다는 것을 체험합니다. 영화 촬영의 앵글, 줌 개념과 연결되어 미디어 리터러시도 향상됩니다. 카메라 위치와 회전을 조절하여 원하는 게임플레이 경험을 설계합니다.
D. 위치와 좌표
| 항목 | 내용 |
|---|---|
| 무엇을 배우나요? | 정확한 위치 지정과 이동 |
| 핵심 개념 | 벡터, 상대/절대 위치 |
position = (x, y, z)로 3D 공간의 정확한 위치를 지정합니다. 절대 위치(세계 기준)와 상대 위치(부모 기준)의 차이를 이해합니다. 학생들은 벡터 연산의 기초를 자연스럽게 익힙니다 - 위치 더하기, 빼기, 스케일링 등. 이동은 position += (dx, dy, dz)처럼 변화량을 더하는 것으로 표현됩니다. 이 개념은 물리학의 변위, 속도와 직접 연결됩니다.
E. 첫 3D 장면 만들기
| 항목 | 내용 |
|---|---|
| 무엇을 배우나요? | 여러 객체로 장면 구성하기 |
| 핵심 개념 | 장면 구성, 레벨 디자인 |
바닥, 벽, 플레이어, 배경을 조합하여 게임 환경을 구성합니다. 학생들은 개별 요소가 모여 하나의 세계를 이룬다는 것을 경험합니다. 색상, 크기, 배치를 조절하여 분위기와 게임플레이에 영향을 줍니다. 이것은 레벨 디자인의 기초로, 사용자 경험을 설계하는 능력입니다. 학생들은 자신만의 세계를 창조하는 창의적 성취감을 느낍니다.
프로젝트: 중첩 반복문 시각화 (VisualLoop)
| 항목 | 내용 |
|---|---|
| 무엇을 배우나요? | 이중 for문으로 2D 그리드 생성 |
| 핵심 개념 | 중첩 반복문, 2D 배열 개념 |
중첩 반복문은 반복문 안에 반복문이 있는 구조입니다. 바깥 루프가 행(row)을, 안쪽 루프가 열(column)을 담당하여 격자 구조를 생성합니다. 학생들은 이것이 스프레드시트, 체스판, 픽셀 이미지와 같은 구조임을 이해합니다. 시각화 도구로 반복문이 어떤 순서로 실행되는지 직접 관찰할 수 있습니다. 이 개념은 이미지 처리, 게임 맵, 데이터 테이블 등 수많은 분야에서 활용됩니다.
프로젝트: 물리와 충돌 (Platformer)
| 항목 | 내용 |
|---|---|
| 무엇을 배우나요? | 중력, 점프, 충돌 감지 |
| 핵심 개념 | 물리 시뮬레이션, 충돌 검사 |
물리 엔진은 중력, 마찰, 충돌을 자동으로 계산합니다. 학생들은 실제 물리 법칙이 게임에서 어떻게 시뮬레이션되는지 배웁니다. 충돌 감지는 두 객체가 겹쳤는지 확인하는 기술로, 모든 액션 게임의 기초입니다. "바닥에 닿으면 멈춤", "적과 닿으면 데미지" 같은 게임 규칙을 구현합니다. 물리학 개념(속도, 가속도, 힘)을 실용적인 맥락에서 체험합니다.
프로젝트: 시간 제어 (Slow Motion)
| 항목 | 내용 |
|---|---|
| 무엇을 배우나요? | 게임 속도와 시간 조절 |
| 핵심 개념 | 델타 타임, 시간 스케일 |
time.dt(델타 타임)는 프레임 간 경과 시간입니다. 이 값을 조절하면 슬로우 모션이나 배속을 구현할 수 있습니다. 학생들은 게임이 실시간으로 시간을 추적한다는 것을 배웁니다. 조건문과 결합하여 "특정 상황에서 시간이 느려짐" 같은 극적인 효과를 만듭니다. 시간 관리는 게임의 페이싱과 드라마를 결정하는 중요한 요소입니다.
프로젝트: 월드 그리드 (World Grid)
| 항목 | 내용 |
|---|---|
| 무엇을 배우나요? | 2D 배열로 게임 맵 표현 |
| 핵심 개념 | 2D 배열, 맵 데이터, 절차적 생성 |
게임 맵을 2D 배열(숫자들의 격자)로 표현합니다. 예: 0은 빈 공간, 1은 벽, 2는 적의 위치. 학생들은 데이터와 시각적 표현의 분리를 이해합니다 - 숫자 배열이 화면에서 블록으로 변환됩니다. 이것은 마인크래프트, 테트리스, 스도쿠 등 수많은 게임의 핵심 구조입니다. 맵 데이터를 수정하면 새로운 레벨을 쉽게 생성할 수 있습니다.
이 코스를 마치면...
학습 성과
학생들은 다음 능력을 갖추게 됩니다:
- 물리 엔진의 기본 원리를 이해하고 활용할 수 있음
- 충돌 감지로 게임 상호작용을 구현할 수 있음
- 카메라와 시점을 조절하여 게임 경험을 설계할 수 있음
- 중첩 반복문으로 2D 그리드 구조를 생성할 수 있음
- 시간과 속도를 제어하여 동적인 효과를 만들 수 있음
- 간단하지만 실제로 플레이 가능한 게임을 만들 수 있음