````markdown
# Input - 입력 관리 서브시스템
**"윈도우 이벤트에서 게임 입력으로"**를 담당하는 입력 처리 계층입니다.
## 📦 모듈 구성
### `manager.rs`
- **`InputManager`**: 입력의 최상위 관리자
- winit 이벤트를 InputState로 변환
- 프레임 단위 입력 상태 관리
- Engine이 직접 소유하는 유일 인스턴스
```rust
let mut input_manager = InputManager::new();
// 이벤트 처리
input_manager.process_window_event(&event);
// 상태 조회
if input_manager.state().is_key_down(KeyCode::KeyW) {
// 앞으로 이동
}
```
## 🎯 설계 철학
### 유일 인스턴스 (Singleton-like)
`InputManager`는 Engine이 단 하나만 소유하며, 모든 입력 처리를 중앙화합니다.
```
Engine
└── InputManager (1개)
└── state: InputState
├── keys_down: HashSet<KeyCode>
├── keys_pressed_this_frame: HashSet<KeyCode>
├── mouse_position: Vec2
└── mouse_buttons_down: HashSet<MouseButton>
```
### Polling 방식
매 프레임마다 현재 입력 상태를 조회하는 방식으로 게임 로직에 적합합니다.
```rust
// 연속 입력 (이동, 조작)
if input.is_key_down(KeyCode::KeyW) {
position.y -= speed * delta_time; // 매 프레임 실행
}
// 단일 입력 (점프, 공격)
if input.is_key_just_pressed(KeyCode::Space) {
jump(); // 1프레임만 실행
}
```
### 계층 구조
```
core/input.rs (추상 - 입력 상태 정의)
↑
runtime/input/ (구현 - 입력 이벤트 관리)
└── manager.rs
```
## 📊 입력 처리 흐름
### 전체 파이프라인
```
1. OS 키보드 이벤트
↓
2. winit::WindowEvent::KeyboardInput
↓
3. preview_runner (이벤트 루프)
└─ 이벤트 필터링
↓
4. Engine::handle_window_event(&event)
↓
5. InputManager::process_window_event(event)
├─ process_keyboard()
├─ process_mouse_move()
├─ process_mouse_button()
└─ process_mouse_wheel()
↓
6. InputState 업데이트
├─ keys_down.insert()
├─ keys_pressed_this_frame.insert()
└─ mouse_position = new_pos
↓
7. Engine::update(delta_time)
├─ input_manager.begin_frame()
├─ world.update(dt, &input_state)
│ └─ System들이 input 참조
│ └─ context.input.is_key_down()
└─ input_manager.end_frame()
```
### 프레임 관리
```rust
// 프레임 시작
input_manager.begin_frame();
// → keys_pressed_this_frame.clear()
// → mouse_buttons_pressed_this_frame.clear()
// 게임 로직 실행
world.update(dt, input_manager.state());
// 프레임 종료
input_manager.end_frame();
// → mouse_delta = Vec2::zero()
// → mouse_wheel_delta = 0.0
```
## 🔗 관련 계층
- **`core/input.rs`**: `InputState` 정의 (추상 계층)
- **`core/system.rs`**: `SystemContext`에 `InputState` 참조 포함
- **`runtime/input/manager.rs`**: `InputManager` 구현 (이 파일)
- **`runtime/engine.rs`**: `InputManager` 소유 및 관리
- **`systems/`**: 모든 System이 `context.input` 사용 가능
## 🚀 사용 예시
### InputManager 생성 (Engine 내부)
```rust
let input_manager = InputManager::new();
```
### 이벤트 처리 (Engine)
```rust
impl Engine {
pub fn handle_window_event(&mut self, event: &WindowEvent) {
self.input_manager.process_window_event(event);
}
}
```
### 게임 루프 (Engine::update)
```rust
pub fn update(&mut self, delta_time: f32) -> Result<(), SystemError> {
self.input_manager.begin_frame();
// World에 InputState 참조 전달
self.world.update(delta_time, self.input_manager.state())?;
self.input_manager.end_frame();
Ok(())
}
```
### System에서 입력 사용
```rust
use moltrun_core::core::{System, SystemContext, SystemError};
use winit::keyboard::KeyCode;
pub struct PlayerMovementSystem {
pub speed: f32,
}
impl System for PlayerMovementSystem {
fn update(&mut self, context: &mut SystemContext) -> Result<(), SystemError> {
let input = context.input;
let dt = context.delta_time;
for entity in context.entities.iter_mut() {
if let Some(transform) = entity.get_component_mut::<Transform>() {
// 연속 입력 (WASD)
if input.is_key_down(KeyCode::KeyW) {
transform.position.y -= self.speed * dt;
}
if input.is_key_down(KeyCode::KeyS) {
transform.position.y += self.speed * dt;
}
if input.is_key_down(KeyCode::KeyA) {
transform.position.x -= self.speed * dt;
}
if input.is_key_down(KeyCode::KeyD) {
transform.position.x += self.speed * dt;
}
// 단일 입력 (Space)
if input.is_key_just_pressed(KeyCode::Space) {
println!("Jump!");
}
}
}
Ok(())
}
fn as_any(&self) -> &dyn std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
}
```
### 마우스 입력 사용
```rust
impl System for CameraSystem {
fn update(&mut self, context: &mut SystemContext) -> Result<(), SystemError> {
let input = context.input;
// 마우스 위치
let mouse_pos = input.mouse_position();
// 마우스 이동량 (드래그)
if input.is_mouse_button_down(MouseButton::Left) {
let delta = input.mouse_delta();
camera.position += delta;
}
// 마우스 휠 (줌)
let wheel = input.mouse_wheel_delta();
if wheel != 0.0 {
camera.zoom += wheel * 0.1;
}
Ok(())
}
}
```
## 💡 Polling API 정리
### 키보드
```rust
// 연속 입력 (누르고 있는 동안 매 프레임 true)
input.is_key_down(KeyCode::KeyW) -> bool
// 단일 입력 (눌린 첫 프레임만 true)
input.is_key_just_pressed(KeyCode::Space) -> bool
// 떼는 순간 (떼진 프레임만 true)
input.is_key_just_released(KeyCode::Escape) -> bool
```
### 마우스
```rust
// 현재 위치 (픽셀 좌표)
input.mouse_position() -> Vec2
// 이번 프레임 이동량
input.mouse_delta() -> Vec2
// 마우스 버튼
input.is_mouse_button_down(MouseButton::Left) -> bool
input.is_mouse_button_just_pressed(MouseButton::Right) -> bool
input.is_mouse_button_just_released(MouseButton::Middle) -> bool
// 휠 스크롤 (양수: 위, 음수: 아래)
input.mouse_wheel_delta() -> f32
```
## ⚡ 성능 최적화
### HashSet 사용
키 상태를 HashSet으로 관리하여 O(1) 조회 성능 보장
```rust
// O(1) 시간복잡도
if input.is_key_down(KeyCode::KeyW) { ... }
```
### 프레임별 초기화
불필요한 메모리 유지를 방지
```rust
// begin_frame(): just_pressed 플래그만 clear (작은 HashSet)
// end_frame(): delta 값만 0으로 초기화 (Vec2, f32)
```
### 참조 전달
InputState를 복사하지 않고 불변 참조로 공유
```rust
// ✅ 복사 없음
world.update(dt, &input_state);
// ❌ 불필요한 복사
world.update(dt, input_state.clone());
```
## ⚠️ 주의사항
### just_pressed vs down
```rust
// ✅ 올바름: 이동은 is_key_down (연속)
if input.is_key_down(KeyCode::KeyW) {
position.y -= speed * dt; // 매 프레임 이동
}
// ❌ 잘못됨: 이동에 just_pressed 사용
if input.is_key_just_pressed(KeyCode::KeyW) {
position.y -= speed * dt; // 1프레임만 이동 (버그!)
}
```
```rust
// ✅ 올바름: 점프는 is_key_just_pressed (단일)
if input.is_key_just_pressed(KeyCode::Space) {
apply_jump_force(); // 1번만 실행
}
// ❌ 잘못됨: 점프에 is_key_down 사용
if input.is_key_down(KeyCode::Space) {
apply_jump_force(); // 매 프레임 실행 (버그!)
}
```
### begin_frame / end_frame 호출
```rust
// ✅ 올바름: Engine::update()에서 호출
pub fn update(&mut self, dt: f32) {
self.input_manager.begin_frame(); // 필수!
self.world.update(dt, self.input_manager.state())?;
self.input_manager.end_frame(); // 필수!
}
// ❌ 잘못됨: 호출하지 않으면 just_pressed가 계속 true
pub fn update(&mut self, dt: f32) {
self.world.update(dt, self.input_manager.state())?;
// → 버그: just_pressed가 clear되지 않음
}
```
## 🔮 향후 확장 가능성
### 입력 매핑 시스템
```rust
// 키를 액션에 매핑
input_manager.map_key(KeyCode::KeyW, "Move Forward");
if input.is_action_pressed("Move Forward") { ... }
```
### Event 방식 추가 (UI용)
```rust
// Polling과 병행하여 Event 큐 추가
for event in input.poll_events() {
match event {
InputEvent::KeyPressed(KeyCode::Enter) => submit_form(),
InputEvent::TextInput(ch) => text_field.insert(ch),
}
}
```