Use cone agents and build runtime NavMesh

This commit is contained in:
JSD\13999
2026-06-13 14:52:42 +08:00
parent 5849bd6dd9
commit 47142e749d
2 changed files with 135 additions and 8 deletions

View File

@@ -4,6 +4,12 @@ namespace FishROV.AvoidanceBenchmark
{ {
public sealed class BenchmarkAgent : MonoBehaviour public sealed class BenchmarkAgent : MonoBehaviour
{ {
private const int ConeSegments = 16;
private static Mesh sharedConeMesh;
private static Material sharedConeMaterial;
private static MaterialPropertyBlock colorPropertyBlock;
private Vector3 velocity; private Vector3 velocity;
private float maxSpeed = 1f; private float maxSpeed = 1f;
@@ -16,18 +22,19 @@ namespace FishROV.AvoidanceBenchmark
public static BenchmarkAgent CreatePrimitive(BenchmarkAgentSpawnInfo spawnInfo, Transform parent) public static BenchmarkAgent CreatePrimitive(BenchmarkAgentSpawnInfo spawnInfo, Transform parent)
{ {
var primitive = spawnInfo.Role == BenchmarkAgentRole.Shark var go = spawnInfo.Role == BenchmarkAgentRole.Shark
? PrimitiveType.Capsule ? GameObject.CreatePrimitive(PrimitiveType.Capsule)
: PrimitiveType.Cube; : CreateConeObject();
var go = GameObject.CreatePrimitive(primitive);
go.name = $"{spawnInfo.Role}_{spawnInfo.GroupId:0000}"; go.name = $"{spawnInfo.Role}_{spawnInfo.GroupId:0000}";
go.transform.SetParent(parent, false); go.transform.SetParent(parent, false);
go.transform.position = spawnInfo.Position; go.transform.position = spawnInfo.Position;
var scale = spawnInfo.Role == BenchmarkAgentRole.Shark var scale = spawnInfo.Role == BenchmarkAgentRole.Shark
? new Vector3(spawnInfo.Radius, spawnInfo.Height, spawnInfo.Radius) ? new Vector3(spawnInfo.Radius, spawnInfo.Height, spawnInfo.Radius)
: Vector3.one * Mathf.Max(0.1f, spawnInfo.Radius * 2f); : new Vector3(
Mathf.Max(0.1f, spawnInfo.Radius * 1.4f),
Mathf.Max(0.1f, spawnInfo.Radius * 1.4f),
Mathf.Max(0.2f, spawnInfo.Radius * 3.2f));
go.transform.localScale = scale; go.transform.localScale = scale;
var collider = go.GetComponent<Collider>(); var collider = go.GetComponent<Collider>();
@@ -105,7 +112,84 @@ namespace FishROV.AvoidanceBenchmark
break; break;
} }
renderer.material.color = color; var propertyBlock = GetColorPropertyBlock();
propertyBlock.SetColor("_Color", color);
renderer.SetPropertyBlock(propertyBlock);
}
private static GameObject CreateConeObject()
{
var go = new GameObject("ConeAgent");
var meshFilter = go.AddComponent<MeshFilter>();
meshFilter.sharedMesh = GetSharedConeMesh();
var meshRenderer = go.AddComponent<MeshRenderer>();
meshRenderer.sharedMaterial = GetSharedConeMaterial();
return go;
}
private static Mesh GetSharedConeMesh()
{
if (sharedConeMesh != null)
{
return sharedConeMesh;
}
var vertices = new Vector3[ConeSegments + 2];
var triangles = new int[ConeSegments * 6];
vertices[0] = new Vector3(0f, 0f, 0.5f);
vertices[1] = new Vector3(0f, 0f, -0.5f);
for (var i = 0; i < ConeSegments; i++)
{
var angle = i * Mathf.PI * 2f / ConeSegments;
vertices[i + 2] = new Vector3(Mathf.Cos(angle) * 0.5f, Mathf.Sin(angle) * 0.5f, -0.5f);
}
var triangleIndex = 0;
for (var i = 0; i < ConeSegments; i++)
{
var current = i + 2;
var next = i == ConeSegments - 1 ? 2 : current + 1;
triangles[triangleIndex++] = 0;
triangles[triangleIndex++] = current;
triangles[triangleIndex++] = next;
triangles[triangleIndex++] = 1;
triangles[triangleIndex++] = next;
triangles[triangleIndex++] = current;
}
sharedConeMesh = new Mesh
{
name = "Benchmark Agent Direction Cone",
vertices = vertices,
triangles = triangles
};
sharedConeMesh.RecalculateNormals();
sharedConeMesh.RecalculateBounds();
return sharedConeMesh;
}
private static Material GetSharedConeMaterial()
{
if (sharedConeMaterial != null)
{
return sharedConeMaterial;
}
sharedConeMaterial = new Material(Shader.Find("Standard"))
{
name = "Benchmark Agent Shared Material"
};
return sharedConeMaterial;
}
private static MaterialPropertyBlock GetColorPropertyBlock()
{
return colorPropertyBlock ??= new MaterialPropertyBlock();
} }
} }
} }

