7차시: 내가 조종하는 3D 세계 - 키보드·마우스 입력으로 객체 제어하기¶
🎮 키보드 입력 (keysdown)¶
while 루프 안에서 현재 눌린 키를 감지하여 객체를 실시간으로 움직이는 방법을 배웁니다
🖱️ 마우스 입력 (scene.mouse)¶
마우스 클릭 이벤트를 감지하고, 클릭한 위치에 새 객체를 생성하는 인터랙션을 구현합니다
🕹️ 실습: 공 조종기¶
화살표 키로 공을 상하좌우로 움직이고, 클릭으로 별을 찍는 미니 프로그램을 완성합니다
🚀 도전: 스페이스바 발사¶
스페이스바를 누르면 공이 발사되는 기능까지 결합하여 게임의 기초를 만들어봅니다
🔍 도입: 게임 속 입력의 비밀 (5분)¶
일상의 게임 경험과 연결하여 "사용자 입력"이 무엇인지 탐구합니다. 파이썬의 input()과 VPython 실시간 입력의 차이를 이해합니다.
🎮 핵심 개념 1: 키보드 입력 — keysdown() (12분)¶
keysdown() 함수의 원리를 배우고, 화살표 키로 공을 움직이는 코드를 단계별로 작성합니다.
🖱️ 핵심 개념 2: 마우스 입력 — scene.mouse (10분)¶
scene.mouse.getclick()으로 클릭 이벤트를 감지하고, 클릭 위치에 객체를 생성하는 코드를 작성합니다.
🛠️ 통합 실습: 키보드+마우스 결합 프로그램 (15분)¶
두 입력 방식을 하나의 프로그램에 통합합니다. 에러 경험과 디버깅을 포함합니다.
📝 평가 및 성찰 (8분)¶
퀴즈 풀이, 연습 문제 도전, 자기점검 체크리스트로 학습을 정리합니다.
🔍 도입: 게임 속 입력의 비밀¶
여러분, 게임을 할 때를 떠올려 보세요. 방향키를 누르면 캐릭터가 움직이고, 마우스를 클릭하면 공격을 하죠. 이런 동작은 어떻게 만들어지는 걸까요?
비유하자면 이렇습니다. TV 리모컨을 생각해 보세요. 버튼을 누르는 순간, TV가 그 신호를 감지해서 채널을 바꾸거나 볼륨을 조절합니다. 프로그래밍에서도 마찬가지입니다. 키보드와 마우스는 리모컨이고, 우리 프로그램이 TV 역할을 하는 것입니다.
🤔 파이썬의 input()과 뭐가 다를까?¶
여러분은 이미 파이썬에서 input() 함수를 사용해 봤을 것입니다. 하지만 input()에는 큰 한계가 있습니다.
| 비교 항목 | 파이썬 input() |
VPython keysdown() |
|---|---|---|
| 입력 방식 | 텍스트를 타이핑하고 Enter를 누름 | 키를 누르는 순간 바로 감지 |
| 프로그램 동작 | 입력 대기 중 멈춤 (블로킹) | 입력과 애니메이션이 동시에 작동 |
| 사용 사례 | 이름 입력, 메뉴 선택 | 게임 캐릭터 이동, 실시간 제어 |
| 반복 감지 | 매번 새로 호출해야 함 | while 루프 안에서 계속 확인 |
핵심 차이는 실시간성입니다. 게임에서 방향키를 누르고 있는 동안 캐릭터가 계속 움직여야 하잖아요? input()은 이걸 할 수 없지만, VPython의 keysdown()은 가능합니다!
💡
input()은 "말 한마디 하고 기다리기"이고,keysdown()은 "귀를 계속 열어두고 듣기"입니다.
아래 다이어그램은 두 방식의 흐름 차이를 보여줍니다.
🎮 핵심 개념 1: 키보드 입력 — keysdown()¶
📖 개념 이해¶
keysdown() 함수는 지금 이 순간 눌려 있는 키 목록을 반환합니다. 리스트처럼 여러 키를 동시에 감지할 수 있습니다.
정확한 정의를 보겠습니다:
keysdown(): 현재 눌려 있는 모든 키를 리스트 형태로 반환하는 VPython 내장 함수- 반환값:
['left'],['left', 'up']등 — 동시에 여러 키가 눌릴 수 있음 - 반드시 while 루프 안에서 사용해야 합니다 (매 프레임마다 확인해야 하니까요!)
자주 쓰는 키 이름을 정리해 두겠습니다:
| 키 | keysdown()에서의 이름 | 설명 |
|---|---|---|
| ← 왼쪽 화살표 | 'left' |
왼쪽 이동 |
| → 오른쪽 화살표 | 'right' |
오른쪽 이동 |
| ↑ 위쪽 화살표 | 'up' |
위쪽 이동 |
| ↓ 아래쪽 화살표 | 'down' |
아래쪽 이동 |
| 스페이스바 | ' ' (공백 문자) |
점프, 발사 등 |
| A, B, C 등 | 'a', 'b', 'c' |
소문자로 인식 |
⚠️ 주의: 스페이스바는
'space'가 아니라' '(공백 한 칸)입니다! 이건 많은 분이 헷갈려 하는 부분이에요.
🔨 따라하기: v1 — 가장 기본적인 키 감지¶
실행 환경: VPython 3.2 (Web VPython 또는 로컬 설치)
먼저 키를 누르면 어떤 키가 눌렸는지 확인만 해보겠습니다.
from vpython import *
# 빈 장면에 공 하나 만들기
ball = sphere(pos=vector(0, 0, 0), radius=0.5, color=color.cyan)
while True:
rate(30) # <- 1초에 30번 루프 반복 (애니메이션 속도 제어)
keys = keysdown() # <- 여기가 [키보드 입력 감지]
if len(keys) > 0:
print(keys) # 눌린 키 목록을 출력
이 코드를 실행하고 키보드의 화살표 키를 눌러보세요. 아래와 같은 출력이 나옵니다:
위 코드의 6번째 줄 keys = keysdown()이 바로 키보드 입력 감지입니다. 매 루프마다 "지금 뭐가 눌려 있나?"를 확인하는 것이죠.
4번째 줄 rate(30)은 이전 차시에서 배운 애니메이션 속도 제어입니다. 이것이 없으면 루프가 너무 빨리 돌아서 컴퓨터에 부담이 됩니다.
🔨 따라하기: v2 — 키에 따라 공 움직이기¶
이제 단순 출력이 아니라, 실제로 공의 위치를 변경해 보겠습니다. v1에서 print(keys) 부분을 조건문으로 바꿔볼까요?
from vpython import *
ball = sphere(pos=vector(0, 0, 0), radius=0.5, color=color.cyan)
speed = 0.1 # 한 번에 움직일 거리
while True:
rate(30)
keys = keysdown() # 현재 눌린 키 확인
# 왼쪽 화살표가 눌려 있으면 x좌표 감소
if 'left' in keys: # <- 여기가 [특정 키 감지]
ball.pos.x -= speed
# 오른쪽 화살표가 눌려 있으면 x좌표 증가
if 'right' in keys:
ball.pos.x += speed
# 위쪽 화살표 → y좌표 증가
if 'up' in keys:
ball.pos.y += speed
# 아래쪽 화살표 → y좌표 감소
if 'down' in keys:
ball.pos.y -= speed
v1에서 뭐가 바뀌었나요?
- print(keys) → 4개의 if문으로 교체
- speed 변수 추가 (움직임 크기를 한 곳에서 관리)
- 각 화살표 키마다 ball.pos의 x 또는 y를 변경
위 코드의 12번째 줄 if 'left' in keys:가 바로 특정 키 감지 패턴입니다. in 연산자로 리스트 안에 해당 키가 있는지 확인하는 것이죠. 여러분이 이미 배운 리스트의 in 연산자를 그대로 활용하는 것입니다!
🎉 실행해서 화살표 키를 눌러보세요. 공이 상하좌우로 움직이나요? 대각선으로도 움직여 보세요! (두 키를 동시에 누르면 됩니다)
🔨 따라하기: v3 — 바닥과 벽 추가로 장면 꾸미기¶
공만 덩그러니 있으면 심심하죠? 바닥과 경계를 추가하고, 공이 경계 밖으로 나가지 않도록 해봅시다.
from vpython import *
# 장면 설정
scene.background = color.white
scene.width = 600
scene.height = 400
# 바닥 만들기 (납작한 상자)
ground = box(pos=vector(0, -3, 0), size=vector(12, 0.2, 6),
color=color.green)
# 조종할 공
ball = sphere(pos=vector(0, 0, 0), radius=0.5, color=color.cyan)
speed = 0.1
boundary = 5 # 공이 이동할 수 있는 최대 범위
while True:
rate(30)
keys = keysdown()
if 'left' in keys:
ball.pos.x -= speed
if 'right' in keys:
ball.pos.x += speed
if 'up' in keys:
ball.pos.y += speed
if 'down' in keys:
ball.pos.y -= speed
# 경계 제한: 공이 범위를 벗어나지 않도록 # <- 여기가 [경계 검사]
if ball.pos.x > boundary:
ball.pos.x = boundary
if ball.pos.x < -boundary:
ball.pos.x = -boundary
if ball.pos.y > boundary:
ball.pos.y = boundary
if ball.pos.y < -boundary + ball.radius:
ball.pos.y = -boundary + ball.radius
v2에서 뭐가 바뀌었나요?
- 장면 배경색과 크기 설정 추가
- ground 바닥 상자 추가
- boundary 변수와 경계 검사 코드 추가 (31~38번째 줄)
31번째 줄부터 시작하는 경계 검사 코드가 바로 경계 검사(Boundary Check) 패턴입니다. 게임에서 캐릭터가 화면 밖으로 나가지 않도록 하는 기본 기법이에요.
🐛 에러 경험: 흔한 실수¶
자, 여기서 많은 학습자가 빠지는 함정이 있습니다. 아래 코드를 실행하면 어떤 문제가 생길까요?
from vpython import *
ball = sphere(pos=vector(0, 0, 0), radius=0.5, color=color.cyan)
speed = 0.1
while True:
rate(30)
keys = keysdown()
if 'left' in keys:
ball.pos.x -= speed
elif 'right' in keys: # <- elif 사용!
ball.pos.x += speed
elif 'up' in keys: # <- elif 사용!
ball.pos.y += speed
elif 'down' in keys: # <- elif 사용!
ball.pos.y -= speed
🤔 질문: 이 코드를 실행하고 왼쪽 키와 위쪽 키를 동시에 누르면 어떻게 될까요?
정답 확인
**문제**: 공이 **왼쪽으로만** 움직이고, 위로는 움직이지 않습니다! **원인**: `elif`는 첫 번째로 참인 조건만 실행하고 나머지는 건너뜁니다. `'left' in keys`가 참이면 `elif 'up' in keys`는 아예 검사하지 않아요. **해결**: 각 키를 **독립적인 `if`문**으로 작성해야 합니다. 그래야 동시에 여러 키를 누를 때 각각 처리됩니다. **핵심**: 동시 입력을 허용하려면 `if`-`if`-`if`, 하나만 선택하게 하려면 `if`-`elif`-`elif`를 사용합니다!이것은 정말 중요한 패턴입니다. 아래 표로 정리해 두겠습니다:
| 패턴 | 코드 구조 | 동시 입력 | 사용 상황 |
|---|---|---|---|
| 독립 조건 | if / if / if |
✅ 가능 | 방향키 이동 (대각선 허용) |
| 배타 조건 | if / elif / elif |
❌ 불가 | 메뉴 선택 (하나만 실행) |
🖱️ 핵심 개념 2: 마우스 입력 — scene.mouse¶
📖 개념 이해¶
키보드만으로는 뭔가 부족하죠? 이번에는 마우스 클릭을 감지해 봅시다.
비유를 하나 들어볼게요. 키보드 입력이 "리모컨 버튼 누르기"라면, 마우스 클릭은 "화면 위 원하는 곳을 손가락으로 터치하기"입니다. 단순히 "뭔가를 했다"는 신호뿐 아니라, "어디를" 했는지 위치 정보까지 알려준다는 점이 다릅니다.
VPython에서 마우스 입력을 처리하는 핵심 도구:
| 속성/함수 | 설명 | 반환값 |
|---|---|---|
scene.mouse.getclick() |
클릭할 때까지 대기, 클릭 정보 반환 | 클릭 이벤트 객체 |
scene.mouse.pos |
마우스 현재 위치 (3D 좌표) | vector(x, y, z) |
scene.waitfor('click') |
클릭을 기다리는 또 다른 방법 | 이벤트 객체 |
⚠️ 중요한 차이:
getclick()과keysdown()은 성격이 다릅니다!keysdown()은 "지금 눌려있는 키"를 확인하지만,getclick()은 "클릭이 일어날 때까지 기다렸다가" 정보를 줍니다.
아래 다이어그램으로 흐름을 비교해 보겠습니다:
🔨 따라하기: v1 — 클릭한 곳에 공 만들기¶
가장 기본적인 마우스 클릭 처리를 해봅시다. 클릭할 때마다 그 위치에 빨간 공이 생깁니다.
from vpython import *
scene.background = color.white
# 안내 문구 (3D 장면 위에 표시)
scene.caption = "화면을 클릭하면 빨간 공이 생깁니다!"
while True:
rate(30)
# 클릭 이벤트 확인 # <- 여기가 [마우스 이벤트 감지]
evt = scene.mouse.getclick()
# 클릭한 위치 가져오기 # <- 여기가 [클릭 위치 추출]
click_pos = evt.pos
# 클릭 위치에 빨간 공 생성
sphere(pos=click_pos, radius=0.3, color=color.red)
실행하고 3D 장면의 여러 곳을 클릭해 보세요. 클릭한 곳마다 빨간 공이 나타납니다!
위 코드의 11번째 줄 evt = scene.mouse.getclick()이 바로 마우스 이벤트 감지입니다. 이 줄에서 프로그램은 클릭이 일어날 때까지 잠시 기다립니다.
12번째 줄 click_pos = evt.pos가 클릭 위치 추출입니다. evt.pos는 vector(x, y, z) 형태로 3D 좌표를 알려줍니다.
🔨 따라하기: v2 — 클릭할 때마다 다른 색상의 별 찍기¶
단순 빨간 공 대신, 클릭할 때마다 랜덤 색상으로 별(작은 구)을 찍어봅시다.