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

4.1 KiB

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

#[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

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

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

#[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에 더하기 전).

pub fn seek(agent: &SteeringAgent, target: Vec3) -> Vec3
  • desired = normalize(target - position) * max_speed
  • steering = desired - velocity
  • truncate to max_force
pub fn flee(agent: &SteeringAgent, threat: Vec3) -> Vec3
  • seek 반대 방향
pub fn arrive(agent: &SteeringAgent, target: Vec3, slow_radius: f32) -> Vec3
  • distance < slow_radius이면 desired speed = max_speed * (distance / slow_radius)
  • 목표에 매우 가까우면 (< 0.01) 제로
pub fn wander(agent: &SteeringAgent, wander_radius: f32, wander_distance: f32, angle: f32) -> Vec3
  • agent 전방 wander_distance에 원(wander_radius) 위의 점을 target으로 seek
  • angle은 호출자가 매 프레임 랜덤 변경
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: 웨이포인트 전진