mcpField
U
unity-ai-navigation

unity-ai-navigation

github.com/nice-wolf-studio开发4060 次安装6 周前更新
代码仓库

关于 unity-ai-navigation

为 Unity 开发者整理 Unity 6 AI 导航与推理文档的技能指南,涵盖 NavMesh 配置、寻路、动态障碍和 ONNX 模型推理。

主要功能

  1. 01NavMesh 构建与烘焙
  2. 02Agent 寻路与移动
  3. 03动态障碍管理
  4. 04Off-Mesh Link 配置
  5. 05Sentis ONNX 推理

适用场景

  1. 01配置运行时或编辑时 NavMesh
  2. 02实现角色寻路与路径状态检查
  3. 03处理动态障碍及跨区域移动
  4. 04在 Unity 中运行 ONNX 模型

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nice-wolf-studio/unity-claude-skills --skill unity-ai-navigation

安装后在 Claude Code 中输入/unity-ai-navigation确认已加载。

附带文件references/navmesh-guide.mdreferences/sentis-ml.md

详细介绍

Unity 6 AI 和导航指南

来源:Unity 6.3 LTS 文档(6000.3)

AI 导航概述

软件包: com.unity.ai.navigation(适用于 Unity 6000.3 的 v2.0.11)

AI Navigation 软件包是一套高级组件系统,可实现基于 NavMesh 的导航和寻路。它支持运行时和编辑时构建 NavMesh、动态障碍物管理,以及用于特殊动作(跳跃、开门)的连接系统。

核心组件
组件用途
NavMeshSurface为特定 Agent 类型定义并构建 NavMesh
NavMeshAgent角色寻路和移动
NavMeshObstacle动态障碍物规避
NavMeshModifier根据 Transform 层级影响 NavMesh 生成
NavMeshModifierVolume根据体积影响 NavMesh 生成
NavMeshLink连接相同或不同的 NavMesh 表面

NavMesh 设置

烘焙 NavMesh
  1. 向 GameObject 添加 NavMeshSurface 组件
  2. 配置 Agent Type(决定哪些 Agent 可以使用此表面)
  3. 将 Use Geometry 设置为 Render Meshes 或 Physics Colliders
  4. 配置 Collect Objects 模式(All、Volume、Current Hierarchy、仅 NavMeshModifier)
  5. 点击 Bake,或在运行时调用 BuildNavMesh()
NavMeshSurface 属性
属性描述
Agent Type哪种 NavMesh Agent 配置可以使用此表面
Default AreaWalkable(默认)、Not Walkable、Jump,以及 29 种自定义类型
Use GeometryRender Meshes 或 Physics Colliders(使用碰撞体可让导航更靠近边缘)
Generate Links烘焙时自动创建已收集 GameObject 之间的连接
Collect Objects所有 GameObject、Volume、Current Hierarchy、仅 NavMeshModifier
Include Layers按图层筛选 GameObject(默认:Everything)
高级烘焙参数
参数描述
Override Voxel Size精度(默认:每个 Agent 半径 3 个体素)
Override Tile Size瓦片尺寸(默认:256 个体素);尺寸越小,挖空效果越好
Minimum Region Area移除小于阈值的断开网格片段
Build Height Mesh生成用于角色定位的高度数据

烘焙时,系统会排除带有 NavMeshAgent 或 NavMeshObstacle 的 GameObject。

运行时烘焙 NavMesh
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

NavMeshAgent 组件负责寻路和移动控制。

通过以下路径添加:Add Component > Navigation > NavMesh Agent

基本移动
using UnityEngine;
using UnityEngine.AI;

public class MoveTo : MonoBehaviour
{
    public Transform goal;

    void Start()
    {
        NavMeshAgent agent = GetComponent<NavMeshAgent>();
        agent.destination = goal.position;
    }
}
Agent 属性

转向: Speed、Angular Speed、Acceleration、Stopping Distance、Auto Braking

障碍物规避: Radius、Height、Quality(从 None 到 High)、Priority(0-99;数值越低,优先级越高)

Agent 会避开优先级更高的 Agent,并忽略优先级更低的 Agent。

寻路: Auto Traverse OffMesh Link、Auto Repath、Area Mask

Agent 脚本模式
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); }
}
部分路径

如果无法到达目标位置,Agent 会生成一条通往最近可到达位置的部分路径:

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

