references/navmesh-guide.md
# NavMesh Complete Reference Guide
> Source: Unity AI Navigation 2.0.11 Documentation
> https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/index.html
## NavMesh Architecture
The navigation system consists of:
1. **NavMesh** -- Baked data describing walkable surfaces
2. **NavMeshAgent** -- Component that moves characters along the NavMesh
3. **NavMeshObstacle** -- Dynamic obstacles that modify agent behavior
4. **NavMeshSurface** -- Component that builds and owns NavMesh data
5. **NavMeshModifier** -- Per-object overrides for NavMesh generation
6. **NavMeshModifierVolume** -- Volume-based overrides for NavMesh generation
7. **NavMeshLink** -- Connections between NavMesh surfaces
## NavMeshSurface
### Properties
| Property | Description | Default |
|----------|-------------|---------|
| Agent Type | Which agent configuration uses this surface | Humanoid |
| Default Area | Area classification for generated mesh | Walkable |
| Use Geometry | Input source: Render Meshes or Physics Colliders | Render Meshes |
| Generate Links | Auto-create connections during bake | false |
| Collect Objects | Scope: All, Volume, Current Hierarchy, NavMeshModifier only | All |
| Include Layers | Layer filter for GameObjects | Everything |
### Advanced Baking
| Parameter | Description | Default |
|-----------|-------------|---------|
| Override Voxel Size | Geometry precision | 3 voxels per agent radius |
| Override Tile Size | Tile grid dimensions | 256 voxels |
| Minimum Region Area | Removes small disconnected segments | 0 |
| Build Height Mesh | Elevation data for placement | false |
**Physics Colliders vs Render Meshes:** Physics Colliders permit agents to navigate closer to environmental edges since collision geometry is typically simpler.
**Tile Size Trade-offs:** Smaller tiles increase fragmentation but improve carving performance with many obstacles. Larger tiles reduce overhead but carving recalculates more geometry.
### Excluded Objects
NavMeshSurface automatically excludes GameObjects with NavMeshAgent or NavMeshObstacle components during baking. These are dynamic navigation users, not static geometry.
### Scripting API
```csharp
using Unity.AI.Navigation;
using UnityEngine;
public class NavMeshSurfaceController : MonoBehaviour
{
NavMeshSurface surface;
void Start()
{
surface = GetComponent<NavMeshSurface>();
}
// Full bake (expensive)
public void BakeNavMesh()
{
surface.BuildNavMesh();
}
// Incremental update (less expensive)
public void UpdateExistingNavMesh()
{
surface.UpdateNavMesh(surface.navMeshData);
}
// Remove NavMesh data
public void ClearNavMesh()
{
surface.RemoveData();
}
}
```
## NavMeshAgent
### Properties Reference
**Agent Configuration:**
| Property | Type | Description |
|----------|------|-------------|
| agentTypeID | int | Agent type identifier |
| baseOffset | float | Collision cylinder offset from transform pivot |
**Steering:**
| Property | Type | Description |
|----------|------|-------------|
| speed | float | Max movement velocity (units/sec) |
| angularSpeed | float | Max rotation velocity (deg/sec) |
| acceleration | float | Max acceleration (units/sec^2) |
| stoppingDistance | float | Distance threshold before halting |
| autoBraking | bool | Decelerate when approaching destination |
**Obstacle Avoidance:**
| Property | Type | Description |
|----------|------|-------------|
| radius | float | Collision detection radius |
| height | float | Overhead clearance |
| obstacleAvoidanceType | ObstacleAvoidanceType | Quality: None, Low, Medium, Good, High |
| avoidancePriority | int | Priority 0-99 (lower = higher priority) |
**Pathfinding:**
| Property | Type | Description |
|----------|------|-------------|
| autoTraverseOffMeshLink | bool | Automatic link crossing |
| autoRepath | bool | Retry path on partial completion |
| areaMask | int | Bitfield of allowed NavMesh areas |
### Key Methods
```csharp
NavMeshAgent agent = GetComponent<NavMeshAgent>();
// Navigation
agent.SetDestination(targetPosition); // Set target and calculate path
agent.destination = targetPosition; // Property alternative
agent.Warp(position); // Teleport to position
agent.Move(offset); // Move by offset (respects NavMesh)
agent.ResetPath(); // Clear current path
agent.CompleteOffMeshLink(); // Finish off-mesh link traversal
// State queries
bool pending = agent.pathPending; // Path calculation in progress
float dist = agent.remainingDistance; // Distance to destination
bool hasPath = agent.hasPath; // Currently has a valid path
bool onMesh = agent.isOnNavMesh; // Agent is on NavMesh
bool onLink = agent.isOnOffMeshLink; // Agent is on off-mesh link
NavMeshPathStatus status = agent.pathStatus; // PathComplete, PathPartial, PathInvalid
// Control
agent.isStopped = true; // Pause movement
agent.isStopped = false; // Resume movement
agent.velocity = Vector3.zero; // Stop immediately
agent.updatePosition = false; // Manual position control
agent.updateRotation = false; // Manual rotation control
```
### Path Status Types
| Status | Meaning |
|--------|---------|
| `PathComplete` | Full path to destination found |
| `PathPartial` | Destination unreachable; path goes to nearest point |
| `PathInvalid` | No valid path exists |
### Agent Priority System
Agents avoid others of higher priority (lower number) and ignore those of lower priority (higher number). Range: 0 (highest) to 99 (lowest).
### Manual Path Calculation
```csharp
// Calculate path without moving
NavMeshPath path = new NavMeshPath();
bool found = agent.CalculatePath(targetPosition, path);
if (path.status == NavMeshPathStatus.PathComplete)
{
// Inspect waypoints
Vector3[] corners = path.corners;
for (int i = 0; i < corners.Length; i++)
{
Debug.Log($"Waypoint {i}: {corners[i]}");
}
}
// Or use static method
NavMesh.CalculatePath(startPos, endPos, NavMesh.AllAreas, path);
```
## NavMeshObstacle
### Properties Reference
| Property | Type | Description |
|----------|------|-------------|
| shape | NavMeshObstacleShape | Box or Capsule |
| center | Vector3 | Position offset from transform |
| size | Vector3 | Box dimensions (Box shape only) |
| radius | float | Capsule radius (Capsule shape only) |
| height | float | Capsule height (Capsule shape only) |
| carving | bool | Enable NavMesh hole carving |
| carvingMoveThreshold | float | Distance triggering update |
| carvingTimeToStationary | float | Seconds before considered stationary |
| carveOnlyStationary | bool | Only carve when not moving |
### Carving vs Non-Carving
**Carved obstacles:** Dynamically modify NavMesh topology. Agents recalculate paths around carved holes. Best for: barrels, crates, closed doors, destructible walls.
**Non-carved obstacles:** Agents use local avoidance to steer around them. NavMesh topology unchanged. Best for: moving characters, projectiles, temporary barriers.
### Scripting
```csharp
using UnityEngine;
using UnityEngine.AI;
public class Door : MonoBehaviour
{
NavMeshObstacle obstacle;
void Start()
{
obstacle = GetComponent<NavMeshObstacle>();
obstacle.shape = NavMeshObstacleShape.Box;
obstacle.carving = true;
obstacle.carveOnlyStationary = true;
}
public void Open()
{
obstacle.enabled = false;
// Play open animation
}
public void Close()
{
obstacle.enabled = true;
// Play close animation
}
}
```
## NavMeshLink
### Properties Reference
| Property | Type | Description |
|----------|------|-------------|
| agentTypeID | int | Which agent type can traverse |
| startTransform | Transform | Start edge reference |
| endTransform | Transform | End edge reference |
| startPoint | Vector3 | Start position (local space) |
| endPoint | Vector3 | End position (local space) |
| width | float | Link span width |
| costModifier | float | Cost override (-1 = use area cost) |
| autoUpdate | bool | Update when transforms change |
| bidirectional | bool | Two-way traversal |
| area | int | Area type index |
| activated | bool | Link usability |
### Area Types
| Built-in Type | Description |
|---------------|-------------|
| Walkable | Default; permits crossing |
| Not Walkable | Blocks traversal |
| Jump | Auto-generated link default |
Plus 29 user-defined custom area types accessible via **Navigation > Areas** tab.
## NavMesh Static Queries
### SamplePosition
Find nearest point on NavMesh:
```csharp
NavMeshHit hit;
float maxDistance = 10f;
if (NavMesh.SamplePosition(worldPosition, out hit, maxDistance, NavMesh.AllAreas))
{
Vector3 nearestPoint = hit.position;
float distance = hit.distance;
int areaMask = hit.mask;
}
```
### Raycast
Cast ray along NavMesh surface:
```csharp
NavMeshHit hit;
if (NavMesh.Raycast(startPos, endPos, out hit, NavMesh.AllAreas))
{
// Hit an edge or boundary
Vector3 hitPosition = hit.position;
Vector3 hitNormal = hit.normal;
}
```
### CalculatePath
Compute path between two points:
```csharp
NavMeshPath path = new NavMeshPath();
if (NavMesh.CalculatePath(start, end, NavMesh.AllAreas, path))
{
// path.corners contains waypoints
// path.status indicates completeness
}
```
### GetAreaCost / SetAreaCost
```csharp
// Get cost for area index
float cost = NavMesh.GetAreaCost(areaIndex);
// Set custom cost (higher = more expensive to traverse)
NavMesh.SetAreaCost(3, 5.0f); // Area 3 costs 5x
// Get area index from name
int areaIndex = NavMesh.GetAreaFromName("Water");
```
## NavMeshModifier
Applied to GameObjects to override NavMesh baking behavior:
- **Override Area** -- Set a specific NavMesh area type for this object's geometry
- **Ignore From Build** -- Exclude this object from NavMesh generation entirely
- **Affected Agents** -- Specify which agent types are affected by this modifier
- **Apply To Children** -- Whether the modifier affects child GameObjects
## NavMeshModifierVolume
Defines a box volume that overrides area types for any geometry within it:
- **Size** -- Volume dimensions
- **Center** -- Volume center offset from transform
- **Area Type** -- Override area for all geometry within the volume
- **Affected Agents** -- Which agent types are affected
Useful for marking entire regions (e.g., marking a swamp area as high-cost without modifying individual objects).
## Navigation Areas and Costs
Areas define different terrain types with associated traversal costs:
| Area | Default Cost | Use Case |
|------|-------------|----------|
| Walkable | 1 | Normal ground |
| Not Walkable | N/A | Impassable |
| Jump | 2 | Off-mesh links |
| Custom (3-31) | User-defined | Water, mud, roads, etc. |
Higher costs make agents prefer alternative routes. Agents choose the lowest total cost path.
```csharp
// Example: Make agents avoid water
int waterArea = NavMesh.GetAreaFromName("Water");
NavMesh.SetAreaCost(waterArea, 10.0f);
// Agent area mask: exclude specific areas
agent.areaMask = NavMesh.AllAreas & ~(1 << waterArea);
```
## Common Patterns
### Patrol Between Points
```csharp
using UnityEngine;
using UnityEngine.AI;
public class Patrol : MonoBehaviour
{
public Transform[] points;
int destPoint = 0;
NavMeshAgent agent;
void Start()
{
agent = GetComponent<NavMeshAgent>();
agent.autoBraking = false;
GoToNextPoint();
}
void GoToNextPoint()
{
if (points.Length == 0) return;
agent.destination = points[destPoint].position;
destPoint = (destPoint + 1) % points.Length;
}
void Update()
{
if (!agent.pathPending && agent.remainingDistance < 0.5f)
GoToNextPoint();
}
}
```
### Flee From Target
```csharp
public void FleeFrom(Vector3 threatPosition, float fleeDistance)
{
Vector3 fleeDirection = (transform.position - threatPosition).normalized;
Vector3 fleeTarget = transform.position + fleeDirection * fleeDistance;
NavMeshHit hit;
if (NavMesh.SamplePosition(fleeTarget, out hit, fleeDistance, NavMesh.AllAreas))
{
agent.SetDestination(hit.position);
}
}
```
### Group Formation
```csharp
public void MoveInFormation(Vector3 leaderTarget, Vector3 offset)
{
Vector3 formationTarget = leaderTarget + offset;
NavMeshHit hit;
if (NavMesh.SamplePosition(formationTarget, out hit, 2f, NavMesh.AllAreas))
{
agent.SetDestination(hit.position);
}
}
```
## Additional Resources
- [NavMesh Agent Scripting API](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/NavMeshAgent.html)
- [NavMesh Surface](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/NavMeshSurface.html)
- [NavMesh Obstacle](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/NavMeshObstacle.html)
- [NavMesh Link](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/NavMeshLink.html)
- [NavMesh Building Components](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/NavMeshBuildingComponents.html)
references/sentis-ml.md
# Unity Sentis / Inference Engine Reference
> Source: Unity Sentis 2.1 Documentation
> https://docs.unity3d.com/Packages/com.unity.sentis@2.1/manual/index.html
> Note: Sentis has been renamed to "Inference Engine" (`com.unity.ai.inference`)
## Overview
Sentis is a neural network inference library for Unity that enables importing and executing trained ML models in real-time across all Unity runtime platforms using end-user device compute (GPU/CPU).
## Core Workflow
```
ONNX Model File --> ModelAsset --> ModelLoader.Load() --> Model
|
new Worker(model, backend)
|
worker.Schedule(inputTensor)
|
worker.PeekOutput() --> Tensor<T>
```
### Step-by-Step
1. **Import** -- Drag ONNX file into Unity Assets (becomes ModelAsset)
2. **Load** -- `ModelLoader.Load(modelAsset)` creates runtime Model
3. **Create Worker** -- `new Worker(model, BackendType.GPUCompute)`
4. **Prepare Input** -- Create `Tensor<T>` from textures or arrays
5. **Execute** -- `worker.Schedule(inputTensor)`
6. **Read Output** -- `worker.PeekOutput() as Tensor<float>`
7. **Dispose** -- Clean up Worker and Tensors in `OnDestroy()`
## Supported Model Format
- **ONNX** (Open Neural Network Exchange)
- Opset versions 7 through 15
- Models from: Hugging Face, PyTorch Hub, ONNX Model Zoo, Kaggle, Meta Research
- Most ONNX operators supported; unsupported operators cause Worker assertion failures
## Backend Types
| Backend | Execution | Performance | Requirements |
|---------|-----------|-------------|--------------|
| `BackendType.GPUCompute` | GPU compute shaders | Fastest on GPU | `SystemInfo.supportsComputeShaders` must be true |
| `BackendType.CPU` | CPU with Burst | Fastest on CPU | Burst package; slow on WebGL (compiles to WASM) |
| `BackendType.GPUPixel` | GPU pixel shaders | Slower than GPUCompute | Fallback when compute shaders unavailable |
**DirectML acceleration** is available when using GPUCompute with DirectX12 on supported Windows platforms.
### Choosing a Backend
```csharp
BackendType backend;
if (SystemInfo.supportsComputeShaders)
{
backend = BackendType.GPUCompute;
}
else
{
backend = BackendType.CPU;
}
var worker = new Worker(runtimeModel, backend);
```
## Complete API Reference
### ModelAsset and ModelLoader
```csharp
using Unity.Sentis;
// Load from Inspector reference
public ModelAsset modelAsset;
Model runtimeModel = ModelLoader.Load(modelAsset);
// Load from Resources folder
ModelAsset asset = Resources.Load("model-file") as ModelAsset;
Model model = ModelLoader.Load(asset);
```
### Worker (Inference Engine)
```csharp
// Create worker
Worker worker = new Worker(runtimeModel, BackendType.GPUCompute);
// Run inference
worker.Schedule(inputTensor);
// Get output (does not take ownership of tensor)
Tensor<float> output = worker.PeekOutput() as Tensor<float>;
// Get named output
Tensor<float> namedOutput = worker.PeekOutput("output_name") as Tensor<float>;
// Dispose when done
worker.Dispose();
```
### Tensor Creation
```csharp
using Unity.Sentis;
// From texture (image input)
Texture2D texture = Resources.Load("image") as Texture2D;
Tensor<float> imageTensor = TextureConverter.ToTensor(texture);
// From float array
float[] data = new float[] { 1.0f, 2.0f, 3.0f, 4.0f };
Tensor<float> floatTensor = new Tensor<float>(new TensorShape(1, 4), data);
// From int array
int[] intData = new int[] { 1, 2, 3, 4 };
Tensor<int> intTensor = new Tensor<int>(new TensorShape(4), intData);
// TensorShape defines dimensions
var shape = new TensorShape(1, 3, 224, 224); // batch, channels, height, width
```
### TextureConverter
```csharp
// Texture to Tensor
Tensor<float> tensor = TextureConverter.ToTensor(texture2D);
// Tensor to Texture (for output visualization)
// Process output tensor back to texture for display
```
### TensorShape
```csharp
// Define tensor dimensions
var shape1D = new TensorShape(10); // 1D: 10 elements
var shape2D = new TensorShape(3, 4); // 2D: 3x4
var shape4D = new TensorShape(1, 3, 224, 224); // Batch, Channels, H, W
```
## Complete Inference Examples
### Image Classification
```csharp
using UnityEngine;
using Unity.Sentis;
public class ImageClassifier : MonoBehaviour
{
public ModelAsset modelAsset;
public Texture2D inputImage;
Model runtimeModel;
Worker worker;
void Start()
{
runtimeModel = ModelLoader.Load(modelAsset);
worker = new Worker(runtimeModel, BackendType.GPUCompute);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Classify();
}
}
void Classify()
{
// Convert image to tensor
Tensor<float> inputTensor = TextureConverter.ToTensor(inputImage);
// Run inference
worker.Schedule(inputTensor);
// Get output probabilities
Tensor<float> outputTensor = worker.PeekOutput() as Tensor<float>;
// Find highest probability class
// Process outputTensor values...
// Clean up input
inputTensor.Dispose();
}
void OnDestroy()
{
worker?.Dispose();
}
}
```
### Simple Data Inference
```csharp
using UnityEngine;
using Unity.Sentis;
public class DataPredictor : MonoBehaviour
{
public ModelAsset modelAsset;
Model runtimeModel;
Worker worker;
void Start()
{
runtimeModel = ModelLoader.Load(modelAsset);
// Choose backend based on platform
BackendType backend = SystemInfo.supportsComputeShaders
? BackendType.GPUCompute
: BackendType.CPU;
worker = new Worker(runtimeModel, backend);
}
public float[] Predict(float[] inputData)
{
// Create input tensor
var inputTensor = new Tensor<float>(
new TensorShape(1, inputData.Length), inputData);
// Execute
worker.Schedule(inputTensor);
// Read output
Tensor<float> outputTensor = worker.PeekOutput() as Tensor<float>;
// Copy results to array
// Note: Access tensor data for processing
float[] results = new float[outputTensor.shape[1]];
// Process output tensor...
inputTensor.Dispose();
return results;
}
void OnDestroy()
{
worker?.Dispose();
}
}
```
## Performance Considerations
- **Model complexity** directly affects inference time
- **Backend selection** has significant impact; GPUCompute is fastest when available
- **Tensor allocation** can cause GC pressure; reuse tensors where possible
- **WebGL + CPU backend** is very slow due to Burst-to-WebAssembly compilation
- **Profile** using Unity Profiler to measure model execution time
- **Unsupported operators** cause runtime assertion failures; check operator compatibility
## Anti-Patterns
- **Not disposing Worker** -- Always call `worker.Dispose()` in `OnDestroy()` to prevent GPU/CPU resource leaks
- **Not disposing Tensors** -- Input tensors should be disposed after scheduling; output tensors from `PeekOutput()` are owned by the Worker
- **Running inference every frame** -- ML inference is expensive; schedule only when needed or use a coroutine with frame budgeting
- **Ignoring platform capabilities** -- Always check `SystemInfo.supportsComputeShaders` before using GPUCompute backend
- **Large models on mobile** -- Mobile GPU memory is limited; consider model quantization or smaller architectures
- **Blocking the main thread** -- For large models, consider splitting inference across frames
## Migration Note
Sentis has been renamed to **Inference Engine** with the package namespace changing to `com.unity.ai.inference`. Existing Sentis code should be migrated to the new package for future updates.
## Additional Resources
- [Sentis Manual](https://docs.unity3d.com/Packages/com.unity.sentis@2.1/manual/index.html)
- [Create a Worker](https://docs.unity3d.com/Packages/com.unity.sentis@2.1/manual/create-an-engine.html)
- [ONNX Model Zoo](https://github.com/onnx/models)
- [Hugging Face Models](https://huggingface.co/models)
SKILL.md
---
name: unity-ai-navigation
description: >
Unity 6 AI and navigation guide. Use when working with NavMesh, pathfinding, NavMeshAgent, NavMeshSurface, NavMeshObstacle, off-mesh links, or Unity Sentis (ML model inference). Covers AI navigation package, runtime NavMesh baking, and common AI patterns like state machines and behavior trees. Based on Unity 6.3 LTS documentation.
---
# Unity 6 AI and Navigation Guide
> Source: Unity 6.3 LTS Documentation (6000.3)
## AI Navigation Overview
**Package:** `com.unity.ai.navigation` (v2.0.11 for Unity 6000.3)
The AI Navigation package is a high-level component system that enables NavMesh-based navigation and pathfinding. It supports runtime and edit-time NavMesh construction, dynamic obstacle management, and link systems for specialized actions (jumping, doors).
### Core Components
| Component | Purpose |
|-----------|---------|
| NavMeshSurface | Defines and builds NavMesh for a specific agent type |
| NavMeshAgent | Character pathfinding and movement |
| NavMeshObstacle | Dynamic obstacle avoidance |
| NavMeshModifier | Affects NavMesh generation based on transform hierarchy |
| NavMeshModifierVolume | Affects NavMesh generation based on volume |
| NavMeshLink | Connects same or different NavMesh surfaces |
## NavMesh Setup
### Baking a NavMesh
1. Add a **NavMeshSurface** component to a GameObject
2. Configure the **Agent Type** (determines which agents can use this surface)
3. Set **Use Geometry** to Render Meshes or Physics Colliders
4. Configure **Collect Objects** mode (All, Volume, Current Hierarchy, NavMeshModifier only)
5. Click **Bake** or call `BuildNavMesh()` at runtime
### NavMeshSurface Properties
| Property | Description |
|----------|-------------|
| Agent Type | Which NavMesh Agent configuration can use this surface |
| Default Area | Walkable (default), Not Walkable, Jump, plus 29 custom types |
| Use Geometry | Render Meshes or Physics Colliders (colliders allow closer edge navigation) |
| Generate Links | Auto-creates connections between collected GameObjects during bake |
| Collect Objects | All GameObjects, Volume, Current Hierarchy, NavMeshModifier only |
| Include Layers | Filters GameObjects by layer (default: Everything) |
### Advanced Baking Parameters
| Parameter | Description |
|-----------|-------------|
| Override Voxel Size | Precision (default: 3 voxels per agent radius) |
| Override Tile Size | Tile dimensions (default: 256 voxels); smaller = better carving |
| Minimum Region Area | Removes disconnected mesh segments below threshold |
| Build Height Mesh | Generates elevation data for character placement |
The system excludes GameObjects with NavMeshAgent or NavMeshObstacle during baking.
### Runtime NavMesh Baking
```csharp
using UnityEngine;
using Unity.AI.Navigation;
public class RuntimeNavMeshBaker : MonoBehaviour
{
NavMeshSurface surface;
void Start()
{
surface = GetComponent<NavMeshSurface>();
surface.BuildNavMesh();
}
public void RebakeNavMesh()
{
surface.UpdateNavMesh(surface.navMeshData);
}
}
```
## NavMeshAgent
The NavMeshAgent component handles both pathfinding and movement control.
Add via: **Add Component > Navigation > NavMesh Agent**
### Basic Movement
```csharp
using UnityEngine;
using UnityEngine.AI;
public class MoveTo : MonoBehaviour
{
public Transform goal;
void Start()
{
NavMeshAgent agent = GetComponent<NavMeshAgent>();
agent.destination = goal.position;
}
}
```
### Agent Properties
**Steering:** Speed, Angular Speed, Acceleration, Stopping Distance, Auto Braking
**Obstacle Avoidance:** Radius, Height, Quality (None to High), Priority (0-99; lower = higher)
Agents avoid others of higher priority and ignore those of lower priority.
**Pathfinding:** Auto Traverse OffMesh Link, Auto Repath, Area Mask
### Agent Scripting Patterns
```csharp
using UnityEngine;
using UnityEngine.AI;
public class AIController : MonoBehaviour
{
NavMeshAgent agent;
void Start() { agent = GetComponent<NavMeshAgent>(); }
public void MoveToTarget(Vector3 target) { agent.SetDestination(target); }
bool HasReachedDestination()
{
if (!agent.pathPending
&& agent.remainingDistance <= agent.stoppingDistance
&& (!agent.hasPath || agent.velocity.sqrMagnitude == 0f))
return true;
return false;
}
public void StopMoving() { agent.isStopped = true; }
public void ResumeMoving() { agent.isStopped = false; }
public void WarpTo(Vector3 position) { agent.Warp(position); }
}
```
### Partial Paths
When a destination is unreachable, the agent generates a partial path to the nearest reachable location:
```csharp
if (agent.pathStatus == NavMeshPathStatus.PathPartial)
Debug.Log("Destination unreachable, using partial path");
else if (agent.pathStatus == NavMeshPathStatus.PathInvalid)
Debug.Log("No valid path found");
```
## NavMeshObstacle
Defines dynamic obstacles that agents avoid. Add via: **Add Component > Navigation > NavMesh Obstacle**
**Shapes:** Box (Center + Size) or Capsule (Center + Radius + Height)
### Carving
| Property | Description |
|----------|-------------|
| Move Threshold | Distance triggering update for moving obstacles |
| Time To Stationary | Seconds before classified as stationary |
| Carve Only Stationary | Only carve when not moving |
- **Carved:** Dynamically modify NavMesh topology (barrels, crates, doors)
- **Non-carved:** Exclusion zones without mesh modification (moving characters)
```csharp
using UnityEngine;
using UnityEngine.AI;
public class DynamicObstacle : MonoBehaviour
{
NavMeshObstacle obstacle;
void Start()
{
obstacle = GetComponent<NavMeshObstacle>();
obstacle.carving = true;
obstacle.carveOnlyStationary = true;
}
public void SetBlocking(bool blocking) { obstacle.enabled = blocking; }
}
```
## Off-Mesh Links (NavMeshLink)
Connects separate NavMesh surfaces. Use for doors, jump points, ledges, ladders.
Add via: **GameObject > AI > NavMesh Link** or **Add Component > Navigation > NavMesh Link**
| Property | Description |
|----------|-------------|
| Agent Type | Which agent type can traverse |
| Start/End Transform | GameObjects at link edges |
| Width | Link span width |
| Bidirectional | Two-way traversal |
| Area Type | Walkable, Not Walkable, or Jump |
| Activated | Controls link usability |
### Custom Link Traversal
```csharp
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
public class CustomLinkTraversal : MonoBehaviour
{
NavMeshAgent agent;
void Start()
{
agent = GetComponent<NavMeshAgent>();
agent.autoTraverseOffMeshLink = false;
}
void Update()
{
if (agent.isOnOffMeshLink) StartCoroutine(TraverseLink());
}
IEnumerator TraverseLink()
{
OffMeshLinkData linkData = agent.currentOffMeshLinkData;
Vector3 startPos = agent.transform.position;
Vector3 endPos = linkData.endPos + Vector3.up * agent.baseOffset;
float elapsed = 0f, duration = 0.5f;
while (elapsed < duration)
{
float t = elapsed / duration;
agent.transform.position = Vector3.Lerp(startPos, endPos, t)
+ Vector3.up * Mathf.Sin(t * Mathf.PI) * 2f;
elapsed += Time.deltaTime;
yield return null;
}
agent.CompleteOffMeshLink();
}
}
```
## Unity Sentis Overview
**Package:** `com.unity.sentis` (v2.1) -- now renamed **Inference Engine** (`com.unity.ai.inference`)
Neural network inference library for running ONNX models (opset 7-15) on GPU/CPU across all Unity platforms.
### Core Workflow
```csharp
using UnityEngine;
using Unity.Sentis;
public class MLInference : MonoBehaviour
{
public ModelAsset modelAsset;
Model runtimeModel;
Worker worker;
void Start()
{
runtimeModel = ModelLoader.Load(modelAsset);
worker = new Worker(runtimeModel, BackendType.GPUCompute);
}
void RunInference()
{
Tensor<float> input = TextureConverter.ToTensor(
Resources.Load("image") as Texture2D);
worker.Schedule(input);
Tensor<float> output = worker.PeekOutput() as Tensor<float>;
input.Dispose();
}
void OnDestroy() { worker?.Dispose(); }
}
```
### Backend Types
| Backend | Performance | Notes |
|---------|-------------|-------|
| GPUCompute | Fastest (GPU) | Check `SystemInfo.supportsComputeShaders` |
| CPU | Fastest (CPU) | Slow on WebGL (Burst to WASM) |
| GPUPixel | Slower | Fallback without compute shaders |
See `skills/unity-ai-navigation/references/sentis-ml.md` for full API details.
## Common AI Patterns
### Simple State Machine
```csharp
using UnityEngine;
using UnityEngine.AI;
public enum AIState { Idle, Patrol, Chase, Attack }
public class AIStateMachine : MonoBehaviour
{
public AIState currentState = AIState.Idle;
public Transform[] patrolPoints;
public float chaseRange = 10f, attackRange = 2f;
NavMeshAgent agent;
Transform player;
int patrolIndex;
void Start()
{
agent = GetComponent<NavMeshAgent>();
player = GameObject.FindWithTag("Player").transform;
}
void Update()
{
float dist = Vector3.Distance(transform.position, player.position);
switch (currentState)
{
case AIState.Idle:
if (dist < chaseRange) currentState = AIState.Chase;
else if (patrolPoints.Length > 0) currentState = AIState.Patrol;
break;
case AIState.Patrol:
agent.SetDestination(patrolPoints[patrolIndex].position);
if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance)
patrolIndex = (patrolIndex + 1) % patrolPoints.Length;
if (dist < chaseRange) currentState = AIState.Chase;
break;
case AIState.Chase:
agent.SetDestination(player.position);
if (dist < attackRange) currentState = AIState.Attack;
else if (dist > chaseRange * 1.5f) currentState = AIState.Patrol;
break;
case AIState.Attack:
agent.isStopped = true;
if (dist > attackRange) { agent.isStopped = false; currentState = AIState.Chase; }
break;
}
}
}
```
### Behavior Tree Nodes
```csharp
public enum NodeState { Running, Success, Failure }
public abstract class BTNode { public abstract NodeState Evaluate(); }
public class Selector : BTNode
{
BTNode[] children;
public Selector(params BTNode[] children) { this.children = children; }
public override NodeState Evaluate()
{
foreach (var child in children)
{
var result = child.Evaluate();
if (result != NodeState.Failure) return result;
}
return NodeState.Failure;
}
}
public class Sequence : BTNode
{
BTNode[] children;
public Sequence(params BTNode[] children) { this.children = children; }
public override NodeState Evaluate()
{
foreach (var child in children)
{
var result = child.Evaluate();
if (result != NodeState.Success) return result;
}
return NodeState.Success;
}
}
```
### NavMesh Queries
```csharp
using UnityEngine;
using UnityEngine.AI;
public class NavMeshQueries : MonoBehaviour
{
public Vector3 GetNearestNavMeshPoint(Vector3 pos, float maxDist)
{
NavMeshHit hit;
return NavMesh.SamplePosition(pos, out hit, maxDist, NavMesh.AllAreas)
? hit.position : pos;
}
public bool IsOnNavMesh(Vector3 pos)
{
NavMeshHit hit;
return NavMesh.SamplePosition(pos, out hit, 0.1f, NavMesh.AllAreas);
}
public bool CanReachTarget(Vector3 start, Vector3 end)
{
NavMeshPath path = new NavMeshPath();
NavMesh.CalculatePath(start, end, NavMesh.AllAreas, path);
return path.status == NavMeshPathStatus.PathComplete;
}
public Vector3 GetRandomNavMeshPoint(Vector3 center, float range)
{
Vector3 dir = Random.insideUnitSphere * range + center;
NavMeshHit hit;
return NavMesh.SamplePosition(dir, out hit, range, NavMesh.AllAreas)
? hit.position : center;
}
}
```
## Anti-Patterns
- **Baking NavMesh every frame** -- `BuildNavMesh()` is expensive. Only call when geometry changes. Use `UpdateNavMesh()` for incremental updates.
- **Not checking pathStatus** -- Always check `agent.pathStatus`. Partial or invalid paths cause agents to get stuck silently.
- **Setting destination in Update without guard** -- Recalculating paths every frame wastes CPU. Only update when target moves significantly.
- **NavMeshObstacle on agents** -- Do not add NavMeshObstacle to GameObjects that also have NavMeshAgent. Agents already avoid each other.
- **Forgetting area masks** -- Agents without proper Area Mask may walk through restricted zones.
- **Carving everything** -- Carving is expensive. Use non-carving obstacles for things agents can path around naturally.
- **Missing NavMeshSurface** -- Without a NavMeshSurface, there is no NavMesh. Agents will not move.
- **Not disposing Sentis workers** -- Always call `worker.Dispose()` in `OnDestroy()`.
- **Using CPU backend on WebGL** -- Burst compiles to WASM, resulting in very slow ML inference.
## Key API Quick Reference
| Class | Namespace | Purpose |
|-------|-----------|---------|
| `NavMeshAgent` | UnityEngine.AI | Pathfinding and movement |
| `NavMeshObstacle` | UnityEngine.AI | Dynamic obstacle |
| `NavMesh` | UnityEngine.AI | Static queries and sampling |
| `NavMeshPath` | UnityEngine.AI | Calculated path data |
| `NavMeshHit` | UnityEngine.AI | Raycast/sample result |
| `NavMeshSurface` | Unity.AI.Navigation | NavMesh baking |
| `NavMeshModifier` | Unity.AI.Navigation | Per-object overrides |
| `NavMeshModifierVolume` | Unity.AI.Navigation | Volume-based overrides |
| `NavMeshLink` | Unity.AI.Navigation | Surface connections |
| `ModelAsset` | Unity.Sentis | ONNX model reference |
| `ModelLoader` | Unity.Sentis | Runtime model loading |
| `Worker` | Unity.Sentis | Inference engine |
| `BackendType` | Unity.Sentis | GPUCompute, CPU, GPUPixel |
| `Tensor<T>` | Unity.Sentis | Input/output data |
## Related Skills
- `unity-foundations` -- GameObject, components, scene hierarchy
- `unity-scripting` -- C# scripting patterns, MonoBehaviour lifecycle
- `unity-physics` -- Colliders, raycasting, physics integration with NavMesh
## Additional Resources
- [AI Navigation Package](https://docs.unity3d.com/6000.3/Documentation/Manual/com.unity.ai.navigation.html)
- [AI Navigation 2.0 Manual](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/index.html)
- [NavMeshAgent](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/NavMeshAgent.html)
- [NavMeshSurface](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/NavMeshSurface.html)
- [NavMeshObstacle](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/NavMeshObstacle.html)
- [NavMeshLink](https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/NavMeshLink.html)
- [Unity Sentis](https://docs.unity3d.com/Packages/com.unity.sentis@2.1/manual/index.html)