Files
game_engine/docs/superpowers/specs/2026-03-25-phase8-1-ai.md
2026-03-25 14:20:50 +09:00

152 lines
4.1 KiB
Markdown

# Phase 8-1: AI System — Design Spec
## Overview
`voltex_ai` crate를 신규 생성한다. 수동 정의 내비메시, A* 패스파인딩, 스티어링 행동을 구현한다.
## Scope
- NavMesh (수동 삼각형 폴리곤, 인접 정보)
- A* 패스파인딩 (삼각형 그래프)
- 스티어링 행동 (Seek, Flee, Arrive, Wander, FollowPath)
## Out of Scope
- 자동 내비메시 생성 (Recast 스타일 복셀화)
- String pulling (Funnel algorithm)
- 동적 장애물 회피
- ECS 통합 (AI 컴포넌트)
- 내비메시 직렬화
## Module Structure
```
crates/voltex_ai/
├── Cargo.toml
└── src/
├── lib.rs
├── navmesh.rs — NavMesh, NavTriangle
├── pathfinding.rs — A* find_path
└── steering.rs — SteeringAgent, seek/flee/arrive/wander/follow_path
```
## Dependencies
- `voltex_math` — Vec3
## Types
### NavTriangle
```rust
#[derive(Debug, Clone)]
pub struct NavTriangle {
pub indices: [usize; 3],
pub neighbors: [Option<usize>; 3],
}
```
- `indices` — NavMesh.vertices 인덱스 (CCW)
- `neighbors[i]` — edge i↔(i+1) 반대편 삼각형. None이면 경계 에지.
### NavMesh
```rust
pub struct NavMesh {
pub vertices: Vec<Vec3>,
pub triangles: Vec<NavTriangle>,
}
```
**Methods:**
- `new(vertices, triangles)` — 생성
- `find_triangle(point: Vec3) -> Option<usize>` — XZ 평면 투영으로 점이 속한 삼각형 인덱스 반환
- `triangle_center(tri_idx: usize) -> Vec3` — 삼각형 세 꼭짓점의 중심
- `edge_midpoint(tri_idx: usize, edge: usize) -> Vec3` — 에지 중점 (공유 에지 통과 지점)
### A* Pathfinding
```rust
pub fn find_path(navmesh: &NavMesh, start: Vec3, goal: Vec3) -> Option<Vec<Vec3>>
```
1. `find_triangle(start)`, `find_triangle(goal)` — 시작/목표 삼각형
2. 둘 다 찾지 못하면 None
3. 같은 삼각형이면 `vec![start, goal]`
4. A* on triangle graph:
- Node = triangle index
- Neighbors = triangle.neighbors (Some만)
- g cost = 현재까지 실제 거리 (중심점 간)
- h cost = 현재 삼각형 중심 → 목표 삼각형 중심 유클리드 거리
5. 결과: 삼각형 체인 → 각 삼각형 중심점으로 경로 생성
6. 경로 시작에 start, 끝에 goal 삽입
### SteeringAgent
```rust
#[derive(Debug, Clone, Copy)]
pub struct SteeringAgent {
pub position: Vec3,
pub velocity: Vec3,
pub max_speed: f32,
pub max_force: f32,
}
```
### Steering Functions
모두 순수 함수, `Vec3` 조향력 반환 (에이전트 velocity에 더하기 전).
```rust
pub fn seek(agent: &SteeringAgent, target: Vec3) -> Vec3
```
- desired = normalize(target - position) * max_speed
- steering = desired - velocity
- truncate to max_force
```rust
pub fn flee(agent: &SteeringAgent, threat: Vec3) -> Vec3
```
- seek 반대 방향
```rust
pub fn arrive(agent: &SteeringAgent, target: Vec3, slow_radius: f32) -> Vec3
```
- distance < slow_radius이면 desired speed = max_speed * (distance / slow_radius)
- 목표에 매우 가까우면 (< 0.01) 제로
```rust
pub fn wander(agent: &SteeringAgent, wander_radius: f32, wander_distance: f32, angle: f32) -> Vec3
```
- agent 전방 wander_distance에 원(wander_radius) 위의 점을 target으로 seek
- angle은 호출자가 매 프레임 랜덤 변경
```rust
pub fn follow_path(agent: &SteeringAgent, path: &[Vec3], current_waypoint: usize, waypoint_radius: f32) -> (Vec3, usize)
```
- current_waypoint에 도달하면 다음으로 전진
- 마지막 웨이포인트면 arrive
- 아니면 seek
- 반환: (steering_force, updated_waypoint_index)
## Test Plan
### navmesh.rs
- 정사각형(2 삼각형) 내비메시 생성
- find_triangle: 내부 점 → Some, 외부 점 → None
- triangle_center: 정확한 중심
- edge_midpoint: 공유 에지 중점
### pathfinding.rs
- 같은 삼각형: 직선 경로
- 인접 삼각형: 2-step 경로
- 3개 삼각형 체인: 올바른 순서
- 도달 불가: None
- start/goal이 navmesh 밖: None
### steering.rs
- seek: 목표 방향
- flee: 반대 방향
- arrive: 가까우면 감속, 멀면 max_speed
- follow_path: 웨이포인트 전진