View File

@@ -8,6 +8,8 @@ namespace FishROV.AvoidanceBenchmark
{ {
private readonly Dictionary<BenchmarkAgent, NavMeshAgent> navAgents = new Dictionary<BenchmarkAgent, NavMeshAgent>(512); private readonly Dictionary<BenchmarkAgent, NavMeshAgent> navAgents = new Dictionary<BenchmarkAgent, NavMeshAgent>(512);
private AvoidanceBenchmarkConfig config; private AvoidanceBenchmarkConfig config;
private NavMeshData runtimeNavMeshData;
private NavMeshDataInstance runtimeNavMeshInstance;
private bool hasNavMeshData; private bool hasNavMeshData;
private int fallbackAgentCount; private int fallbackAgentCount;
@@ -20,7 +22,7 @@ namespace FishROV.AvoidanceBenchmark
{ {
if (!hasNavMeshData) if (!hasNavMeshData)
{ {
return "未检测到运行时 NavMesh 数据;当前退回基线移动。请先烘焙 NavMesh或启用 com.unity.ai.navigation 并构建运行时 NavMeshSurface。"; return "NavMesh 数据构建失败;当前退回基线移动。";
} }
if (fallbackAgentCount > 0) if (fallbackAgentCount > 0)
@@ -37,6 +39,7 @@ namespace FishROV.AvoidanceBenchmark
this.config = config.Clone(); this.config = config.Clone();
navAgents.Clear(); navAgents.Clear();
fallbackAgentCount = 0; fallbackAgentCount = 0;
BuildRuntimeNavMesh();
var triangulation = NavMesh.CalculateTriangulation(); var triangulation = NavMesh.CalculateTriangulation();
hasNavMeshData = triangulation.vertices != null && triangulation.vertices.Length > 0; hasNavMeshData = triangulation.vertices != null && triangulation.vertices.Length > 0;
@@ -118,6 +121,46 @@ namespace FishROV.AvoidanceBenchmark
navAgents.Clear(); navAgents.Clear();
fallbackAgentCount = 0; fallbackAgentCount = 0;
if (runtimeNavMeshInstance.valid)
{
runtimeNavMeshInstance.Remove();
}
runtimeNavMeshData = null;
}
private void BuildRuntimeNavMesh()
{
if (runtimeNavMeshInstance.valid)
{
runtimeNavMeshInstance.Remove();
}
var buildSettings = NavMesh.GetSettingsByID(0);
buildSettings.agentRadius = Mathf.Max(0.05f, config.FishRadius);
buildSettings.agentHeight = Mathf.Max(0.2f, config.FishRadius * 2f);
buildSettings.agentClimb = 0.4f;
buildSettings.agentSlope = 45f;
var arenaSize = Mathf.Max(8f, config.ArenaRadius * 2.4f);
var sources = new List<NavMeshBuildSource>
{
new NavMeshBuildSource
{
shape = NavMeshBuildSourceShape.Box,
transform = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one),
size = new Vector3(arenaSize, 0.1f, arenaSize),
area = 0
}
};
var bounds = new Bounds(Vector3.zero, new Vector3(arenaSize, 2f, arenaSize));
runtimeNavMeshData = NavMeshBuilder.BuildNavMeshData(buildSettings, sources, bounds, Vector3.zero, Quaternion.identity);
if (runtimeNavMeshData != null)
{
runtimeNavMeshInstance = NavMesh.AddNavMeshData(runtimeNavMeshData);
}
} }
private bool TryPlaceOnNavMesh(BenchmarkAgent benchmarkAgent, BenchmarkAgentSpawnInfo spawnInfo) private bool TryPlaceOnNavMesh(BenchmarkAgent benchmarkAgent, BenchmarkAgentSpawnInfo spawnInfo)