Use cone agents and build runtime NavMesh
This commit is contained in:
@@ -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<Collider>();
|
||||
@@ -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>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace FishROV.AvoidanceBenchmark
|
||||
{
|
||||
private readonly Dictionary<BenchmarkAgent, NavMeshAgent> navAgents = new Dictionary<BenchmarkAgent, NavMeshAgent>(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<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)
|
||||
|
||||
Reference in New Issue
Block a user