定义 Agent 会避开的动态障碍物。通过以下路径添加:Add Component > Navigation > NavMesh Obstacle

形状: Box(Center + Size)或 Capsule(Center + Radius + Height)

挖空
属性描述
Move Threshold触发移动障碍物更新的距离
Time To Stationary被判定为静止前等待的秒数
Carve Only Stationary仅在停止移动时挖空
  • 挖空: 动态修改 NavMesh 拓扑结构(木桶、箱子、门)
  • 不挖空: 不修改网格的排除区域(移动中的角色)
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; }
}

离网连接(NavMeshLink)

连接分离的 NavMesh 表面。可用于门、跳跃点、边缘和梯子。

通过以下方式添加:GameObject > AI > NavMesh Link 或 Add Component > Navigation > NavMesh Link

属性描述
Agent Type可通过连接的 Agent 类型
Start/End Transform位于连接边缘的 GameObject
Width连接跨度宽度
Bidirectional双向通行
Area Type可行走、不可行走或跳跃
Activated控制连接是否可用
自定义连接遍历
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 概览

Package: com.unity.sentis(v2.1)——现已更名为 Inference Engine(com.unity.ai.inference)

用于运行 ONNX 模型(opset 7-15)的神经网络推理库,可在所有 Unity 平台上使用 GPU/CPU。

核心工作流
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(); }
}
后端类型
后端性能说明
GPUCompute最快(GPU)检查 SystemInfo.supportsComputeShaders
CPU最快(CPU)在 WebGL 上较慢(Burst 编译为 WASM)
GPUPixel较慢无计算着色器时的回退方案

完整 API 详情请参阅 skills/unity-ai-navigation/references/sentis-ml.md。

常见 AI 模式

简单状态机
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;
        }
    }
}
行为树节点
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 查询
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;
    }
}

反模式

  • 每帧烘焙 NavMesh —— BuildNavMesh() 开销很大。仅在几何体发生变化时调用。增量更新请使用 UpdateNavMesh()。
  • 未检查 pathStatus —— 始终检查 agent.pathStatus。部分路径或无效路径会导致 Agent 悄无声息地卡住。
  • 未加保护就在 Update 中设置目标位置 —— 每帧重新计算路径会浪费 CPU。仅在目标发生明显移动时更新。
  • 在 Agent 上添加 NavMeshObstacle —— 不要给同时带有 NavMeshAgent 的 GameObject 添加 NavMeshObstacle。Agent 已经会相互避让。
  • 忘记区域遮罩 —— 未设置适当 Area Mask 的 Agent 可能会穿过受限区域。
  • 对所有对象都使用雕刻 —— 雕刻开销很大。对于 Agent 可以自然绕行的物体,请使用不进行雕刻的障碍物。
  • 缺少 NavMeshSurface —— 没有 NavMeshSurface,就没有 NavMesh。Agent 将无法移动。
  • 未释放 Sentis worker —— 始终在 OnDestroy() 中调用 worker.Dispose()。
  • 在 WebGL 上使用 CPU 后端 —— Burst 会编译为 WASM,导致 ML 推理速度非常慢。

关键 API 速查

类命名空间用途
NavMeshAgentUnityEngine.AI寻路和移动
NavMeshObstacleUnityEngine.AI动态障碍物
NavMeshUnityEngine.AI静态查询和采样
NavMeshPathUnityEngine.AI计算出的路径数据
NavMeshHitUnityEngine.AI射线检测/采样结果
NavMeshSurfaceUnity.AI.NavigationNavMesh 烘焙
NavMeshModifierUnity.AI.Navigation单对象覆盖设置
NavMeshModifierVolumeUnity.AI.Navigation基于体积的覆盖设置
NavMeshLinkUnity.AI.Navigation表面连接
ModelAssetUnity.SentisONNX 模型引用
ModelLoaderUnity.Sentis运行时模型加载
WorkerUnity.Sentis推理引擎
BackendTypeUnity.SentisGPUCompute、CPU、GPUPixel
Tensor<T>Unity.Sentis输入/输出数据

相关技能

  • unity-foundations —— GameObject、组件、场景层级
  • unity-scripting —— C# 脚本模式、MonoBehaviour 生命周期
  • unity-physics —— 碰撞体、射线检测、与 NavMesh 的物理集成

Additional Resources

评论

支持 Markdown