Use cone agents and build runtime NavMesh
This commit is contained in:
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user