2023-03-14 08:02:43 +03:00
|
|
|
/*
|
|
|
|
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org
|
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any damages
|
|
|
|
arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
|
|
including commercial applications, and to alter it and redistribute it
|
|
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
|
|
claim that you wrote the original software. If you use this software
|
|
|
|
in a product, an acknowledgment in the product documentation would be
|
|
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using DotRecast.Core;
|
|
|
|
using DotRecast.Detour;
|
|
|
|
using DotRecast.Detour.Crowd;
|
|
|
|
using DotRecast.Recast.Demo.Builder;
|
|
|
|
using DotRecast.Recast.Demo.Draw;
|
|
|
|
using Silk.NET.Windowing;
|
|
|
|
using static DotRecast.Recast.Demo.Draw.DebugDraw;
|
|
|
|
|
|
|
|
namespace DotRecast.Recast.Demo.Tools;
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
public class CrowdProfilingTool
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
private readonly Func<CrowdAgentParams> agentParamsSupplier;
|
|
|
|
private readonly int[] expandSimOptions = new[] { 1 };
|
|
|
|
private readonly int[] expandCrowdOptions = new[] { 1 };
|
|
|
|
private readonly int[] agents = new[] { 1000 };
|
|
|
|
private readonly int[] randomSeed = new[] { 270 };
|
|
|
|
private readonly int[] numberOfZones = new[] { 4 };
|
|
|
|
private readonly float[] zoneRadius = new[] { 20f };
|
|
|
|
private readonly float[] percentMobs = new[] { 80f };
|
|
|
|
private readonly float[] percentTravellers = new[] { 15f };
|
|
|
|
private readonly int[] pathQueueSize = new[] { 32 };
|
|
|
|
private readonly int[] maxIterations = new[] { 300 };
|
|
|
|
private Crowd crowd;
|
|
|
|
private NavMesh navMesh;
|
|
|
|
private CrowdConfig config;
|
|
|
|
private NavMeshQuery.FRand rnd;
|
|
|
|
private readonly List<FindRandomPointResult> zones = new();
|
|
|
|
private long crowdUpdateTime;
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
public CrowdProfilingTool(Func<CrowdAgentParams> agentParamsSupplier)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
this.agentParamsSupplier = agentParamsSupplier;
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
public void layout(IWindow ctx)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
// nk_layout_row_dynamic(ctx, 1, 1);
|
|
|
|
// nk_spacing(ctx, 1);
|
|
|
|
// if (nk_tree_state_push(ctx, 0, "Simulation Options", expandSimOptions)) {
|
|
|
|
// nk_layout_row_dynamic(ctx, 20, 1);
|
|
|
|
// nk_property_int(ctx, "Agents", 0, agents, 10000, 1, 1);
|
|
|
|
// nk_layout_row_dynamic(ctx, 20, 1);
|
|
|
|
// nk_property_int(ctx, "Random Seed", 0, randomSeed, 1024, 1, 1);
|
|
|
|
// nk_layout_row_dynamic(ctx, 20, 1);
|
|
|
|
// nk_property_int(ctx, "Number of Zones", 0, numberOfZones, 10, 1, 1);
|
|
|
|
// nk_layout_row_dynamic(ctx, 20, 1);
|
|
|
|
// nk_property_float(ctx, "Zone Radius", 0, zoneRadius, 100, 1, 1);
|
|
|
|
// nk_layout_row_dynamic(ctx, 20, 1);
|
|
|
|
// nk_property_float(ctx, "Mobs %", 0, percentMobs, 100, 1, 1);
|
|
|
|
// nk_layout_row_dynamic(ctx, 20, 1);
|
|
|
|
// nk_property_float(ctx, "Travellers %", 0, percentTravellers, 100, 1, 1);
|
|
|
|
// nk_tree_state_pop(ctx);
|
|
|
|
// }
|
|
|
|
// if (nk_tree_state_push(ctx, 0, "Crowd Options", expandCrowdOptions)) {
|
|
|
|
// nk_layout_row_dynamic(ctx, 20, 1);
|
|
|
|
// nk_property_int(ctx, "Path Queue Size", 0, pathQueueSize, 1024, 1, 1);
|
|
|
|
// nk_layout_row_dynamic(ctx, 20, 1);
|
|
|
|
// nk_property_int(ctx, "Max Iterations", 0, maxIterations, 4000, 1, 1);
|
|
|
|
// nk_tree_state_pop(ctx);
|
|
|
|
// }
|
|
|
|
// nk_layout_row_dynamic(ctx, 1, 1);
|
|
|
|
// nk_spacing(ctx, 1);
|
|
|
|
// nk_layout_row_dynamic(ctx, 20, 1);
|
|
|
|
// if (nk_button_text(ctx, "Start")) {
|
|
|
|
// if (navMesh != null) {
|
|
|
|
// rnd = new NavMeshQuery.FRand(randomSeed[0]);
|
|
|
|
// createCrowd();
|
|
|
|
// createZones();
|
|
|
|
// NavMeshQuery navquery = new NavMeshQuery(navMesh);
|
|
|
|
// QueryFilter filter = new DefaultQueryFilter();
|
|
|
|
// for (int i = 0; i < agents[0]; i++) {
|
|
|
|
// float tr = rnd.frand();
|
|
|
|
// AgentType type = AgentType.MOB;
|
|
|
|
// float mobsPcnt = percentMobs[0] / 100f;
|
|
|
|
// if (tr > mobsPcnt) {
|
|
|
|
// tr = rnd.frand();
|
|
|
|
// float travellerPcnt = percentTravellers[0] / 100f;
|
|
|
|
// if (tr > travellerPcnt) {
|
|
|
|
// type = AgentType.VILLAGER;
|
|
|
|
// } else {
|
|
|
|
// type = AgentType.TRAVELLER;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// float[] pos = null;
|
|
|
|
// switch (type) {
|
|
|
|
// case MOB:
|
|
|
|
// pos = getMobPosition(navquery, filter, pos);
|
|
|
|
// break;
|
|
|
|
// case VILLAGER:
|
|
|
|
// pos = getVillagerPosition(navquery, filter, pos);
|
|
|
|
// break;
|
|
|
|
// case TRAVELLER:
|
|
|
|
// pos = getVillagerPosition(navquery, filter, pos);
|
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
// if (pos != null) {
|
|
|
|
// addAgent(pos, type);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// if (crowd != null) {
|
|
|
|
// nk_layout_row_dynamic(ctx, 18, 1);
|
|
|
|
// nk_label(ctx, string.format("Max time to enqueue request: %.3f s", crowd.telemetry().maxTimeToEnqueueRequest()),
|
|
|
|
// NK_TEXT_ALIGN_LEFT);
|
|
|
|
// nk_layout_row_dynamic(ctx, 18, 1);
|
|
|
|
// nk_label(ctx, string.format("Max time to find path: %.3f s", crowd.telemetry().maxTimeToFindPath()),
|
|
|
|
// NK_TEXT_ALIGN_LEFT);
|
|
|
|
// List<Tuple<string, long>> timings = crowd.telemetry().executionTimings().entrySet().stream()
|
|
|
|
// .map(e => Tuple.Create(e.getKey(), e.getValue())).sorted((t1, t2) => long.compare(t2.Item2, t1.Item2))
|
|
|
|
// .collect(toList());
|
|
|
|
// foreach (Tuple<string, long> e in timings) {
|
|
|
|
// nk_layout_row_dynamic(ctx, 18, 1);
|
|
|
|
// nk_label(ctx, string.format("%s: %d us", e.Item1, e.Item2 / 1_000), NK_TEXT_ALIGN_LEFT);
|
|
|
|
// }
|
|
|
|
// nk_layout_row_dynamic(ctx, 1, 1);
|
|
|
|
// nk_spacing(ctx, 1);
|
|
|
|
// nk_layout_row_dynamic(ctx, 18, 1);
|
|
|
|
// nk_label(ctx, string.format("Update Time: %d ms", crowdUpdateTime), NK_TEXT_ALIGN_LEFT);
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private float[] getMobPosition(NavMeshQuery navquery, QueryFilter filter, float[] pos)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
Result<FindRandomPointResult> result = navquery.findRandomPoint(filter, rnd);
|
2023-03-16 19:48:49 +03:00
|
|
|
if (result.succeeded())
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
pos = result.result.getRandomPt();
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
|
2023-03-14 08:02:43 +03:00
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private float[] getVillagerPosition(NavMeshQuery navquery, QueryFilter filter, float[] pos)
|
|
|
|
{
|
|
|
|
if (0 < zones.Count)
|
|
|
|
{
|
|
|
|
int zone = (int)(rnd.frand() * zones.Count);
|
2023-03-14 08:02:43 +03:00
|
|
|
Result<FindRandomPointResult> result = navquery.findRandomPointWithinCircle(zones[zone].getRandomRef(),
|
2023-03-16 19:48:49 +03:00
|
|
|
zones[zone].getRandomPt(), zoneRadius[0], filter, rnd);
|
|
|
|
if (result.succeeded())
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
pos = result.result.getRandomPt();
|
|
|
|
}
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
|
2023-03-14 08:02:43 +03:00
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private void createZones()
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
zones.Clear();
|
|
|
|
QueryFilter filter = new DefaultQueryFilter();
|
|
|
|
NavMeshQuery navquery = new NavMeshQuery(navMesh);
|
2023-03-16 19:48:49 +03:00
|
|
|
for (int i = 0; i < numberOfZones[0]; i++)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
float zoneSeparation = zoneRadius[0] * zoneRadius[0] * 16;
|
2023-03-16 19:48:49 +03:00
|
|
|
for (int k = 0; k < 100; k++)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
Result<FindRandomPointResult> result = navquery.findRandomPoint(filter, rnd);
|
2023-03-16 19:48:49 +03:00
|
|
|
if (result.succeeded())
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
bool valid = true;
|
2023-03-16 19:48:49 +03:00
|
|
|
foreach (FindRandomPointResult zone in zones)
|
|
|
|
{
|
|
|
|
if (DemoMath.vDistSqr(zone.getRandomPt(), result.result.getRandomPt(), 0) < zoneSeparation)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
valid = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
|
|
|
|
if (valid)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
zones.Add(result.result);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private void createCrowd()
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
crowd = new Crowd(config, navMesh, __ => new DefaultQueryFilter(SampleAreaModifications.SAMPLE_POLYFLAGS_ALL,
|
2023-03-16 19:48:49 +03:00
|
|
|
SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED, new float[] { 1f, 10f, 1f, 1f, 2f, 1.5f }));
|
2023-03-14 08:02:43 +03:00
|
|
|
|
|
|
|
ObstacleAvoidanceQuery.ObstacleAvoidanceParams option = new ObstacleAvoidanceQuery.ObstacleAvoidanceParams(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);
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
public void update(float dt)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
long startTime = Stopwatch.GetTimestamp();
|
2023-03-16 19:48:49 +03:00
|
|
|
if (crowd != null)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
crowd.config().pathQueueSize = pathQueueSize[0];
|
|
|
|
crowd.config().maxFindPathIterations = maxIterations[0];
|
|
|
|
crowd.update(dt, null);
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
long endTime = Stopwatch.GetTimestamp();
|
|
|
|
if (crowd != null)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
NavMeshQuery navquery = new NavMeshQuery(navMesh);
|
|
|
|
QueryFilter filter = new DefaultQueryFilter();
|
2023-03-16 19:48:49 +03:00
|
|
|
foreach (CrowdAgent ag in crowd.getActiveAgents())
|
|
|
|
{
|
|
|
|
if (needsNewTarget(ag))
|
|
|
|
{
|
|
|
|
AgentData agentData = (AgentData)ag.option.userData;
|
|
|
|
switch (agentData.type)
|
|
|
|
{
|
|
|
|
case AgentType.MOB:
|
|
|
|
moveMob(navquery, filter, ag, agentData);
|
|
|
|
break;
|
|
|
|
case AgentType.VILLAGER:
|
|
|
|
moveVillager(navquery, filter, ag, agentData);
|
|
|
|
break;
|
|
|
|
case AgentType.TRAVELLER:
|
|
|
|
moveTraveller(navquery, filter, ag, agentData);
|
|
|
|
break;
|
2023-03-14 08:02:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
|
2023-03-14 08:02:43 +03:00
|
|
|
crowdUpdateTime = (endTime - startTime) / 1_000_000;
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private void moveMob(NavMeshQuery navquery, QueryFilter filter, CrowdAgent ag, AgentData agentData)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
// Move somewhere
|
|
|
|
Result<FindNearestPolyResult> nearestPoly = navquery.findNearestPoly(ag.npos, crowd.getQueryExtents(), filter);
|
2023-03-16 19:48:49 +03:00
|
|
|
if (nearestPoly.succeeded())
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
Result<FindRandomPointResult> result = navquery.findRandomPointAroundCircle(nearestPoly.result.getNearestRef(),
|
2023-03-16 19:48:49 +03:00
|
|
|
agentData.home, zoneRadius[0] * 2f, filter, rnd);
|
|
|
|
if (result.succeeded())
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
crowd.requestMoveTarget(ag, result.result.getRandomRef(), result.result.getRandomPt());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private void moveVillager(NavMeshQuery navquery, QueryFilter filter, CrowdAgent ag, AgentData agentData)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
// Move somewhere close
|
|
|
|
Result<FindNearestPolyResult> nearestPoly = navquery.findNearestPoly(ag.npos, crowd.getQueryExtents(), filter);
|
2023-03-16 19:48:49 +03:00
|
|
|
if (nearestPoly.succeeded())
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
Result<FindRandomPointResult> result = navquery.findRandomPointAroundCircle(nearestPoly.result.getNearestRef(),
|
2023-03-16 19:48:49 +03:00
|
|
|
agentData.home, zoneRadius[0] * 0.2f, filter, rnd);
|
|
|
|
if (result.succeeded())
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
crowd.requestMoveTarget(ag, result.result.getRandomRef(), result.result.getRandomPt());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private void moveTraveller(NavMeshQuery navquery, QueryFilter filter, CrowdAgent ag, AgentData agentData)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
// Move to another zone
|
|
|
|
List<FindRandomPointResult> potentialTargets = new();
|
2023-03-16 19:48:49 +03:00
|
|
|
foreach (FindRandomPointResult zone in zones)
|
|
|
|
{
|
|
|
|
if (DemoMath.vDistSqr(zone.getRandomPt(), ag.npos, 0) > zoneRadius[0] * zoneRadius[0])
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
potentialTargets.Add(zone);
|
|
|
|
}
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
|
|
|
|
if (0 < potentialTargets.Count)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
potentialTargets.Shuffle();
|
|
|
|
crowd.requestMoveTarget(ag, potentialTargets[0].getRandomRef(), potentialTargets[0].getRandomPt());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private bool needsNewTarget(CrowdAgent ag)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
if (ag.targetState == CrowdAgent.MoveRequestState.DT_CROWDAGENT_TARGET_NONE
|
2023-03-16 19:48:49 +03:00
|
|
|
|| ag.targetState == CrowdAgent.MoveRequestState.DT_CROWDAGENT_TARGET_FAILED)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
return true;
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
|
|
|
|
if (ag.targetState == CrowdAgent.MoveRequestState.DT_CROWDAGENT_TARGET_VALID)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
float dx = ag.targetPos[0] - ag.npos[0];
|
|
|
|
float dy = ag.targetPos[1] - ag.npos[1];
|
|
|
|
float dz = ag.targetPos[2] - ag.npos[2];
|
|
|
|
return dx * dx + dy * dy + dz * dz < 0.3f;
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
|
2023-03-14 08:02:43 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
public void setup(float maxAgentRadius, NavMesh nav)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
navMesh = nav;
|
2023-03-16 19:48:49 +03:00
|
|
|
if (nav != null)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
config = new CrowdConfig(maxAgentRadius);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
public void handleRender(NavMeshRenderer renderer)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
RecastDebugDraw dd = renderer.getDebugDraw();
|
|
|
|
dd.depthMask(false);
|
2023-03-16 19:48:49 +03:00
|
|
|
if (crowd != null)
|
|
|
|
{
|
|
|
|
foreach (CrowdAgent ag in crowd.getActiveAgents())
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
float radius = ag.option.radius;
|
|
|
|
float[] pos = ag.npos;
|
|
|
|
dd.debugDrawCircle(pos[0], pos[1], pos[2], radius, duRGBA(0, 0, 0, 32), 2.0f);
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
foreach (CrowdAgent ag in crowd.getActiveAgents())
|
|
|
|
{
|
|
|
|
AgentData agentData = (AgentData)ag.option.userData;
|
2023-03-14 08:02:43 +03:00
|
|
|
|
|
|
|
float height = ag.option.height;
|
|
|
|
float radius = ag.option.radius;
|
|
|
|
float[] pos = ag.npos;
|
|
|
|
|
|
|
|
int col = duRGBA(220, 220, 220, 128);
|
2023-03-16 19:48:49 +03:00
|
|
|
if (agentData.type == AgentType.TRAVELLER)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
col = duRGBA(100, 160, 100, 128);
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
|
|
|
|
if (agentData.type == AgentType.VILLAGER)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
col = duRGBA(120, 80, 160, 128);
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
|
2023-03-14 08:02:43 +03:00
|
|
|
if (ag.targetState == CrowdAgent.MoveRequestState.DT_CROWDAGENT_TARGET_REQUESTING
|
2023-03-16 19:48:49 +03:00
|
|
|
|| ag.targetState == CrowdAgent.MoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE)
|
2023-03-14 08:02:43 +03:00
|
|
|
col = duLerpCol(col, duRGBA(255, 255, 32, 128), 128);
|
|
|
|
else if (ag.targetState == CrowdAgent.MoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_PATH)
|
|
|
|
col = duLerpCol(col, duRGBA(255, 64, 32, 128), 128);
|
|
|
|
else if (ag.targetState == CrowdAgent.MoveRequestState.DT_CROWDAGENT_TARGET_FAILED)
|
|
|
|
col = duRGBA(255, 32, 16, 128);
|
|
|
|
else if (ag.targetState == CrowdAgent.MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY)
|
|
|
|
col = duLerpCol(col, duRGBA(64, 255, 0, 128), 128);
|
|
|
|
|
|
|
|
dd.debugDrawCylinder(pos[0] - radius, pos[1] + radius * 0.1f, pos[2] - radius, pos[0] + radius, pos[1] + height,
|
2023-03-16 19:48:49 +03:00
|
|
|
pos[2] + radius, col);
|
2023-03-14 08:02:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dd.depthMask(true);
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private CrowdAgent addAgent(float[] p, AgentType type)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
CrowdAgentParams ap = agentParamsSupplier.Invoke();
|
|
|
|
ap.userData = new AgentData(type, p);
|
|
|
|
return crowd.addAgent(p, ap);
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
public enum AgentType
|
|
|
|
{
|
|
|
|
VILLAGER,
|
|
|
|
TRAVELLER,
|
|
|
|
MOB,
|
2023-03-14 08:02:43 +03:00
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
private class AgentData
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
public readonly AgentType type;
|
|
|
|
public readonly float[] home = new float[3];
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
public AgentData(AgentType type, float[] home)
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
this.type = type;
|
|
|
|
RecastVectors.copy(this.home, home);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-16 19:48:49 +03:00
|
|
|
public void updateAgentParams(int updateFlags, int obstacleAvoidanceType, float separationWeight)
|
|
|
|
{
|
|
|
|
if (crowd != null)
|
|
|
|
{
|
|
|
|
foreach (CrowdAgent ag in crowd.getActiveAgents())
|
|
|
|
{
|
2023-03-14 08:02:43 +03:00
|
|
|
CrowdAgentParams option = new CrowdAgentParams();
|
|
|
|
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 = updateFlags;
|
|
|
|
option.obstacleAvoidanceType = obstacleAvoidanceType;
|
|
|
|
option.separationWeight = separationWeight;
|
|
|
|
crowd.updateAgentParameters(ag, option);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-16 19:48:49 +03:00
|
|
|
}
|