From 47142e749d3d6617d811a1167ab5fe6f5ea56b4c Mon Sep 17 00:00:00 2001 From: "JSD\\13999" <1399945104@qq.com> Date: Sat, 13 Jun 2026 14:52:42 +0800 Subject: [PATCH] Use cone agents and build runtime NavMesh --- .../Benchmark/Agents/BenchmarkAgent.cs | 98 +++++++++++++++++-- .../FrameworkAdapters/UnityNavMeshAdapter.cs | 45 ++++++++- 2 files changed, 135 insertions(+), 8 deletions(-) diff --git a/FishROV/Assets/Scripts/Benchmark/Agents/BenchmarkAgent.cs b/FishROV/Assets/Scripts/Benchmark/Agents/BenchmarkAgent.cs index 7841a53..5059fe5 100644 --- a/FishROV/Assets/Scripts/Benchmark/Agents/BenchmarkAgent.cs +++ b/FishROV/Assets/Scripts/Benchmark/Agents/BenchmarkAgent.cs @@ -4,6 +4,12 @@ namespace FishROV.AvoidanceBenchmark { 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 float maxSpeed = 1f; @@ -16,18 +22,19 @@ namespace FishROV.AvoidanceBenchmark public static BenchmarkAgent CreatePrimitive(BenchmarkAgentSpawnInfo spawnInfo, Transform parent) { - var primitive = spawnInfo.Role == BenchmarkAgentRole.Shark - ? PrimitiveType.Capsule - : PrimitiveType.Cube; - - var go = GameObject.CreatePrimitive(primitive); + var go = spawnInfo.Role == BenchmarkAgentRole.Shark + ? GameObject.CreatePrimitive(PrimitiveType.Capsule) + : CreateConeObject(); go.name = $"{spawnInfo.Role}_{spawnInfo.GroupId:0000}"; go.transform.SetParent(parent, false); go.transform.position = spawnInfo.Position; var scale = spawnInfo.Role == BenchmarkAgentRole.Shark ? 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; var collider = go.GetComponent(); @@ -105,7 +112,84 @@ namespace FishROV.AvoidanceBenchmark 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.sharedMesh = GetSharedConeMesh(); + + var meshRenderer = go.AddComponent(); + 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(); } } } diff --git a/FishROV/Assets/Scripts/Benchmark/FrameworkAdapters/UnityNavMeshAdapter.cs b/FishROV/Assets/Scripts/Benchmark/FrameworkAdapters/UnityNavMeshAdapter.cs index 2be5fb2..040838c 100644 --- a/FishROV/Assets/Scripts/Benchmark/FrameworkAdapters/UnityNavMeshAdapter.cs +++ b/FishROV/Assets/Scripts/Benchmark/FrameworkAdapters/UnityNavMeshAdapter.cs @@ -8,6 +8,8 @@ namespace FishROV.AvoidanceBenchmark { private readonly Dictionary navAgents = new Dictionary(512); private AvoidanceBenchmarkConfig config; + private NavMeshData runtimeNavMeshData; + private NavMeshDataInstance runtimeNavMeshInstance; private bool hasNavMeshData; private int fallbackAgentCount; @@ -20,7 +22,7 @@ namespace FishROV.AvoidanceBenchmark { if (!hasNavMeshData) { - return "未检测到运行时 NavMesh 数据;当前退回基线移动。请先烘焙 NavMesh,或启用 com.unity.ai.navigation 并构建运行时 NavMeshSurface。"; + return "NavMesh 数据构建失败;当前退回基线移动。"; } if (fallbackAgentCount > 0) @@ -37,6 +39,7 @@ namespace FishROV.AvoidanceBenchmark this.config = config.Clone(); navAgents.Clear(); fallbackAgentCount = 0; + BuildRuntimeNavMesh(); var triangulation = NavMesh.CalculateTriangulation(); hasNavMeshData = triangulation.vertices != null && triangulation.vertices.Length > 0; @@ -118,6 +121,46 @@ namespace FishROV.AvoidanceBenchmark navAgents.Clear(); 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 + { + 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)