You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity:如何在NavMesh上获取均匀分布的随机点?

解决NavMesh上生成均匀分布随机点的问题

问题根源分析

你遇到的分布异常,核心原因是初始随机点的范围未匹配NavMesh的实际覆盖区域,加上NavMesh.SamplePosition的maxDistance参数使用不当:

  • 当maxDistance过大时,大部分随机点落在NavMesh范围外,SamplePosition会返回离随机点最近的NavMesh边缘点,导致集中在边缘;
  • 当maxDistance过小且初始随机点范围未对齐NavMesh时,只有原点附近的点能被采样到,结果集中在原点。

解决方案:基于NavMesh bounds生成随机点

直接基于NavMesh的实际边界生成初始随机点,再通过SamplePosition验证并调整,确保点均匀分布在NavMesh上。以下是修改后的SpawnUtils静态方法:

using UnityEngine;
using UnityEngine.AI;

public static class SpawnUtils
{
    public static Vector3 GetRandomSpawnPosition(int maxSampleAttempts = 5)
    {
        // 获取场景中所有NavMesh的合并边界
        NavMeshTriangulation navMeshData = NavMesh.CalculateTriangulation();
        Bounds navMeshBounds = new Bounds(navMeshData.vertices[0], Vector3.zero);
        foreach (Vector3 vertex in navMeshData.vertices)
        {
            navMeshBounds.Encapsulate(vertex);
        }

        // 在NavMesh边界内生成随机初始点
        Vector3 randomPoint = new Vector3(
            Random.Range(navMeshBounds.min.x, navMeshBounds.max.x),
            navMeshBounds.center.y, // 固定Y轴为NavMesh的中心高度,避免高度偏差
            Random.Range(navMeshBounds.min.z, navMeshBounds.max.z)
        );

        // 尝试采样NavMesh上的有效点
        for (int i = 0; i < maxSampleAttempts; i++)
        {
            if (NavMesh.SamplePosition(randomPoint, out NavMeshHit hit, 5f, NavMesh.AllAreas))
            {
                // 确保采样点距离初始随机点足够近(避免跳转到边缘)
                if (Vector3.Distance(randomPoint, hit.position) < 10f)
                {
                    return hit.position;
                }
            }
            // 如果当前采样失败,重新生成随机点
            randomPoint = new Vector3(
                Random.Range(navMeshBounds.min.x, navMeshBounds.max.x),
                navMeshBounds.center.y,
                Random.Range(navMeshBounds.min.z, navMeshBounds.max.z)
            );
        }

        // 如果多次采样失败,返回NavMesh中心作为 fallback
        return navMeshBounds.center;
    }
}

关键调整说明

  • 基于NavMesh bounds生成随机点:不再依赖地板缩放,直接从NavMesh的顶点计算边界,确保初始随机点落在NavMesh覆盖区域内;
  • 限制采样距离:将maxDistance设为较小值(如5f),只接受离初始随机点近的采样结果,避免跳转到边缘;
  • 增加重试逻辑:采样失败时重新生成随机点,提升获取有效点的概率;
  • 固定Y轴高度:使用NavMesh的中心Y值,避免生成的点高度偏离地面。

额外优化建议

  • 如果你的场景中有多个NavMesh区域,可以指定NavMesh.SamplePosition的区域掩码(替换NavMesh.AllAreas),只在目标区域生成点;
  • 若需要更严格的均匀分布,可以基于NavMesh的三角面进行采样:随机选择三角面,再在三角面内生成随机点,这种方式能完全避免边缘集中问题,代码示例如下:
public static Vector3 GetRandomSpawnPositionOnTriangles(int maxSampleAttempts = 5)
{
    NavMeshTriangulation navMeshData = NavMesh.CalculateTriangulation();
    int[] indices = navMeshData.indices;

    for (int i = 0; i < maxSampleAttempts; i++)
    {
        // 随机选择一个三角面
        int triIndex = Random.Range(0, indices.Length / 3);
        int i1 = indices[triIndex * 3];
        int i2 = indices[triIndex * 3 + 1];
        int i3 = indices[triIndex * 3 + 2];

        Vector3 v1 = navMeshData.vertices[i1];
        Vector3 v2 = navMeshData.vertices[i2];
        Vector3 v3 = navMeshData.vertices[i3];

        // 在三角面内生成均匀随机点
        float u = Random.Range(0f, 1f);
        float v = Random.Range(0f, 1f);
        if (u + v > 1f)
        {
            u = 1f - u;
            v = 1f - v;
        }
        Vector3 randomPointInTri = v1 + u * (v2 - v1) + v * (v3 - v1);

        // 验证点是否在NavMesh上(可选,确保三角面数据有效)
        if (NavMesh.SamplePosition(randomPointInTri, out NavMeshHit hit, 1f, NavMesh.AllAreas))
        {
            return hit.position;
        }
    }

    return navMeshData.bounds.center;
}

内容的提问来源于stack exchange,提问作者Preston Bourne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 14:31:12