Merge Astar RVO benchmark adapter
This commit is contained in:
@@ -26,8 +26,8 @@ namespace FishROV.AvoidanceBenchmark
|
|||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
Application.targetFrameRate = 60;
|
|
||||||
QualitySettings.vSyncCount = 0;
|
QualitySettings.vSyncCount = 0;
|
||||||
|
Application.targetFrameRate = -1;
|
||||||
pendingFishCount = fishCount;
|
pendingFishCount = fishCount;
|
||||||
metrics.Start();
|
metrics.Start();
|
||||||
EnsureCameraAndLight();
|
EnsureCameraAndLight();
|
||||||
@@ -63,33 +63,34 @@ namespace FishROV.AvoidanceBenchmark
|
|||||||
private void OnGUI()
|
private void OnGUI()
|
||||||
{
|
{
|
||||||
GUILayout.BeginArea(new Rect(12, 12, 390, Screen.height - 24), GUI.skin.box);
|
GUILayout.BeginArea(new Rect(12, 12, 390, Screen.height - 24), GUI.skin.box);
|
||||||
GUILayout.Label("FishROV Local Avoidance Benchmark");
|
GUILayout.Label("FishROV 本地避障压测");
|
||||||
GUILayout.Label($"Adapter: {adapter?.Name ?? "None"}");
|
GUILayout.Label($"当前方案:{adapter?.Name ?? "未初始化"}");
|
||||||
GUILayout.Label($"Status: {adapter?.Status ?? "Not initialized"}");
|
GUILayout.Label($"状态:{adapter?.Status ?? "未初始化"}");
|
||||||
GUILayout.Label($"Agents: fish {fishCount}, shark {sharkCount}, total {agents.Count}");
|
GUILayout.Label($"对象数量:小鱼 {fishCount},鲨鱼 {sharkCount},总数 {agents.Count}");
|
||||||
GUILayout.Label($"FPS avg {metrics.AverageFps:F1} | 1% low {metrics.OnePercentLowFps:F1}");
|
GUILayout.Label($"帧率:平均 {metrics.AverageFps:F1} | 1% Low {metrics.OnePercentLowFps:F1}");
|
||||||
GUILayout.Label($"Simulation {metrics.SimulationMs:F2} ms | GC {metrics.GcAllocBytes / 1024f:F1} KB");
|
GUILayout.Label($"模拟耗时:{metrics.SimulationMs:F2} ms | GC:{metrics.GcAllocBytes / 1024f:F1} KB");
|
||||||
|
GUILayout.Label($"帧率限制:不锁帧,vSync={QualitySettings.vSyncCount},targetFrameRate={Application.targetFrameRate}");
|
||||||
|
|
||||||
GUILayout.Space(8);
|
GUILayout.Space(8);
|
||||||
DrawEnumButtons("Framework", framework, value =>
|
DrawEnumButtons("避障方案", framework, value =>
|
||||||
{
|
{
|
||||||
framework = value;
|
framework = value;
|
||||||
ResetBenchmark();
|
ResetBenchmark();
|
||||||
});
|
});
|
||||||
DrawEnumButtons("Scenario", scenario, value =>
|
DrawEnumButtons("测试场景", scenario, value =>
|
||||||
{
|
{
|
||||||
scenario = value;
|
scenario = value;
|
||||||
ResetBenchmark();
|
ResetBenchmark();
|
||||||
});
|
});
|
||||||
DrawEnumButtons("Dimension", dimensionMode, value =>
|
DrawEnumButtons("计算维度", dimensionMode, value =>
|
||||||
{
|
{
|
||||||
dimensionMode = value;
|
dimensionMode = value;
|
||||||
ResetBenchmark();
|
ResetBenchmark();
|
||||||
});
|
});
|
||||||
DrawEnumButtons("View", viewMode, value => viewMode = value);
|
DrawEnumButtons("观察视角", viewMode, value => viewMode = value);
|
||||||
|
|
||||||
GUILayout.Space(8);
|
GUILayout.Space(8);
|
||||||
GUILayout.Label($"Fish Count: {pendingFishCount}");
|
GUILayout.Label($"小鱼数量:{pendingFishCount}");
|
||||||
GUILayout.BeginHorizontal();
|
GUILayout.BeginHorizontal();
|
||||||
foreach (var preset in CountPresets)
|
foreach (var preset in CountPresets)
|
||||||
{
|
{
|
||||||
@@ -105,18 +106,18 @@ namespace FishROV.AvoidanceBenchmark
|
|||||||
pendingFishCount = Mathf.RoundToInt(GUILayout.HorizontalSlider(pendingFishCount, 10, 5000));
|
pendingFishCount = Mathf.RoundToInt(GUILayout.HorizontalSlider(pendingFishCount, 10, 5000));
|
||||||
|
|
||||||
GUILayout.BeginHorizontal();
|
GUILayout.BeginHorizontal();
|
||||||
if (GUILayout.Button("Apply Count"))
|
if (GUILayout.Button("应用数量"))
|
||||||
{
|
{
|
||||||
fishCount = pendingFishCount;
|
fishCount = pendingFishCount;
|
||||||
ResetBenchmark();
|
ResetBenchmark();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GUILayout.Button(paused ? "Resume" : "Pause"))
|
if (GUILayout.Button(paused ? "继续" : "暂停"))
|
||||||
{
|
{
|
||||||
paused = !paused;
|
paused = !paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GUILayout.Button("Reset"))
|
if (GUILayout.Button("重置"))
|
||||||
{
|
{
|
||||||
ResetBenchmark();
|
ResetBenchmark();
|
||||||
}
|
}
|
||||||
@@ -322,7 +323,7 @@ namespace FishROV.AvoidanceBenchmark
|
|||||||
foreach (T value in System.Enum.GetValues(typeof(T)))
|
foreach (T value in System.Enum.GetValues(typeof(T)))
|
||||||
{
|
{
|
||||||
GUI.enabled = !EqualityComparer<T>.Default.Equals(value, current);
|
GUI.enabled = !EqualityComparer<T>.Default.Equals(value, current);
|
||||||
if (GUILayout.Button(value.ToString(), GUILayout.Height(24)))
|
if (GUILayout.Button(GetDisplayName(value), GUILayout.Height(24)))
|
||||||
{
|
{
|
||||||
onChanged(value);
|
onChanged(value);
|
||||||
}
|
}
|
||||||
@@ -330,5 +331,49 @@ namespace FishROV.AvoidanceBenchmark
|
|||||||
GUI.enabled = true;
|
GUI.enabled = true;
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetDisplayName<T>(T value) where T : System.Enum
|
||||||
|
{
|
||||||
|
var boxed = (object)value;
|
||||||
|
switch (boxed)
|
||||||
|
{
|
||||||
|
case AvoidanceFrameworkKind.NoAvoidance:
|
||||||
|
return "无避障";
|
||||||
|
case AvoidanceFrameworkKind.AstarRvo:
|
||||||
|
return "A* RVO";
|
||||||
|
case AvoidanceFrameworkKind.Rvo2:
|
||||||
|
return "RVO2";
|
||||||
|
case AvoidanceFrameworkKind.UnityNavMesh:
|
||||||
|
return "NavMesh";
|
||||||
|
case AvoidanceScenarioKind.FreeSwim:
|
||||||
|
return "自由巡游";
|
||||||
|
case AvoidanceScenarioKind.CrossFlow:
|
||||||
|
return "对向穿流";
|
||||||
|
case AvoidanceScenarioKind.SharkCharge:
|
||||||
|
return "鲨鱼冲群";
|
||||||
|
case AvoidanceScenarioKind.NarrowPassage:
|
||||||
|
return "狭窄通道";
|
||||||
|
case AvoidanceScenarioKind.DenseCircleStress:
|
||||||
|
return "圆阵高压";
|
||||||
|
case AvoidanceDimensionMode.XZ2D:
|
||||||
|
return "2D/XZ";
|
||||||
|
case AvoidanceDimensionMode.XZ25D:
|
||||||
|
return "2.5D";
|
||||||
|
case AvoidanceDimensionMode.XYZ3D:
|
||||||
|
return "3D/XYZ";
|
||||||
|
case BenchmarkViewMode.Overview:
|
||||||
|
return "总览";
|
||||||
|
case BenchmarkViewMode.TopDown:
|
||||||
|
return "俯视";
|
||||||
|
case BenchmarkViewMode.Side:
|
||||||
|
return "侧视";
|
||||||
|
case BenchmarkViewMode.Shark:
|
||||||
|
return "跟鲨鱼";
|
||||||
|
case BenchmarkViewMode.FollowSchool:
|
||||||
|
return "跟鱼群";
|
||||||
|
default:
|
||||||
|
return value.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace FishROV.AvoidanceBenchmark
|
||||||
|
{
|
||||||
|
public sealed class AstarRvoAdapter : IAvoidanceAdapter
|
||||||
|
{
|
||||||
|
private const BindingFlags MemberFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||||
|
|
||||||
|
private readonly Dictionary<BenchmarkAgent, RvoAgentBinding> bindings =
|
||||||
|
new Dictionary<BenchmarkAgent, RvoAgentBinding>(512);
|
||||||
|
|
||||||
|
private Type rvoControllerType;
|
||||||
|
private Type rvoSimulatorType;
|
||||||
|
private MethodInfo setTargetMethod;
|
||||||
|
private MethodInfo calculateMovementDeltaMethod;
|
||||||
|
private GameObject simulatorObject;
|
||||||
|
private AvoidanceBenchmarkConfig config;
|
||||||
|
private string status = "Not initialized";
|
||||||
|
|
||||||
|
public string Name => "A* RVO 本地避障";
|
||||||
|
public bool IsAvailable => rvoControllerType != null && rvoSimulatorType != null;
|
||||||
|
public string Status => status;
|
||||||
|
|
||||||
|
public void Initialize(AvoidanceBenchmarkConfig config)
|
||||||
|
{
|
||||||
|
this.config = config;
|
||||||
|
ResolveAstarTypes();
|
||||||
|
|
||||||
|
if (!IsAvailable)
|
||||||
|
{
|
||||||
|
status = "未检测到 A* Pathfinding Project Pro 的 RVO 模块;请导入 A* Pro,确保存在 Pathfinding.RVO.RVOController 和 RVOSimulator。当前退回基线移动。";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
simulatorObject = new GameObject("A* RVO 模拟器");
|
||||||
|
var simulator = simulatorObject.AddComponent(rvoSimulatorType);
|
||||||
|
ConfigureSimulator(simulator);
|
||||||
|
|
||||||
|
var dimensionNote = config.DimensionMode == AvoidanceDimensionMode.XYZ3D
|
||||||
|
? "3D 压测会投影到 A* RVO 的 XZ 平面"
|
||||||
|
: "XZ 平面";
|
||||||
|
status = $"A* Pro RVO 已通过反射接入({dimensionNote})。";
|
||||||
|
}
|
||||||
|
|
||||||
|
public BenchmarkAgent CreateAgent(BenchmarkAgentSpawnInfo spawnInfo, Transform parent)
|
||||||
|
{
|
||||||
|
var agent = BenchmarkAgent.CreatePrimitive(spawnInfo, parent);
|
||||||
|
if (!IsAvailable)
|
||||||
|
{
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
var controller = agent.gameObject.AddComponent(rvoControllerType);
|
||||||
|
ConfigureController(controller, spawnInfo);
|
||||||
|
bindings[agent] = new RvoAgentBinding(agent, controller);
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPreferredVelocity(BenchmarkAgent agent, Vector3 preferredVelocity)
|
||||||
|
{
|
||||||
|
if (!IsAvailable || !bindings.TryGetValue(agent, out var binding))
|
||||||
|
{
|
||||||
|
agent.ApplyVelocity(preferredVelocity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.PreferredVelocity = Vector3.ClampMagnitude(preferredVelocity, agent.MaxSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTarget(BenchmarkAgent agent, Vector3 target)
|
||||||
|
{
|
||||||
|
agent.Target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Tick(float deltaTime)
|
||||||
|
{
|
||||||
|
if (!IsAvailable || deltaTime <= 0f)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var binding in bindings.Values)
|
||||||
|
{
|
||||||
|
var velocity = CalculateAvoidedVelocity(binding, deltaTime);
|
||||||
|
binding.Agent.ApplyVelocity(velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
bindings.Clear();
|
||||||
|
if (simulatorObject != null)
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(simulatorObject);
|
||||||
|
simulatorObject = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResolveAstarTypes()
|
||||||
|
{
|
||||||
|
rvoControllerType = FindType("Pathfinding.RVO.RVOController");
|
||||||
|
rvoSimulatorType = FindType("Pathfinding.RVO.RVOSimulator");
|
||||||
|
|
||||||
|
if (rvoControllerType == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTargetMethod = rvoControllerType.GetMethod(
|
||||||
|
"SetTarget",
|
||||||
|
MemberFlags,
|
||||||
|
null,
|
||||||
|
new[] { typeof(Vector3), typeof(float), typeof(float), typeof(Vector3) },
|
||||||
|
null);
|
||||||
|
calculateMovementDeltaMethod = rvoControllerType.GetMethod(
|
||||||
|
"CalculateMovementDelta",
|
||||||
|
MemberFlags,
|
||||||
|
null,
|
||||||
|
new[] { typeof(Vector3), typeof(float) },
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigureSimulator(object simulator)
|
||||||
|
{
|
||||||
|
SetMember(simulator, "desiredSimulationFPS", 60);
|
||||||
|
SetMember(simulator, "movementPlane", "XZ");
|
||||||
|
SetMember(simulator, "doubleBuffering", true);
|
||||||
|
SetMember(simulator, "workerThreads", "None");
|
||||||
|
SetMember(simulator, "symmetryBreakingBias", 0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigureController(object controller, BenchmarkAgentSpawnInfo spawnInfo)
|
||||||
|
{
|
||||||
|
SetMember(controller, "radius", spawnInfo.Radius);
|
||||||
|
SetMember(controller, "height", Mathf.Max(spawnInfo.Height, spawnInfo.Radius * 2f));
|
||||||
|
SetMember(controller, "center", Vector3.up * spawnInfo.Height * 0.5f);
|
||||||
|
SetMember(controller, "maxSpeed", spawnInfo.MaxSpeed);
|
||||||
|
SetMember(controller, "priority", Mathf.Clamp01(spawnInfo.Priority));
|
||||||
|
SetMember(controller, "layer", spawnInfo.Layer);
|
||||||
|
SetMember(controller, "collidesWith", spawnInfo.CollidesWith);
|
||||||
|
SetMember(controller, "locked", spawnInfo.IsKinematicObstacle);
|
||||||
|
SetMember(controller, "lockWhenNotMoving", spawnInfo.IsKinematicObstacle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3 CalculateAvoidedVelocity(RvoAgentBinding binding, float deltaTime)
|
||||||
|
{
|
||||||
|
var agent = binding.Agent;
|
||||||
|
var preferredVelocity = binding.PreferredVelocity;
|
||||||
|
var position = agent.transform.position;
|
||||||
|
var target = position + preferredVelocity;
|
||||||
|
var speed = preferredVelocity.magnitude;
|
||||||
|
|
||||||
|
if (setTargetMethod != null)
|
||||||
|
{
|
||||||
|
TryInvoke(
|
||||||
|
setTargetMethod,
|
||||||
|
binding.Controller,
|
||||||
|
new object[] { target, speed, agent.MaxSpeed, target });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calculateMovementDeltaMethod == null)
|
||||||
|
{
|
||||||
|
return preferredVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var delta = calculateMovementDeltaMethod.Invoke(
|
||||||
|
binding.Controller,
|
||||||
|
new object[] { position, deltaTime });
|
||||||
|
if (delta is Vector3 movementDelta)
|
||||||
|
{
|
||||||
|
return movementDelta / deltaTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
status = $"A* RVO 反射调用失败:{exception.GetBaseException().Message}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return preferredVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryInvoke(MethodInfo method, object target, object[] parameters)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
method.Invoke(target, parameters);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Keep the benchmark running even when an A* version changes a reflected member.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetMember(object target, string name, object value)
|
||||||
|
{
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = target.GetType();
|
||||||
|
var property = type.GetProperty(name, MemberFlags);
|
||||||
|
if (property != null && property.CanWrite)
|
||||||
|
{
|
||||||
|
TrySetValue(property.PropertyType, value, converted => property.SetValue(target, converted, null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var field = type.GetField(name, MemberFlags);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
TrySetValue(field.FieldType, value, converted => field.SetValue(target, converted));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TrySetValue(Type targetType, object value, Action<object> setter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
setter(ConvertValue(targetType, value));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Optional reflected configuration only; defaults are acceptable across A* versions.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object ConvertValue(Type targetType, object value)
|
||||||
|
{
|
||||||
|
if (value == null || targetType.IsInstanceOfType(value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetType.IsEnum)
|
||||||
|
{
|
||||||
|
if (value is string enumName)
|
||||||
|
{
|
||||||
|
return Enum.Parse(targetType, enumName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enum.ToObject(targetType, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Convert.ChangeType(value, targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Type FindType(string fullName)
|
||||||
|
{
|
||||||
|
var direct = Type.GetType(fullName);
|
||||||
|
if (direct != null)
|
||||||
|
{
|
||||||
|
return direct;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
{
|
||||||
|
var type = assembly.GetType(fullName);
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class RvoAgentBinding
|
||||||
|
{
|
||||||
|
public RvoAgentBinding(BenchmarkAgent agent, object controller)
|
||||||
|
{
|
||||||
|
Agent = agent;
|
||||||
|
Controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BenchmarkAgent Agent { get; }
|
||||||
|
public object Controller { get; }
|
||||||
|
public Vector3 PreferredVelocity { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3f0e3f32e10e42eda4a2c6e58d0f9f8b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -9,6 +9,7 @@ namespace FishROV.AvoidanceBenchmark
|
|||||||
case AvoidanceFrameworkKind.NoAvoidance:
|
case AvoidanceFrameworkKind.NoAvoidance:
|
||||||
return new NoAvoidanceAdapter();
|
return new NoAvoidanceAdapter();
|
||||||
case AvoidanceFrameworkKind.AstarRvo:
|
case AvoidanceFrameworkKind.AstarRvo:
|
||||||
|
return new AstarRvoAdapter();
|
||||||
case AvoidanceFrameworkKind.Rvo2:
|
case AvoidanceFrameworkKind.Rvo2:
|
||||||
case AvoidanceFrameworkKind.UnityNavMesh:
|
case AvoidanceFrameworkKind.UnityNavMesh:
|
||||||
return new UnavailableAvoidanceAdapter(kind);
|
return new UnavailableAvoidanceAdapter(kind);
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ namespace FishROV.AvoidanceBenchmark
|
|||||||
{
|
{
|
||||||
public sealed class NoAvoidanceAdapter : IAvoidanceAdapter
|
public sealed class NoAvoidanceAdapter : IAvoidanceAdapter
|
||||||
{
|
{
|
||||||
public string Name => "No Avoidance";
|
public string Name => "无避障";
|
||||||
public bool IsAvailable => true;
|
public bool IsAvailable => true;
|
||||||
public string Status => "Baseline movement only";
|
public string Status => "基线移动:只按期望速度移动,不做避障。";
|
||||||
|
|
||||||
public void Initialize(AvoidanceBenchmarkConfig config)
|
public void Initialize(AvoidanceBenchmarkConfig config)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace FishROV.AvoidanceBenchmark
|
|||||||
|
|
||||||
public string Name => kind.ToString();
|
public string Name => kind.ToString();
|
||||||
public bool IsAvailable => false;
|
public bool IsAvailable => false;
|
||||||
public string Status => "Adapter placeholder. Implement this framework in its worktree.";
|
public string Status => "当前方案在这个 worktree 中只是占位;请选择本分支要测试的避障方案。";
|
||||||
|
|
||||||
public void Initialize(AvoidanceBenchmarkConfig config)
|
public void Initialize(AvoidanceBenchmarkConfig config)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,3 +46,12 @@ Adapter implementations may add package-specific components to created agents, b
|
|||||||
`NoAvoidanceAdapter` is the control group. It moves agents directly by preferred velocity and does not perform avoidance.
|
`NoAvoidanceAdapter` is the control group. It moves agents directly by preferred velocity and does not perform avoidance.
|
||||||
|
|
||||||
The A* RVO, RVO2, and Unity NavMesh adapters currently start as placeholders so their worktree diffs stay easy to review.
|
The A* RVO, RVO2, and Unity NavMesh adapters currently start as placeholders so their worktree diffs stay easy to review.
|
||||||
|
|
||||||
|
## A* Pathfinding Project Pro RVO Adapter
|
||||||
|
|
||||||
|
`AstarRvoAdapter` is isolated under `Assets/Scripts/Benchmark/FrameworkAdapters/` and is selected only by `AvoidanceFrameworkKind.AstarRvo`.
|
||||||
|
|
||||||
|
- It uses reflection to find `Pathfinding.RVO.RVOController` and `Pathfinding.RVO.RVOSimulator`, so the project still compiles when A* Pathfinding Project Pro is not imported.
|
||||||
|
- When the A* Pro RVO types are missing, the Game view status reports the missing package and the agents fall back to baseline preferred-velocity movement.
|
||||||
|
- When available, the adapter creates one runtime `RVOSimulator`, adds `RVOController` components to benchmark-created agents, feeds each preferred velocity into A* RVO, and writes the reflected avoidance velocity back through `BenchmarkAgent.ApplyVelocity`.
|
||||||
|
- The shared benchmark scene, HUD, scenarios, count presets, metrics, and camera logic remain unchanged; the adapter is the only intended variable for this branch.
|
||||||
|
|||||||
Reference in New Issue
Block a user