forked from bit/DotRecastNetSim
crowd profiling model + view
This commit is contained in:
parent
95af26e884
commit
67a1713e47
|
@ -1,8 +1,6 @@
|
||||||
using DotRecast.Detour.Crowd;
|
namespace DotRecast.Detour.Crowd
|
||||||
|
|
||||||
namespace DotRecast.Recast.Toolset.Tools
|
|
||||||
{
|
{
|
||||||
public class CrowdConfig
|
public class DtCrowdAgentConfig
|
||||||
{
|
{
|
||||||
public int expandOptions = 1;
|
public int expandOptions = 1;
|
||||||
public bool anticipateTurns = true;
|
public bool anticipateTurns = true;
|
|
@ -40,25 +40,8 @@ public class CrowdProfilingSampleTool : ISampleTool
|
||||||
private DemoSample _sample;
|
private DemoSample _sample;
|
||||||
private DtNavMesh m_nav;
|
private DtNavMesh m_nav;
|
||||||
|
|
||||||
private readonly CrowdConfig _option = new CrowdConfig();
|
private readonly RcCrowdProfilingTool _tool;
|
||||||
private RcCrowdProfilingTool _tool;
|
|
||||||
|
|
||||||
private int expandSimOptions = 1;
|
|
||||||
private int expandCrowdOptions = 1;
|
|
||||||
private int agents = 1000;
|
|
||||||
private int randomSeed = 270;
|
|
||||||
private int numberOfZones = 4;
|
|
||||||
private float zoneRadius = 20f;
|
|
||||||
private float percentMobs = 80f;
|
|
||||||
private float percentTravellers = 15f;
|
|
||||||
private int pathQueueSize = 32;
|
|
||||||
private int maxIterations = 300;
|
|
||||||
private DtCrowd crowd;
|
|
||||||
private DtNavMesh navMesh;
|
|
||||||
private DtCrowdConfig config;
|
|
||||||
private FRand rnd;
|
|
||||||
private readonly List<DtPolyPoint> _polyPoints = new();
|
|
||||||
private long crowdUpdateTime;
|
|
||||||
|
|
||||||
public CrowdProfilingSampleTool()
|
public CrowdProfilingSampleTool()
|
||||||
{
|
{
|
||||||
|
@ -67,58 +50,64 @@ public class CrowdProfilingSampleTool : ISampleTool
|
||||||
|
|
||||||
public void Layout()
|
public void Layout()
|
||||||
{
|
{
|
||||||
var prevOptimizeVis = _option.optimizeVis;
|
var cfg = _tool.GetCrowdConfig();
|
||||||
var prevOptimizeTopo = _option.optimizeTopo;
|
var prevOptimizeVis = cfg.optimizeVis;
|
||||||
var prevAnticipateTurns = _option.anticipateTurns;
|
var prevOptimizeTopo = cfg.optimizeTopo;
|
||||||
var prevObstacleAvoidance = _option.obstacleAvoidance;
|
var prevAnticipateTurns = cfg.anticipateTurns;
|
||||||
var prevSeparation = _option.separation;
|
var prevObstacleAvoidance = cfg.obstacleAvoidance;
|
||||||
var prevObstacleAvoidanceType = _option.obstacleAvoidanceType;
|
var prevSeparation = cfg.separation;
|
||||||
var prevSeparationWeight = _option.separationWeight;
|
var prevObstacleAvoidanceType = cfg.obstacleAvoidanceType;
|
||||||
|
var prevSeparationWeight = cfg.separationWeight;
|
||||||
|
|
||||||
ImGui.Text("Options");
|
ImGui.Text("Options");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.Checkbox("Optimize Visibility", ref _option.optimizeVis);
|
ImGui.Checkbox("Optimize Visibility", ref cfg.optimizeVis);
|
||||||
ImGui.Checkbox("Optimize Topology", ref _option.optimizeTopo);
|
ImGui.Checkbox("Optimize Topology", ref cfg.optimizeTopo);
|
||||||
ImGui.Checkbox("Anticipate Turns", ref _option.anticipateTurns);
|
ImGui.Checkbox("Anticipate Turns", ref cfg.anticipateTurns);
|
||||||
ImGui.Checkbox("Obstacle Avoidance", ref _option.obstacleAvoidance);
|
ImGui.Checkbox("Obstacle Avoidance", ref cfg.obstacleAvoidance);
|
||||||
ImGui.SliderInt("Avoidance Quality", ref _option.obstacleAvoidanceType, 0, 3);
|
ImGui.SliderInt("Avoidance Quality", ref cfg.obstacleAvoidanceType, 0, 3);
|
||||||
ImGui.Checkbox("Separation", ref _option.separation);
|
ImGui.Checkbox("Separation", ref cfg.separation);
|
||||||
ImGui.SliderFloat("Separation Weight", ref _option.separationWeight, 0f, 20f, "%.2f");
|
ImGui.SliderFloat("Separation Weight", ref cfg.separationWeight, 0f, 20f, "%.2f");
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
|
|
||||||
if (prevOptimizeVis != _option.optimizeVis || prevOptimizeTopo != _option.optimizeTopo
|
if (prevOptimizeVis != cfg.optimizeVis || prevOptimizeTopo != cfg.optimizeTopo
|
||||||
|| prevAnticipateTurns != _option.anticipateTurns
|
|| prevAnticipateTurns != cfg.anticipateTurns
|
||||||
|| prevObstacleAvoidance != _option.obstacleAvoidance
|
|| prevObstacleAvoidance != cfg.obstacleAvoidance
|
||||||
|| prevSeparation != _option.separation
|
|| prevSeparation != cfg.separation
|
||||||
|| prevObstacleAvoidanceType != _option.obstacleAvoidanceType
|
|| prevObstacleAvoidanceType != cfg.obstacleAvoidanceType
|
||||||
|| !prevSeparationWeight.Equals(_option.separationWeight))
|
|| !prevSeparationWeight.Equals(cfg.separationWeight))
|
||||||
{
|
{
|
||||||
UpdateAgentParams();
|
_tool.UpdateAgentParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var toolCfg = _tool.GetToolConfig();
|
||||||
|
|
||||||
ImGui.Text("Simulation Options");
|
ImGui.Text("Simulation Options");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.SliderInt("Agents", ref agents, 0, 10000);
|
ImGui.SliderInt("Agents", ref toolCfg.agents, 0, 10000);
|
||||||
ImGui.SliderInt("Random Seed", ref randomSeed, 0, 1024);
|
ImGui.SliderInt("Random Seed", ref toolCfg.randomSeed, 0, 1024);
|
||||||
ImGui.SliderInt("Number of Zones", ref numberOfZones, 0, 10);
|
ImGui.SliderInt("Number of Zones", ref toolCfg.numberOfZones, 0, 10);
|
||||||
ImGui.SliderFloat("Zone Radius", ref zoneRadius, 0, 100, "%.0f");
|
ImGui.SliderFloat("Zone Radius", ref toolCfg.zoneRadius, 0, 100, "%.0f");
|
||||||
ImGui.SliderFloat("Mobs %", ref percentMobs, 0, 100, "%.0f");
|
ImGui.SliderFloat("Mobs %", ref toolCfg.percentMobs, 0, 100, "%.0f");
|
||||||
ImGui.SliderFloat("Travellers %", ref percentTravellers, 0, 100, "%.0f");
|
ImGui.SliderFloat("Travellers %", ref toolCfg.percentTravellers, 0, 100, "%.0f");
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
|
|
||||||
ImGui.Text("Crowd Options");
|
ImGui.Text("Crowd Options");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.SliderInt("Path Queue Size", ref pathQueueSize, 0, 1024);
|
ImGui.SliderInt("Path Queue Size", ref toolCfg.pathQueueSize, 0, 1024);
|
||||||
ImGui.SliderInt("Max Iterations", ref maxIterations, 0, 4000);
|
ImGui.SliderInt("Max Iterations", ref toolCfg.maxIterations, 0, 4000);
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
|
|
||||||
if (ImGui.Button("Start Crowd Profiling"))
|
if (ImGui.Button("Start Crowd Profiling"))
|
||||||
{
|
{
|
||||||
StartProfiling();
|
var settings = _sample.GetSettings();
|
||||||
|
_tool.StartProfiling(settings.agentRadius, settings.agentHeight, settings.agentMaxAcceleration, settings.agentMaxSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Text("Times");
|
ImGui.Text("Times");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
|
var crowd = _tool.GetCrowd();
|
||||||
if (crowd != null)
|
if (crowd != null)
|
||||||
{
|
{
|
||||||
ImGui.Text($"Max time to enqueue request: {crowd.Telemetry().MaxTimeToEnqueueRequest()} s");
|
ImGui.Text($"Max time to enqueue request: {crowd.Telemetry().MaxTimeToEnqueueRequest()} s");
|
||||||
|
@ -129,7 +118,7 @@ public class CrowdProfilingSampleTool : ISampleTool
|
||||||
ImGui.Text($"{rtt.Key}: {rtt.Micros} us");
|
ImGui.Text($"{rtt.Key}: {rtt.Micros} us");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Text($"Update Time: {crowdUpdateTime} ms");
|
ImGui.Text($"Update Time: {_tool.GetCrowdUpdateTime()} ms");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +126,8 @@ public class CrowdProfilingSampleTool : ISampleTool
|
||||||
{
|
{
|
||||||
RecastDebugDraw dd = renderer.GetDebugDraw();
|
RecastDebugDraw dd = renderer.GetDebugDraw();
|
||||||
dd.DepthMask(false);
|
dd.DepthMask(false);
|
||||||
|
|
||||||
|
var crowd = _tool.GetCrowd();
|
||||||
if (crowd != null)
|
if (crowd != null)
|
||||||
{
|
{
|
||||||
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
||||||
|
@ -197,27 +188,10 @@ public class CrowdProfilingSampleTool : ISampleTool
|
||||||
if (navMesh != null && m_nav != navMesh)
|
if (navMesh != null && m_nav != navMesh)
|
||||||
{
|
{
|
||||||
m_nav = navMesh;
|
m_nav = navMesh;
|
||||||
Setup(settings.agentRadius, m_nav);
|
_tool.Setup(settings.agentRadius, m_nav);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DtCrowdAgentParams GetAgentParams()
|
|
||||||
{
|
|
||||||
var settings = _sample.GetSettings();
|
|
||||||
|
|
||||||
DtCrowdAgentParams ap = new DtCrowdAgentParams();
|
|
||||||
ap.radius = settings.agentRadius;
|
|
||||||
ap.height = settings.agentHeight;
|
|
||||||
ap.maxAcceleration = settings.agentMaxAcceleration;
|
|
||||||
ap.maxSpeed = settings.agentMaxSpeed;
|
|
||||||
ap.collisionQueryRange = ap.radius * 12.0f;
|
|
||||||
ap.pathOptimizationRange = ap.radius * 30.0f;
|
|
||||||
ap.updateFlags = _option.GetUpdateFlags();
|
|
||||||
ap.obstacleAvoidanceType = _option.obstacleAvoidanceType;
|
|
||||||
ap.separationWeight = _option.separationWeight;
|
|
||||||
return ap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IRcToolable GetTool()
|
public IRcToolable GetTool()
|
||||||
{
|
{
|
||||||
return _tool;
|
return _tool;
|
||||||
|
@ -229,293 +203,14 @@ public class CrowdProfilingSampleTool : ISampleTool
|
||||||
//throw new NotImplementedException();
|
//throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DtStatus GetMobPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt)
|
|
||||||
{
|
|
||||||
return navquery.FindRandomPoint(filter, rnd, out var randomRef, out randomPt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DtStatus GetVillagerPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt)
|
|
||||||
{
|
|
||||||
randomPt = RcVec3f.Zero;
|
|
||||||
|
|
||||||
if (0 >= _polyPoints.Count)
|
|
||||||
return DtStatus.DT_FAILURE;
|
|
||||||
|
|
||||||
int zone = (int)(rnd.Next() * _polyPoints.Count);
|
|
||||||
return navquery.FindRandomPointWithinCircle(_polyPoints[zone].refs, _polyPoints[zone].pt, zoneRadius, filter, rnd,
|
|
||||||
out var randomRef, out randomPt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateZones()
|
|
||||||
{
|
|
||||||
_polyPoints.Clear();
|
|
||||||
IDtQueryFilter filter = new DtQueryDefaultFilter();
|
|
||||||
DtNavMeshQuery navquery = new DtNavMeshQuery(navMesh);
|
|
||||||
for (int i = 0; i < numberOfZones; i++)
|
|
||||||
{
|
|
||||||
float zoneSeparation = zoneRadius * zoneRadius * 16;
|
|
||||||
for (int k = 0; k < 100; k++)
|
|
||||||
{
|
|
||||||
var status = navquery.FindRandomPoint(filter, rnd, out var randomRef, out var randomPt);
|
|
||||||
if (status.Succeeded())
|
|
||||||
{
|
|
||||||
bool valid = true;
|
|
||||||
foreach (var zone in _polyPoints)
|
|
||||||
{
|
|
||||||
if (RcVec3f.DistSqr(zone.pt, randomPt) < zoneSeparation)
|
|
||||||
{
|
|
||||||
valid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid)
|
|
||||||
{
|
|
||||||
_polyPoints.Add(new DtPolyPoint(randomRef, randomPt));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateCrowd()
|
|
||||||
{
|
|
||||||
crowd = new DtCrowd(config, navMesh, __ => new DtQueryDefaultFilter(SampleAreaModifications.SAMPLE_POLYFLAGS_ALL,
|
|
||||||
SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED, new float[] { 1f, 10f, 1f, 1f, 2f, 1.5f }));
|
|
||||||
|
|
||||||
DtObstacleAvoidanceParams option = new DtObstacleAvoidanceParams(crowd.GetObstacleAvoidanceParams(0));
|
|
||||||
// Low (11)
|
|
||||||
option.velBias = 0.5f;
|
|
||||||
option.adaptiveDivs = 5;
|
|
||||||
option.adaptiveRings = 2;
|
|
||||||
option.adaptiveDepth = 1;
|
|
||||||
crowd.SetObstacleAvoidanceParams(0, option);
|
|
||||||
// Medium (22)
|
|
||||||
option.velBias = 0.5f;
|
|
||||||
option.adaptiveDivs = 5;
|
|
||||||
option.adaptiveRings = 2;
|
|
||||||
option.adaptiveDepth = 2;
|
|
||||||
crowd.SetObstacleAvoidanceParams(1, option);
|
|
||||||
// Good (45)
|
|
||||||
option.velBias = 0.5f;
|
|
||||||
option.adaptiveDivs = 7;
|
|
||||||
option.adaptiveRings = 2;
|
|
||||||
option.adaptiveDepth = 3;
|
|
||||||
crowd.SetObstacleAvoidanceParams(2, option);
|
|
||||||
// High (66)
|
|
||||||
option.velBias = 0.5f;
|
|
||||||
option.adaptiveDivs = 7;
|
|
||||||
option.adaptiveRings = 3;
|
|
||||||
option.adaptiveDepth = 3;
|
|
||||||
crowd.SetObstacleAvoidanceParams(3, option);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartProfiling()
|
|
||||||
{
|
|
||||||
if (null == navMesh)
|
|
||||||
return;
|
|
||||||
|
|
||||||
rnd = new FRand(randomSeed);
|
|
||||||
CreateCrowd();
|
|
||||||
CreateZones();
|
|
||||||
DtNavMeshQuery navquery = new DtNavMeshQuery(navMesh);
|
|
||||||
IDtQueryFilter filter = new DtQueryDefaultFilter();
|
|
||||||
for (int i = 0; i < agents; i++)
|
|
||||||
{
|
|
||||||
float tr = rnd.Next();
|
|
||||||
CrowdAgentType type = CrowdAgentType.MOB;
|
|
||||||
float mobsPcnt = percentMobs / 100f;
|
|
||||||
if (tr > mobsPcnt)
|
|
||||||
{
|
|
||||||
tr = rnd.Next();
|
|
||||||
float travellerPcnt = percentTravellers / 100f;
|
|
||||||
if (tr > travellerPcnt)
|
|
||||||
{
|
|
||||||
type = CrowdAgentType.VILLAGER;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
type = CrowdAgentType.TRAVELLER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var status = DtStatus.DT_FAILURE;
|
|
||||||
var randomPt = RcVec3f.Zero;
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case CrowdAgentType.MOB:
|
|
||||||
status = GetMobPosition(navquery, filter, out randomPt);
|
|
||||||
break;
|
|
||||||
case CrowdAgentType.VILLAGER:
|
|
||||||
status = GetVillagerPosition(navquery, filter, out randomPt);
|
|
||||||
break;
|
|
||||||
case CrowdAgentType.TRAVELLER:
|
|
||||||
status = GetVillagerPosition(navquery, filter, out randomPt);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.Succeeded())
|
|
||||||
{
|
|
||||||
AddAgent(randomPt, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(float dt)
|
|
||||||
{
|
|
||||||
long startTime = RcFrequency.Ticks;
|
|
||||||
if (crowd != null)
|
|
||||||
{
|
|
||||||
crowd.Config().pathQueueSize = pathQueueSize;
|
|
||||||
crowd.Config().maxFindPathIterations = maxIterations;
|
|
||||||
crowd.Update(dt, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
long endTime = RcFrequency.Ticks;
|
|
||||||
if (crowd != null)
|
|
||||||
{
|
|
||||||
DtNavMeshQuery navquery = new DtNavMeshQuery(navMesh);
|
|
||||||
IDtQueryFilter filter = new DtQueryDefaultFilter();
|
|
||||||
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
|
||||||
{
|
|
||||||
if (NeedsNewTarget(ag))
|
|
||||||
{
|
|
||||||
CrowdAgentData crowAgentData = (CrowdAgentData)ag.option.userData;
|
|
||||||
switch (crowAgentData.type)
|
|
||||||
{
|
|
||||||
case CrowdAgentType.MOB:
|
|
||||||
MoveMob(navquery, filter, ag, crowAgentData);
|
|
||||||
break;
|
|
||||||
case CrowdAgentType.VILLAGER:
|
|
||||||
MoveVillager(navquery, filter, ag, crowAgentData);
|
|
||||||
break;
|
|
||||||
case CrowdAgentType.TRAVELLER:
|
|
||||||
MoveTraveller(navquery, filter, ag, crowAgentData);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
crowdUpdateTime = (endTime - startTime) / TimeSpan.TicksPerMillisecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoveMob(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, CrowdAgentData crowAgentData)
|
|
||||||
{
|
|
||||||
// Move somewhere
|
|
||||||
var status = navquery.FindNearestPoly(ag.npos, crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _);
|
|
||||||
if (status.Succeeded())
|
|
||||||
{
|
|
||||||
status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, zoneRadius * 2f, filter, rnd,
|
|
||||||
out var randomRef, out var randomPt);
|
|
||||||
if (status.Succeeded())
|
|
||||||
{
|
|
||||||
crowd.RequestMoveTarget(ag, randomRef, randomPt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoveVillager(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, CrowdAgentData crowAgentData)
|
|
||||||
{
|
|
||||||
// Move somewhere close
|
|
||||||
var status = navquery.FindNearestPoly(ag.npos, crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _);
|
|
||||||
if (status.Succeeded())
|
|
||||||
{
|
|
||||||
status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, zoneRadius * 0.2f, filter, rnd,
|
|
||||||
out var randomRef, out var randomPt);
|
|
||||||
if (status.Succeeded())
|
|
||||||
{
|
|
||||||
crowd.RequestMoveTarget(ag, randomRef, randomPt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MoveTraveller(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, CrowdAgentData crowAgentData)
|
|
||||||
{
|
|
||||||
// Move to another zone
|
|
||||||
List<DtPolyPoint> potentialTargets = new();
|
|
||||||
foreach (var zone in _polyPoints)
|
|
||||||
{
|
|
||||||
if (RcVec3f.DistSqr(zone.pt, ag.npos) > zoneRadius * zoneRadius)
|
|
||||||
{
|
|
||||||
potentialTargets.Add(zone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 < potentialTargets.Count)
|
|
||||||
{
|
|
||||||
potentialTargets.Shuffle();
|
|
||||||
crowd.RequestMoveTarget(ag, potentialTargets[0].refs, potentialTargets[0].pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool NeedsNewTarget(DtCrowdAgent ag)
|
|
||||||
{
|
|
||||||
if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE
|
|
||||||
|| ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_FAILED)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VALID)
|
|
||||||
{
|
|
||||||
float dx = ag.targetPos.x - ag.npos.x;
|
|
||||||
float dy = ag.targetPos.y - ag.npos.y;
|
|
||||||
float dz = ag.targetPos.z - ag.npos.z;
|
|
||||||
return dx * dx + dy * dy + dz * dz < 0.3f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Setup(float maxAgentRadius, DtNavMesh nav)
|
|
||||||
{
|
|
||||||
navMesh = nav;
|
|
||||||
if (nav != null)
|
|
||||||
{
|
|
||||||
config = new DtCrowdConfig(maxAgentRadius);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void HandleUpdate(float dt)
|
public void HandleUpdate(float dt)
|
||||||
{
|
{
|
||||||
Update(dt);
|
_tool.Update(dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleClickRay(RcVec3f start, RcVec3f direction, bool shift)
|
public void HandleClickRay(RcVec3f start, RcVec3f direction, bool shift)
|
||||||
{
|
{
|
||||||
//throw new NotImplementedException();
|
//throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DtCrowdAgent AddAgent(RcVec3f p, CrowdAgentType type)
|
|
||||||
{
|
|
||||||
DtCrowdAgentParams ap = GetAgentParams();
|
|
||||||
ap.userData = new CrowdAgentData(type, p);
|
|
||||||
return crowd.AddAgent(p, ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAgentParams()
|
|
||||||
{
|
|
||||||
if (crowd != null)
|
|
||||||
{
|
|
||||||
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
|
||||||
{
|
|
||||||
DtCrowdAgentParams option = new DtCrowdAgentParams();
|
|
||||||
option.radius = ag.option.radius;
|
|
||||||
option.height = ag.option.height;
|
|
||||||
option.maxAcceleration = ag.option.maxAcceleration;
|
|
||||||
option.maxSpeed = ag.option.maxSpeed;
|
|
||||||
option.collisionQueryRange = ag.option.collisionQueryRange;
|
|
||||||
option.pathOptimizationRange = ag.option.pathOptimizationRange;
|
|
||||||
option.queryFilterType = ag.option.queryFilterType;
|
|
||||||
option.userData = ag.option.userData;
|
|
||||||
option.updateFlags = _option.GetUpdateFlags();
|
|
||||||
option.obstacleAvoidanceType = _option.obstacleAvoidanceType;
|
|
||||||
option.separationWeight = _option.separationWeight;
|
|
||||||
crowd.UpdateAgentParameters(ag, option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
namespace DotRecast.Recast.Toolset.Tools
|
||||||
|
{
|
||||||
|
public class CrowdProfilingToolConfig
|
||||||
|
{
|
||||||
|
public int expandSimOptions = 1;
|
||||||
|
public int expandCrowdOptions = 1;
|
||||||
|
public int agents = 1000;
|
||||||
|
public int randomSeed = 270;
|
||||||
|
public int numberOfZones = 4;
|
||||||
|
public float zoneRadius = 20f;
|
||||||
|
public float percentMobs = 80f;
|
||||||
|
public float percentTravellers = 15f;
|
||||||
|
public int pathQueueSize = 32;
|
||||||
|
public int maxIterations = 300;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,353 @@
|
||||||
namespace DotRecast.Recast.Toolset.Tools
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using DotRecast.Core;
|
||||||
|
using DotRecast.Detour;
|
||||||
|
using DotRecast.Detour.Crowd;
|
||||||
|
using DotRecast.Recast.Toolset.Builder;
|
||||||
|
|
||||||
|
namespace DotRecast.Recast.Toolset.Tools
|
||||||
{
|
{
|
||||||
public class RcCrowdProfilingTool : IRcToolable
|
public class RcCrowdProfilingTool : IRcToolable
|
||||||
{
|
{
|
||||||
|
private CrowdProfilingToolConfig _cfg;
|
||||||
|
|
||||||
|
private DtCrowdConfig _crowdCfg;
|
||||||
|
private DtCrowd crowd;
|
||||||
|
private readonly DtCrowdAgentConfig _agCfg;
|
||||||
|
|
||||||
|
private DtNavMesh navMesh;
|
||||||
|
|
||||||
|
private FRand rnd;
|
||||||
|
private readonly List<DtPolyPoint> _polyPoints;
|
||||||
|
private long crowdUpdateTime;
|
||||||
|
|
||||||
|
public RcCrowdProfilingTool()
|
||||||
|
{
|
||||||
|
_cfg = new CrowdProfilingToolConfig();
|
||||||
|
_agCfg = new DtCrowdAgentConfig();
|
||||||
|
_polyPoints = new List<DtPolyPoint>();
|
||||||
|
}
|
||||||
|
|
||||||
public string GetName()
|
public string GetName()
|
||||||
{
|
{
|
||||||
return "Crowd Profiling";
|
return "Crowd Profiling";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CrowdProfilingToolConfig GetToolConfig()
|
||||||
|
{
|
||||||
|
return _cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DtCrowdAgentConfig GetCrowdConfig()
|
||||||
|
{
|
||||||
|
return _agCfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DtCrowd GetCrowd()
|
||||||
|
{
|
||||||
|
return crowd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Setup(float maxAgentRadius, DtNavMesh nav)
|
||||||
|
{
|
||||||
|
navMesh = nav;
|
||||||
|
if (nav != null)
|
||||||
|
{
|
||||||
|
_crowdCfg = new DtCrowdConfig(maxAgentRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DtCrowdAgentParams GetAgentParams(float agentRadius, float agentHeight, float agentMaxAcceleration, float agentMaxSpeed)
|
||||||
|
{
|
||||||
|
DtCrowdAgentParams ap = new DtCrowdAgentParams();
|
||||||
|
ap.radius = agentRadius;
|
||||||
|
ap.height = agentHeight;
|
||||||
|
ap.maxAcceleration = agentMaxAcceleration;
|
||||||
|
ap.maxSpeed = agentMaxSpeed;
|
||||||
|
ap.collisionQueryRange = ap.radius * 12.0f;
|
||||||
|
ap.pathOptimizationRange = ap.radius * 30.0f;
|
||||||
|
ap.updateFlags = _agCfg.GetUpdateFlags();
|
||||||
|
ap.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
||||||
|
ap.separationWeight = _agCfg.separationWeight;
|
||||||
|
return ap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DtStatus GetMobPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt)
|
||||||
|
{
|
||||||
|
return navquery.FindRandomPoint(filter, rnd, out var randomRef, out randomPt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DtStatus GetVillagerPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt)
|
||||||
|
{
|
||||||
|
randomPt = RcVec3f.Zero;
|
||||||
|
|
||||||
|
if (0 >= _polyPoints.Count)
|
||||||
|
return DtStatus.DT_FAILURE;
|
||||||
|
|
||||||
|
int zone = (int)(rnd.Next() * _polyPoints.Count);
|
||||||
|
return navquery.FindRandomPointWithinCircle(_polyPoints[zone].refs, _polyPoints[zone].pt, _cfg.zoneRadius, filter, rnd,
|
||||||
|
out var randomRef, out randomPt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateZones()
|
||||||
|
{
|
||||||
|
_polyPoints.Clear();
|
||||||
|
IDtQueryFilter filter = new DtQueryDefaultFilter();
|
||||||
|
DtNavMeshQuery navquery = new DtNavMeshQuery(navMesh);
|
||||||
|
for (int i = 0; i < _cfg.numberOfZones; i++)
|
||||||
|
{
|
||||||
|
float zoneSeparation = _cfg.zoneRadius * _cfg.zoneRadius * 16;
|
||||||
|
for (int k = 0; k < 100; k++)
|
||||||
|
{
|
||||||
|
var status = navquery.FindRandomPoint(filter, rnd, out var randomRef, out var randomPt);
|
||||||
|
if (status.Succeeded())
|
||||||
|
{
|
||||||
|
bool valid = true;
|
||||||
|
foreach (var zone in _polyPoints)
|
||||||
|
{
|
||||||
|
if (RcVec3f.DistSqr(zone.pt, randomPt) < zoneSeparation)
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
_polyPoints.Add(new DtPolyPoint(randomRef, randomPt));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateCrowd()
|
||||||
|
{
|
||||||
|
crowd = new DtCrowd(_crowdCfg, navMesh, __ => new DtQueryDefaultFilter(
|
||||||
|
SampleAreaModifications.SAMPLE_POLYFLAGS_ALL,
|
||||||
|
SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED,
|
||||||
|
new float[] { 1f, 10f, 1f, 1f, 2f, 1.5f })
|
||||||
|
);
|
||||||
|
|
||||||
|
DtObstacleAvoidanceParams option = new DtObstacleAvoidanceParams(crowd.GetObstacleAvoidanceParams(0));
|
||||||
|
// Low (11)
|
||||||
|
option.velBias = 0.5f;
|
||||||
|
option.adaptiveDivs = 5;
|
||||||
|
option.adaptiveRings = 2;
|
||||||
|
option.adaptiveDepth = 1;
|
||||||
|
crowd.SetObstacleAvoidanceParams(0, option);
|
||||||
|
// Medium (22)
|
||||||
|
option.velBias = 0.5f;
|
||||||
|
option.adaptiveDivs = 5;
|
||||||
|
option.adaptiveRings = 2;
|
||||||
|
option.adaptiveDepth = 2;
|
||||||
|
crowd.SetObstacleAvoidanceParams(1, option);
|
||||||
|
// Good (45)
|
||||||
|
option.velBias = 0.5f;
|
||||||
|
option.adaptiveDivs = 7;
|
||||||
|
option.adaptiveRings = 2;
|
||||||
|
option.adaptiveDepth = 3;
|
||||||
|
crowd.SetObstacleAvoidanceParams(2, option);
|
||||||
|
// High (66)
|
||||||
|
option.velBias = 0.5f;
|
||||||
|
option.adaptiveDivs = 7;
|
||||||
|
option.adaptiveRings = 3;
|
||||||
|
option.adaptiveDepth = 3;
|
||||||
|
crowd.SetObstacleAvoidanceParams(3, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartProfiling(float agentRadius, float agentHeight, float agentMaxAcceleration, float agentMaxSpeed)
|
||||||
|
{
|
||||||
|
if (null == navMesh)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rnd = new FRand(_cfg.randomSeed);
|
||||||
|
CreateCrowd();
|
||||||
|
CreateZones();
|
||||||
|
DtNavMeshQuery navquery = new DtNavMeshQuery(navMesh);
|
||||||
|
IDtQueryFilter filter = new DtQueryDefaultFilter();
|
||||||
|
for (int i = 0; i < _cfg.agents; i++)
|
||||||
|
{
|
||||||
|
float tr = rnd.Next();
|
||||||
|
CrowdAgentType type = CrowdAgentType.MOB;
|
||||||
|
float mobsPcnt = _cfg.percentMobs / 100f;
|
||||||
|
if (tr > mobsPcnt)
|
||||||
|
{
|
||||||
|
tr = rnd.Next();
|
||||||
|
float travellerPcnt = _cfg.percentTravellers / 100f;
|
||||||
|
if (tr > travellerPcnt)
|
||||||
|
{
|
||||||
|
type = CrowdAgentType.VILLAGER;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = CrowdAgentType.TRAVELLER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = DtStatus.DT_FAILURE;
|
||||||
|
var randomPt = RcVec3f.Zero;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CrowdAgentType.MOB:
|
||||||
|
status = GetMobPosition(navquery, filter, out randomPt);
|
||||||
|
break;
|
||||||
|
case CrowdAgentType.VILLAGER:
|
||||||
|
status = GetVillagerPosition(navquery, filter, out randomPt);
|
||||||
|
break;
|
||||||
|
case CrowdAgentType.TRAVELLER:
|
||||||
|
status = GetVillagerPosition(navquery, filter, out randomPt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.Succeeded())
|
||||||
|
{
|
||||||
|
AddAgent(randomPt, type, agentRadius, agentHeight, agentMaxAcceleration, agentMaxSpeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(float dt)
|
||||||
|
{
|
||||||
|
long startTime = RcFrequency.Ticks;
|
||||||
|
if (crowd != null)
|
||||||
|
{
|
||||||
|
crowd.Config().pathQueueSize = _cfg.pathQueueSize;
|
||||||
|
crowd.Config().maxFindPathIterations = _cfg.maxIterations;
|
||||||
|
crowd.Update(dt, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
long endTime = RcFrequency.Ticks;
|
||||||
|
if (crowd != null)
|
||||||
|
{
|
||||||
|
DtNavMeshQuery navquery = new DtNavMeshQuery(navMesh);
|
||||||
|
IDtQueryFilter filter = new DtQueryDefaultFilter();
|
||||||
|
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
||||||
|
{
|
||||||
|
if (NeedsNewTarget(ag))
|
||||||
|
{
|
||||||
|
CrowdAgentData crowAgentData = (CrowdAgentData)ag.option.userData;
|
||||||
|
switch (crowAgentData.type)
|
||||||
|
{
|
||||||
|
case CrowdAgentType.MOB:
|
||||||
|
MoveMob(navquery, filter, ag, crowAgentData);
|
||||||
|
break;
|
||||||
|
case CrowdAgentType.VILLAGER:
|
||||||
|
MoveVillager(navquery, filter, ag, crowAgentData);
|
||||||
|
break;
|
||||||
|
case CrowdAgentType.TRAVELLER:
|
||||||
|
MoveTraveller(navquery, filter, ag, crowAgentData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crowdUpdateTime = (endTime - startTime) / TimeSpan.TicksPerMillisecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MoveMob(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, CrowdAgentData crowAgentData)
|
||||||
|
{
|
||||||
|
// Move somewhere
|
||||||
|
var status = navquery.FindNearestPoly(ag.npos, crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _);
|
||||||
|
if (status.Succeeded())
|
||||||
|
{
|
||||||
|
status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, _cfg.zoneRadius * 2f, filter, rnd,
|
||||||
|
out var randomRef, out var randomPt);
|
||||||
|
if (status.Succeeded())
|
||||||
|
{
|
||||||
|
crowd.RequestMoveTarget(ag, randomRef, randomPt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MoveVillager(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, CrowdAgentData crowAgentData)
|
||||||
|
{
|
||||||
|
// Move somewhere close
|
||||||
|
var status = navquery.FindNearestPoly(ag.npos, crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _);
|
||||||
|
if (status.Succeeded())
|
||||||
|
{
|
||||||
|
status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, _cfg.zoneRadius * 0.2f, filter, rnd,
|
||||||
|
out var randomRef, out var randomPt);
|
||||||
|
if (status.Succeeded())
|
||||||
|
{
|
||||||
|
crowd.RequestMoveTarget(ag, randomRef, randomPt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MoveTraveller(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, CrowdAgentData crowAgentData)
|
||||||
|
{
|
||||||
|
// Move to another zone
|
||||||
|
List<DtPolyPoint> potentialTargets = new List<DtPolyPoint>();
|
||||||
|
foreach (var zone in _polyPoints)
|
||||||
|
{
|
||||||
|
if (RcVec3f.DistSqr(zone.pt, ag.npos) > _cfg.zoneRadius * _cfg.zoneRadius)
|
||||||
|
{
|
||||||
|
potentialTargets.Add(zone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 < potentialTargets.Count)
|
||||||
|
{
|
||||||
|
potentialTargets.Shuffle();
|
||||||
|
crowd.RequestMoveTarget(ag, potentialTargets[0].refs, potentialTargets[0].pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool NeedsNewTarget(DtCrowdAgent ag)
|
||||||
|
{
|
||||||
|
if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE
|
||||||
|
|| ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_FAILED)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VALID)
|
||||||
|
{
|
||||||
|
float dx = ag.targetPos.x - ag.npos.x;
|
||||||
|
float dy = ag.targetPos.y - ag.npos.y;
|
||||||
|
float dz = ag.targetPos.z - ag.npos.z;
|
||||||
|
return dx * dx + dy * dy + dz * dz < 0.3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DtCrowdAgent AddAgent(RcVec3f p, CrowdAgentType type, float agentRadius, float agentHeight, float agentMaxAcceleration, float agentMaxSpeed)
|
||||||
|
{
|
||||||
|
DtCrowdAgentParams ap = GetAgentParams(agentRadius, agentHeight, agentMaxAcceleration, agentMaxSpeed);
|
||||||
|
ap.userData = new CrowdAgentData(type, p);
|
||||||
|
return crowd.AddAgent(p, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateAgentParams()
|
||||||
|
{
|
||||||
|
if (crowd != null)
|
||||||
|
{
|
||||||
|
foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
|
||||||
|
{
|
||||||
|
DtCrowdAgentParams option = new DtCrowdAgentParams();
|
||||||
|
option.radius = ag.option.radius;
|
||||||
|
option.height = ag.option.height;
|
||||||
|
option.maxAcceleration = ag.option.maxAcceleration;
|
||||||
|
option.maxSpeed = ag.option.maxSpeed;
|
||||||
|
option.collisionQueryRange = ag.option.collisionQueryRange;
|
||||||
|
option.pathOptimizationRange = ag.option.pathOptimizationRange;
|
||||||
|
option.queryFilterType = ag.option.queryFilterType;
|
||||||
|
option.userData = ag.option.userData;
|
||||||
|
option.updateFlags = _agCfg.GetUpdateFlags();
|
||||||
|
option.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
||||||
|
option.separationWeight = _agCfg.separationWeight;
|
||||||
|
crowd.UpdateAgentParameters(ag, option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetCrowdUpdateTime()
|
||||||
|
{
|
||||||
|
return crowdUpdateTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@ namespace DotRecast.Recast.Toolset.Tools
|
||||||
{
|
{
|
||||||
public class RcCrowdTool : IRcToolable
|
public class RcCrowdTool : IRcToolable
|
||||||
{
|
{
|
||||||
private readonly CrowdConfig _cfg;
|
private readonly DtCrowdAgentConfig _agCfg;
|
||||||
private DtCrowd crowd;
|
private DtCrowd crowd;
|
||||||
private readonly DtCrowdAgentDebugInfo _agentDebug;
|
private readonly DtCrowdAgentDebugInfo _agentDebug;
|
||||||
private long crowdUpdateTime;
|
private long crowdUpdateTime;
|
||||||
|
@ -20,7 +20,7 @@ namespace DotRecast.Recast.Toolset.Tools
|
||||||
|
|
||||||
public RcCrowdTool()
|
public RcCrowdTool()
|
||||||
{
|
{
|
||||||
_cfg = new CrowdConfig();
|
_agCfg = new DtCrowdAgentConfig();
|
||||||
_agentDebug = new DtCrowdAgentDebugInfo();
|
_agentDebug = new DtCrowdAgentDebugInfo();
|
||||||
_agentDebug.vod = new DtObstacleAvoidanceDebugData(2048);
|
_agentDebug.vod = new DtObstacleAvoidanceDebugData(2048);
|
||||||
_trails = new Dictionary<long, CrowdAgentTrail>();
|
_trails = new Dictionary<long, CrowdAgentTrail>();
|
||||||
|
@ -32,9 +32,9 @@ namespace DotRecast.Recast.Toolset.Tools
|
||||||
return "Crowd Control";
|
return "Crowd Control";
|
||||||
}
|
}
|
||||||
|
|
||||||
public CrowdConfig GetCrowdConfig()
|
public DtCrowdAgentConfig GetCrowdConfig()
|
||||||
{
|
{
|
||||||
return _cfg;
|
return _agCfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DtCrowdAgentDebugInfo GetCrowdAgentDebugInfo()
|
public DtCrowdAgentDebugInfo GetCrowdAgentDebugInfo()
|
||||||
|
@ -119,9 +119,9 @@ namespace DotRecast.Recast.Toolset.Tools
|
||||||
agOption.obstacleAvoidanceType = ag.option.obstacleAvoidanceType;
|
agOption.obstacleAvoidanceType = ag.option.obstacleAvoidanceType;
|
||||||
agOption.queryFilterType = ag.option.queryFilterType;
|
agOption.queryFilterType = ag.option.queryFilterType;
|
||||||
agOption.userData = ag.option.userData;
|
agOption.userData = ag.option.userData;
|
||||||
agOption.updateFlags = _cfg.GetUpdateFlags();
|
agOption.updateFlags = _agCfg.GetUpdateFlags();
|
||||||
agOption.obstacleAvoidanceType = _cfg.obstacleAvoidanceType;
|
agOption.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
||||||
agOption.separationWeight = _cfg.separationWeight;
|
agOption.separationWeight = _agCfg.separationWeight;
|
||||||
crowd.UpdateAgentParameters(ag, agOption);
|
crowd.UpdateAgentParameters(ag, agOption);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,9 +206,9 @@ namespace DotRecast.Recast.Toolset.Tools
|
||||||
ap.maxSpeed = agentMaxSpeed;
|
ap.maxSpeed = agentMaxSpeed;
|
||||||
ap.collisionQueryRange = ap.radius * 12.0f;
|
ap.collisionQueryRange = ap.radius * 12.0f;
|
||||||
ap.pathOptimizationRange = ap.radius * 30.0f;
|
ap.pathOptimizationRange = ap.radius * 30.0f;
|
||||||
ap.updateFlags = _cfg.GetUpdateFlags();
|
ap.updateFlags = _agCfg.GetUpdateFlags();
|
||||||
ap.obstacleAvoidanceType = _cfg.obstacleAvoidanceType;
|
ap.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
|
||||||
ap.separationWeight = _cfg.separationWeight;
|
ap.separationWeight = _agCfg.separationWeight;
|
||||||
return ap;
|
return ap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue