Merge Unity NavMesh benchmark adapter
This commit is contained in:
@@ -13,7 +13,7 @@ namespace FishROV.AvoidanceBenchmark
|
|||||||
case AvoidanceFrameworkKind.Rvo2:
|
case AvoidanceFrameworkKind.Rvo2:
|
||||||
return new Rvo2Adapter();
|
return new Rvo2Adapter();
|
||||||
case AvoidanceFrameworkKind.UnityNavMesh:
|
case AvoidanceFrameworkKind.UnityNavMesh:
|
||||||
return new UnavailableAvoidanceAdapter(kind);
|
return new UnityNavMeshAdapter();
|
||||||
default:
|
default:
|
||||||
return new UnavailableAvoidanceAdapter(kind);
|
return new UnavailableAvoidanceAdapter(kind);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.AI;
|
||||||
|
|
||||||
|
namespace FishROV.AvoidanceBenchmark
|
||||||
|
{
|
||||||
|
public sealed class UnityNavMeshAdapter : IAvoidanceAdapter
|
||||||
|
{
|
||||||
|
private readonly Dictionary<BenchmarkAgent, NavMeshAgent> navAgents = new Dictionary<BenchmarkAgent, NavMeshAgent>(512);
|
||||||
|
private AvoidanceBenchmarkConfig config;
|
||||||
|
private bool hasNavMeshData;
|
||||||
|
private int fallbackAgentCount;
|
||||||
|
|
||||||
|
public string Name => "Unity NavMeshAgent";
|
||||||
|
public bool IsAvailable => hasNavMeshData;
|
||||||
|
|
||||||
|
public string Status
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!hasNavMeshData)
|
||||||
|
{
|
||||||
|
return "No runtime NavMesh data found; falling back to baseline movement. Bake a NavMesh or enable com.unity.ai.navigation and build a runtime NavMeshSurface.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallbackAgentCount > 0)
|
||||||
|
{
|
||||||
|
return $"Using NavMeshAgent for {navAgents.Count} agents; {fallbackAgentCount} agents could not be placed on the NavMesh and use baseline movement.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"Using Unity NavMeshAgent avoidance for {navAgents.Count} agents.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(AvoidanceBenchmarkConfig config)
|
||||||
|
{
|
||||||
|
this.config = config.Clone();
|
||||||
|
navAgents.Clear();
|
||||||
|
fallbackAgentCount = 0;
|
||||||
|
|
||||||
|
var triangulation = NavMesh.CalculateTriangulation();
|
||||||
|
hasNavMeshData = triangulation.vertices != null && triangulation.vertices.Length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BenchmarkAgent CreateAgent(BenchmarkAgentSpawnInfo spawnInfo, Transform parent)
|
||||||
|
{
|
||||||
|
var benchmarkAgent = BenchmarkAgent.CreatePrimitive(spawnInfo, parent);
|
||||||
|
if (!hasNavMeshData)
|
||||||
|
{
|
||||||
|
return benchmarkAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryPlaceOnNavMesh(benchmarkAgent, spawnInfo))
|
||||||
|
{
|
||||||
|
fallbackAgentCount++;
|
||||||
|
return benchmarkAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
var navAgent = benchmarkAgent.gameObject.AddComponent<NavMeshAgent>();
|
||||||
|
ConfigureNavMeshAgent(navAgent, spawnInfo);
|
||||||
|
navAgents[benchmarkAgent] = navAgent;
|
||||||
|
return benchmarkAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPreferredVelocity(BenchmarkAgent agent, Vector3 preferredVelocity)
|
||||||
|
{
|
||||||
|
if (!hasNavMeshData || !navAgents.TryGetValue(agent, out var navAgent) || !navAgent.isOnNavMesh)
|
||||||
|
{
|
||||||
|
agent.ApplyVelocity(preferredVelocity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var navVelocity = preferredVelocity;
|
||||||
|
if (config.DimensionMode != AvoidanceDimensionMode.XYZ3D)
|
||||||
|
{
|
||||||
|
navVelocity.y = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
navAgent.nextPosition = agent.transform.position;
|
||||||
|
navAgent.velocity = Vector3.ClampMagnitude(navVelocity, agent.MaxSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTarget(BenchmarkAgent agent, Vector3 target)
|
||||||
|
{
|
||||||
|
agent.Target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Tick(float deltaTime)
|
||||||
|
{
|
||||||
|
if (!hasNavMeshData)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entry in navAgents)
|
||||||
|
{
|
||||||
|
var benchmarkAgent = entry.Key;
|
||||||
|
var navAgent = entry.Value;
|
||||||
|
if (benchmarkAgent == null || navAgent == null || !navAgent.isOnNavMesh)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarkAgent.ApplyVelocity(navAgent.velocity);
|
||||||
|
navAgent.nextPosition = benchmarkAgent.transform.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var navAgent in navAgents.Values)
|
||||||
|
{
|
||||||
|
if (navAgent != null)
|
||||||
|
{
|
||||||
|
Object.Destroy(navAgent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navAgents.Clear();
|
||||||
|
fallbackAgentCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryPlaceOnNavMesh(BenchmarkAgent benchmarkAgent, BenchmarkAgentSpawnInfo spawnInfo)
|
||||||
|
{
|
||||||
|
var searchDistance = Mathf.Max(2f, spawnInfo.Height + spawnInfo.Radius);
|
||||||
|
if (!NavMesh.SamplePosition(spawnInfo.Position, out var hit, searchDistance, NavMesh.AllAreas))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarkAgent.transform.position = hit.position;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConfigureNavMeshAgent(NavMeshAgent navAgent, BenchmarkAgentSpawnInfo spawnInfo)
|
||||||
|
{
|
||||||
|
navAgent.radius = Mathf.Max(0.01f, spawnInfo.Radius);
|
||||||
|
navAgent.height = Mathf.Max(0.1f, spawnInfo.Height);
|
||||||
|
navAgent.speed = Mathf.Max(0.01f, spawnInfo.MaxSpeed);
|
||||||
|
navAgent.acceleration = navAgent.speed * 8f;
|
||||||
|
navAgent.angularSpeed = 720f;
|
||||||
|
navAgent.obstacleAvoidanceType = ObstacleAvoidanceType.HighQualityObstacleAvoidance;
|
||||||
|
navAgent.avoidancePriority = Mathf.Clamp(Mathf.RoundToInt((1f - spawnInfo.Priority) * 99f), 0, 99);
|
||||||
|
navAgent.autoBraking = false;
|
||||||
|
navAgent.autoRepath = false;
|
||||||
|
navAgent.updatePosition = false;
|
||||||
|
navAgent.updateRotation = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7d3b299dd0504f849b83e4af9ed272a7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -65,3 +65,9 @@ The A* RVO, RVO2, and Unity NavMesh adapters are selected through the same debug
|
|||||||
- The adapter looks for a simulator type such as `RVO.Simulator`, `RVO2.Simulator`, `RVO3D.Simulator`, or `RVO.Simulator3D`.
|
- The adapter looks for a simulator type such as `RVO.Simulator`, `RVO2.Simulator`, `RVO3D.Simulator`, or `RVO.Simulator3D`.
|
||||||
- The required simulator methods are `addAgent`/`AddAgent`, `setAgentPrefVelocity`/`SetAgentPrefVelocity`, `getAgentVelocity`/`GetAgentVelocity`, and `doStep`/`DoStep`.
|
- The required simulator methods are `addAgent`/`AddAgent`, `setAgentPrefVelocity`/`SetAgentPrefVelocity`, `getAgentVelocity`/`GetAgentVelocity`, and `doStep`/`DoStep`.
|
||||||
- 2D and 2.5D modes bind to an RVO `Vector2` style type and map Unity `x/z` to RVO `x/y`. 3D mode binds to an RVO `Vector3` style type when available.
|
- 2D and 2.5D modes bind to an RVO `Vector2` style type and map Unity `x/z` to RVO `x/y`. 3D mode binds to an RVO `Vector3` style type when available.
|
||||||
|
|
||||||
|
## Unity NavMesh Adapter Notes
|
||||||
|
|
||||||
|
`UnityNavMeshAdapter` uses the built-in `UnityEngine.AI.NavMeshAgent` API when NavMesh data is already available at runtime. The benchmark scene creates its fish and shark primitives dynamically, so a normal empty runtime cube scene may not have baked NavMesh data. In that case the adapter reports the missing NavMesh in the HUD status and falls back to baseline movement instead of adding invalid `NavMeshAgent` components.
|
||||||
|
|
||||||
|
To exercise Unity's NavMesh avoidance path, provide NavMesh data before selecting `UnityNavMesh`. A baked scene NavMesh is sufficient. If the project should build the benchmark floor at runtime, enable `com.unity.ai.navigation` and add a runtime `NavMeshSurface` setup before the adapter initializes.
|
||||||
|
|||||||
Reference in New Issue
Block a user