# Scene - 씬 관리 서브시스템
**"YAML에서 World로"**를 담당하는 씬 로딩 및 전환 계층입니다.
## 📦 모듈 구성
### `manager.rs`
- **`SceneManager`**: 씬의 최상위 관리자
- YAML 파일 기반 씬 로딩
- 엔티티 생명주기 관리 (생성/삭제)
- Engine이 소유하는 유일 인스턴스
```rust
let mut scene_manager = SceneManager::new("templates/");
scene_manager.load_scene("main_menu", &mut world)?;
```
### `manifest/` - YAML 파싱
YAML 파일을 Rust 구조체로 변환
- **`loader.rs`**: 파일 I/O 및 디시리얼라이즈
- **`structure.rs`**: `SceneManifest`, `EntityInstance` 정의
- **`yaml_support.rs`**: 타입 안전한 키 관리
```rust
let manifest = load_scene_manifest("world/scenes/main_menu.yaml")?;
```
### `solver.rs` - 템플릿 해석
템플릿 상속 및 컴포넌트 병합
- 템플릿 캐싱 (성능 최적화)
- 컴포넌트 오버라이드 처리
- 최종 해석된 씬 생성
```rust
let resolved = solver.resolve_scene(&manifest)?;
```
### `factory.rs` - 컴포넌트 생성
YAML 데이터를 실제 Component로 변환
- `ComponentFactory` trait 정의
- `ComponentRegistry` (팩토리 관리)
- Transform, Sprite, Text 팩토리 구현
```rust
let component = registry.create("Transform", &yaml_data)?;
```
## 🎯 설계 철학
### 유일 인스턴스 (Singleton-like)
`SceneManager`는 Engine이 단 하나만 소유하며, 모든 씬 전환을 중앙화합니다.
```
Engine
└── SceneManager (1개)
├── manifest_solver: ManifestSolver (템플릿 캐시)
├── component_registry: ComponentRegistry (팩토리들)
├── current_scene: Option<String>
└── scene_entities: HashMap<String, Vec<EntityId>>
```
### 선언적 씬 정의
코드 대신 YAML로 씬을 정의하여 빠른 프로토타이핑이 가능합니다.
```yaml
# world/scenes/main_menu.yaml
entities:
title:
template: "ui/text_label"
Transform:
position: [640, 180]
Text:
content: "MAIN MENU"
```
### 템플릿 재사용
공통 엔티티 구조를 템플릿으로 정의하고 재사용합니다.
```yaml
# world/entities/ui/text_label.yaml (템플릿)
components:
Transform: { position: [0, 0], scale: [1, 1] }
Text: { content: "", font_size: 24, color: [1, 1, 1, 1] }
```
## 📊 씬 로딩 흐름
### 전체 파이프라인
```
SceneManager::load_scene("main_menu", &world)
├── 1. 기존 씬 언로드
│ └── scene_entities[old] 모두 world.destroy_entity()
│
├── 2. YAML 로드
│ └── manifest::load_scene_manifest("world/scenes/main_menu.yaml")
│
├── 3. 템플릿 해석
│ ├── solver.resolve_scene(&manifest)
│ ├── 템플릿 로드 및 캐싱
│ └── 컴포넌트 병합 (템플릿 + 오버라이드)
│
├── 4. 엔티티 생성
│ ├── world.create_entity()
│ ├── factory.create("Transform", yaml)
│ ├── factory.create("Sprite", yaml)
│ └── entity.add_component_boxed(component)
│
└── 5. 추적 정보 저장
└── scene_entities["main_menu"] = [entity_ids...]
```
## 🔗 관련 계층
- **`runtime/`**: `Engine`이 `SceneManager` 소유 (선택적)
- **`core/`**: `Entity`, `Component` trait 사용
- **`entity/components/`**: Transform, Sprite, Text 등 구현
## 🚀 사용 예시
### SceneManager 생성
```rust
let mut scene_manager = SceneManager::new("templates/shooter-tutorial");
```
### 씬 로드
```rust
// YAML 파일에서 씬 로드 (자동으로 World에 엔티티 생성)
scene_manager.load_scene("main_menu", &mut world)?;
// 렌더링 (생성된 엔티티들 자동 처리)
render_system.render(&world)?;
```
### 씬 전환
```rust
// 기존 씬 정리 + 새 씬 로드
scene_manager.transition_to_scene("game_level1", &mut world)?;
```
### 현재 씬 확인
```rust
if let Some(current) = scene_manager.current_scene() {
println!("Current scene: {}", current);
}
```
### 커스텀 컴포넌트 추가
```rust
// 1. ComponentFactory 구현
struct VelocityFactory;
impl ComponentFactory for VelocityFactory {
fn create_component(&self, yaml: &Value) -> Result<Box<dyn Component>, FactoryError> {
let x = yaml["x"].as_f64().unwrap_or(0.0) as f32;
let y = yaml["y"].as_f64().unwrap_or(0.0) as f32;
Ok(Box::new(Velocity { x, y }))
}
}
// 2. 레지스트리에 등록
scene_manager.component_registry.register("Velocity", VelocityFactory);
// 3. YAML에서 사용
// entities:
// player:
// Velocity: { x: 100, y: 0 }
```
## 💡 YAML 파일 구조
### 씬 파일 (world/scenes/*.yaml)
```yaml
entities:
entity_name:
template: "optional/template/path" # 템플릿 상속
ComponentName: # 직접 정의 또는 오버라이드
property: value
```
### 템플릿 파일 (world/entities/**/*.yaml)
```yaml
components:
ComponentName:
property: default_value
```
### 실제 예시
```yaml
# world/scenes/main_menu.yaml
entities:
background:
template: "environment/static_background"
Sprite:
texture: "backgrounds/menu.png"
title:
template: "ui/text_label"
Transform:
position: [640, 100]
Text:
content: "SHOOTER GAME"
font_size: 48
```
## ⚡ 성능 최적화
### 템플릿 캐싱
```rust
// 첫 번째 엔티티: 템플릿 로드 (디스크 I/O)
resolver.resolve_entity("ui/button", overrides)?;
// 두 번째 엔티티: 캐시에서 재사용 (메모리만)
resolver.resolve_entity("ui/button", overrides)?; // 빠름!
```
### 컴포넌트 팩토리 패턴
- 런타임 타입 체크 없이 빠른 생성
- HashMap 룩업 한 번으로 적절한 팩토리 선택
### 씬별 엔티티 추적
```rust
// O(n) 복잡도로 씬 전체 정리
for entity_id in scene_entities["old_scene"] {
world.destroy_entity(entity_id);
}
```
## ⚠️ 주의사항
### YAML 경로
```rust
// ✅ 올바름: 상대 경로 (base_path 기준)
scene_manager.load_scene("main_menu", &world)?;
// → templates/world/scenes/main_menu.yaml
// ❌ 잘못됨: 절대 경로 사용 금지
scene_manager.load_scene("/Users/.../main_menu.yaml", &world)?;
```
### 템플릿 경로
```yaml
# ✅ 올바름: 슬래시로 구분
template: "ui/text_label"
# → world/entities/ui/text_label.yaml
# ❌ 잘못됨: 파일 확장자 포함 금지
template: "ui/text_label.yaml"
```
### 컴포넌트 이름 일치
```rust
// Factory 등록 이름과 YAML 키가 일치해야 함
registry.register("Transform", TransformFactory);
// YAML에서도 정확히 "Transform" 사용
// Transform: ✅
// transform: ❌ (대소문자 구분)
```
### 순환 템플릿 방지
```yaml
# ❌ 순환 참조 금지
# a.yaml: template: "b"
# b.yaml: template: "a"
# → 무한 루프 발생!
```