gamekit/Tutorial_CrowdDispersion.cpp

gamekit/Tutorial_CrowdDispersion.cpp
/*
* Copyright 2015 Autodesk, Inc. All rights reserved.
* Use of this software is subject to the terms of the Autodesk license agreement and any attachments or Appendices thereto provided at the time of installation or download,
* or which otherwise accompanies this software in either electronic or hard copy form, or which is signed by you and accepted by Autodesk.
*/
#include "common/oneworldenv.h"
#include "LabEngine/base/kaimfileopener.h"
//RUN_THIS_FILE
// This tutorial shows how to obtain Crowd Dispersion with the GameKit.
// Bots' paths will redistributed through several NavTag during PathFollowing.
namespace
{
class CrowdDispersionBot : public LabEngine::LabEngineObject
{
public:
CrowdDispersionBot(Kaim::GameKitCrowdDispersionNavTagHashMap* crowdDispersionNavTagMap, Kaim::Database* database, const Kaim::Vec3f& startOffset, KyUInt32 startIdx, KyUInt32 destIdx, KyFloat32 checkDistance, Kaim::BotConfig botConfig, KyUInt32 navigationProfileId)
{
m_pos[0] = Kaim::Vec3f(30.0f, 12.0f, 10.0f);
m_pos[1] = Kaim::Vec3f( 0.0f, 12.0f, 0.0f);
m_pos[2] = Kaim::Vec3f(15.0f, -5.0f, 0.0f);
m_pos[3] = Kaim::Vec3f( 3.0f, -3.0f, 0.0f);
m_pos[4] = Kaim::Vec3f( 2.0f, 25.0f, 0.0f);
m_pos[5] = Kaim::Vec3f(15.0f, 30.0f, 0.0f);
m_gameKitTraverseLogicData = *KY_NEW Kaim::GameKitTraverseLogicData();
m_gameKitTraverseLogicData->EnableCrowdDispersion(crowdDispersionNavTagMap, checkDistance, 1.f);
m_currentDestIdx = destIdx;
Kaim::BotInitConfig botInitConfig;
botInitConfig.m_database = database;
botInitConfig.m_startPosition = m_pos[startIdx] + startOffset;
botInitConfig.m_startNewPathNavigationProfileId = navigationProfileId;
m_navBot = *KY_NEW Kaim::Bot;
botConfig.m_maxDesiredLinearSpeed = 4.f+2.f*Kaim::Alg::Random::GetUnitFloat();
m_navBot->Init(botInitConfig, botConfig);
m_navBot->SetBotTraverseLogicUserData(m_gameKitTraverseLogicData);
m_navBot->AddToDatabase();
m_navBot->ComputeNewPathToDestination(m_pos[m_currentDestIdx]);
}
void UpdateLogic()
{
if (m_navBot->IsAddedToWorld() == false)
return;
const Kaim::BotOutput& botOutput = m_navBot->GetBotOutput();
// ----- PathFollow new destination logic -----
if (m_navBot->GetFollowedPath() != KY_NULL && m_navBot->HasReachedPosition(m_pos[m_currentDestIdx], 1.f)) // arrival
{
m_gameKitTraverseLogicData->GetCrowdDispersionData()->OnBotLogicComputePathToBrandNewDestination(); // Brand new destination
m_navBot->RemoveFromDatabase();
return;
}
// ----- CrowdDispersion logic -----
Kaim::GameKitCrowdDispersionLogicAction action = m_gameKitTraverseLogicData->GetCrowdDispersionData()->UpdateBotLogic(m_navBot);
{
if (m_navBot->IsComputingNewPath() == false)
return;
m_navBot->ComputeNewPathToDestination(m_pos[m_currentDestIdx]);
return;
}
{
if (m_navBot->IsComputingNewPath() == true)
m_navBot->CancelAsyncPathComputation();
}
// ----- PathFollow failure Logic -----
// if no change in destination but Navigation cannot continue to follow the current path, re-path!
bool needRecomputation = botOutput.IsPathRecomputationNeeded();
KY_TODO("Why not to put such kind of small independent brick of logic into the GamKit")
// Try to Prevent walking back, this typically occurs due to DA.
// So, if following current path requires to walk back, recompute as well.
if (needRecomputation == false && m_navBot->GetTrajectory())
{
Kaim::Trajectory* trajectory = m_navBot->GetTrajectory();
if (trajectory->GetTrajectoryMode() == Kaim::TrajectoryMode_Shortcut)
{
const Kaim::PositionOnLivePath& targetOnLivePath = trajectory->m_shortcutTrajectory->m_targetOnPath.m_positionOnLivePath;
if (targetOnLivePath.IsStrictlyBefore(m_navBot->GetProgressOnLivePath()))
{
Kaim::Vec2f velocity, targetDir;
botOutput.m_outputVelocity.GetNormalized2d(velocity);
(targetOnLivePath.GetPosition() - m_navBot->GetPosition()).GetNormalized2d(targetDir);
if (Kaim::DotProduct(velocity, targetDir) < -0.01f)
needRecomputation = true;
}
}
}
if (needRecomputation == true)
{
m_gameKitTraverseLogicData->GetCrowdDispersionData()->OnBotLogicRecomputePathToSimilarDestination();
if (m_navBot->IsComputingNewPath() == true)
return;
m_navBot->ComputeNewPathToDestination(m_pos[m_currentDestIdx]);
return;
}
}
void UpdatePhysics()
{
if (m_navBot->IsAddedToWorld() == false)
return;
const KyFloat32 simulationTimeInSeconds = 0.016f;
const Kaim::BotOutput& botOutput = m_navBot->GetBotOutput();
if (botOutput.m_outputVelocity.GetSquareLength() <= 0.0f)
{
m_navBot->SetVelocity(Kaim::Vec3f::Zero());
return;
}
// if navBot on a triangle and expected to be in NavMesh, move on NavMesh
if ((m_navBot->GetNavTrianglePtr().IsValid() == true) &&
(m_navBot->GetProgressOnLivePath().GetPathEdgeType() == Kaim::PathEdgeType_OnNavMesh))
{
Kaim::Vec3f position = m_navBot->ComputeMoveOnNavMeshWithTraverseLogic<Kaim::DefaultTraverseLogic>(botOutput.m_outputVelocity, simulationTimeInSeconds);
m_navBot->SetPositionAndVelocityAndFrontDirection(position, simulationTimeInSeconds);
return;
}
// otherwise move in straight line
m_navBot->SetPosition(m_navBot->GetPosition() + botOutput.m_outputVelocity * simulationTimeInSeconds);
m_navBot->SetVelocityAndFrontDirection(botOutput.m_outputVelocity);
}
void SendVisualDebug()
{
Kaim::ScopedDisplayList displayList(m_navBot->GetWorld());
displayList.InitSingleFrameLifespan("CostMultiplier", "CrowdDispersion", m_navBot->GetVisualDebugId());
m_gameKitTraverseLogicData->GetCrowdDispersionData()->SendVisualDebug(displayList);
Kaim::Vec3f textPos(m_navBot->GetPosition());
textPos.z = 10.f;
displayList.PushTextVarg(textPos, Kaim::VisualColor::Red, "%.3f", 1.f / m_gameKitTraverseLogicData->GetCrowdDispersionData()->m_input.m_speed);
}
public:
Kaim::Ptr<Kaim::Bot> m_navBot;
Kaim::Ptr<Kaim::GameKitTraverseLogicData> m_gameKitTraverseLogicData;
KyUInt32 m_currentDestIdx;
Kaim::Vec3f m_pos[6];
};
class GraphEdges
{
public:
GraphEdges() {}
GraphEdges(Kaim::Vec3f a, Kaim::Vec3f b, KyUInt32 navTagID, const Kaim::DatabaseGenMetrics& /*genMetrics*/)
: m_a(a), m_b(b), m_maxBotOnEdge(1)
{
// select vertical or horizontal axis where the more bots can be put without overlapping
//KyFloat32 horizontal = Kaim::Distance2d(a,b) / ((genMetrics.GetGenerationRadius()*2.f) *2.f);
//KyFloat32 vertical = fabsf(a.z-b.z) / (genMetrics.GetGenerationHeight() *2.f);
//m_maxBotOnEdge = KyUInt32(Kaim::Max(1.f, (Kaim::Max(horizontal, vertical))));
m_navTag.SetLayerID(0); // let's say that layer 0 means crowd dispersion layer
m_navTag.SetSmartObjectID(navTagID); // unique identifier of smartobject
}
KyUInt32 m_maxBotOnEdge;
};
class CrowdDispersionEnv : public OneWorldEnv
{
public:
CrowdDispersionEnv()
: m_enableVisualDebug(true)
, m_botColumn(10)
, m_botLine(10)
, m_tagVolumeMaxCount(4)
, m_crowdDispersionCheckDistance(20.f)
, m_denseGraph(false)
, m_noTimeSlicing(false)
{
m_botConfig.m_shortcutTrajectoryConfig.m_advancedConfig.m_forwardMaxDistance = 5.f;
m_botConfig.m_shortcutTrajectoryConfig.m_advancedConfig.m_backwardMaxDistance = 0.5f;
m_botConfig.m_avoidanceConfig.m_minimalTimeToCollision = 0.2f;
m_botConfig.m_avoidanceConfig.m_enableSlowingDown = true;
m_botConfig.m_avoidanceConfig.m_stopCollisionTime = 0.1f;
m_botConfig.m_avoidanceConfig.m_stopWaitTime = 0.1f;
m_botConfig.m_avoidanceConfig.m_waitPassageTimeLimit = 0.1f;
m_botConfig.m_avoidanceConfig.m_utilityFunctionConfig.m_avoidanceAngleSpan = 180;
m_botConfig.m_avoidanceConfig.m_utilityFunctionConfig.m_avoidanceSampleCount = 10;
Kaim::Alg::Random::SeedRandom(0);
}
virtual bool OnBeginEnv()
{
if (OneWorldEnv::OnBeginEnv() == false)
return false;
GetWorld()->SetNumberOfFramesForShortcutTrajectoryPeriodicUpdate(1);
//Kaim::Alg::Random::SeedRandom(0);
return true;
}
bool TestDown(KyUInt32 destIdx)
{
std::stringstream strstream;
strstream << GetName() << "_Down_" << destIdx;
KT_LOG_TITLE_BEGIN(strstream.str().c_str());
ScopedCleanUpWorld scopedCleanUpWorld(this);
KT_RETURN_IF(LoadLevel() != KY_SUCCESS, KY_ERROR);
// Spawn Bots
const KyUInt32 botCount = m_botColumn * m_botLine;
Kaim::Database* database = GetDefaultDatabase();
for (KyUInt32 i = 0; i < m_botColumn; ++i)
{
for (KyUInt32 j = 0; j < m_botLine; ++j)
{
bots[i+(j*m_botColumn)] = *KY_NEW CrowdDispersionBot(&m_crowdDispersionNavTagMap, database,
Kaim::Vec3f(-2.5f + 0.6f*KyFloat32(j), (0.6f*m_botColumn/2.f)-(0.6f*KyFloat32(i)), 0.0f),
0, destIdx, m_crowdDispersionCheckDistance, m_botConfig, m_navigationProfileId);
bots[i+(j*m_botColumn)]->m_gameKitTraverseLogicData->GetCrowdDispersionData()->m_triggerAvoidance = m_botConfig.m_enableAvoidance;
}
}
return UpdateTest(bots, strstream);
}
bool TestUp(const KyUInt32 startIdx[], KyUInt32 startIdxCount)
{
std::stringstream strstream;
strstream << GetName() << "_Up_";
for (KyUInt32 i = 0; i < startIdxCount; ++i)
strstream << startIdx[i] << "-";
KT_LOG_TITLE_BEGIN(strstream.str().c_str());
ScopedCleanUpWorld scopedCleanUpWorld(this);
KT_RETURN_IF(LoadLevel() != KY_SUCCESS, KY_ERROR);
// Spawn Bots
const KyUInt32 botCount = m_botColumn * m_botLine * startIdxCount;
Kaim::Database* database = GetDefaultDatabase();
for (KyUInt32 s = 0; s < startIdxCount; ++s)
{
for (KyUInt32 i = 0; i < m_botColumn; ++i)
{
for (KyUInt32 j = 0; j < m_botLine; ++j)
{
bots[(s*m_botColumn*m_botLine)+i+(j*m_botColumn)] = *KY_NEW CrowdDispersionBot(&m_crowdDispersionNavTagMap, database,
Kaim::Vec3f(1.f-0.8f*KyFloat32(j), (0.8f*m_botColumn/2.f)-(0.8f*KyFloat32(i)), 0.0f),
startIdx[s], 0, m_crowdDispersionCheckDistance, m_botConfig, m_navigationProfileId);
bots[(s*m_botColumn*m_botLine)+i+(j*m_botColumn)]->m_gameKitTraverseLogicData->GetCrowdDispersionData()->m_triggerAvoidance = m_botConfig.m_enableAvoidance;
}
}
}
return UpdateTest(bots, strstream);
}
private:
bool UpdateTest(Kaim::KyArray<Kaim::Ptr<CrowdDispersionBot> >& bots, std::stringstream& strstream)
{
strstream << ".VisualDebug";
Kaim::VisualDebugServer* visualDebugServer = KY_NULL;
if (m_enableVisualDebug)
{
Kaim::VisualDebugServerConfig visualDebugServerConfig;
visualDebugServerConfig.SetLocalFileSizeLimit(1024);
visualDebugServer = StartVisualDebugUsingLocalFile(strstream.str(), visualDebugServerConfig); // Load this file in the NavigationLab to view the result of this tutorial.
}
KyUInt32 frameCount = 2000;
while(--frameCount && GetDefaultDatabase()->GetBotsCount() != 0)
{
if (visualDebugServer)
visualDebugServer->NewFrame();
for (KyUInt32 i = 0; i < bots.GetCount(); ++i)
bots[i]->UpdateLogic();
VisualDebugNavTagCountHashMap("AfterLogic");
for (KyUInt32 i = 0; i < bots.GetCount(); ++i)
bots[i]->SendVisualDebug();
m_world->Update();
for (KyUInt32 i = 0; i < bots.GetCount(); ++i)
bots[i]->UpdatePhysics();
}
KT_RETURN_IF(frameCount == 0, false);
return true;
}
KyResult LoadLevel()
{
if (m_noTimeSlicing == true)
{
for (KyUInt32 i = 0; i < GetWorld()->GetQueryQueueArraysCount(); ++i)
{
Kaim::QueryQueueArray* queryQueueArray = GetWorld()->GetQueryQueueArray(i);
for (KyUInt32 j = 0; j < queryQueueArray->GetQueueCount(); ++j)
{
queryQueueArray->GetQueue(j)->SetBudgetMs(KyFloat32MAXVAL);
}
}
}
m_crowdDispersionNavTagMap.Clear();
m_graphEdges.Clear();
m_volumeExtents.Clear();
KT_RETURN_IF(LoadAndAddNavData("GeneratedNavData/lengthenStairs/lengthenStairs.NavData") == KY_NULL, KY_ERROR);
Kaim::Database* database = GetDefaultDatabase();
const Kaim::DatabaseGenMetrics& genMetrics = database->GetDatabaseGenMetrics();
if (m_denseGraph == false)
{
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(18.0f, 9.0f, 0.0f), Kaim::Vec3f(21.0f, 9.0f, 5.0f), 10, genMetrics));
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(18.0f, 16.0f, 0.0f), Kaim::Vec3f(21.0f, 16.0f, 5.0f), 11, genMetrics));
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(13.0f, 16.0f, 0.0f), Kaim::Vec3f(13.0f, 13.0f, 1.5f), 12, genMetrics));
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(13.0f, 9.0f, 0.0f), Kaim::Vec3f(13.0f, 13.0f, 1.5f), 13, genMetrics));
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(17.0f, 16.0f, 0.0f), Kaim::Vec3f(17.0f, 13.0f, 3.75f), 14, genMetrics));
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(17.0f, 9.0f, 0.0f), Kaim::Vec3f(17.0f, 13.0f, 3.75f), 15, genMetrics));
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(22.0f, 6.0f, 5.0f), Kaim::Vec3f(23.5f, 6.0f, 10.0f), 16, genMetrics));
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(22.0f, 12.0f, 5.0f), Kaim::Vec3f(23.5f, 12.0f, 10.0f), 16, genMetrics));
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(22.0f, 19.0f, 5.0f), Kaim::Vec3f(23.5f, 19.0f, 10.0f), 16, genMetrics));
}
else
{
KyUInt32 i = 10;
for (KyFloat32 y = 15.0f; y <= 19.0f; y += 0.5f)
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(18.0f, y, 0.0f), Kaim::Vec3f(21.0f, y, 5.0f), i++, genMetrics));
for (KyFloat32 y = 6.0f; y <= 10.0f; y += 0.5f)
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(18.0f, y, 0.0f), Kaim::Vec3f(21.0f, y, 5.0f), i++, genMetrics));
for (KyFloat32 y = 6.0f; y <= 19.0f; y += 0.5f)
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(22.0f, y, 5.0f), Kaim::Vec3f(23.5f, y, 10.0f), i++, genMetrics));
for (KyFloat32 x = 0.0f; x <= 4.0f; x += 0.5f)
{
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(13.0f+x, 9.0f, 0.0f), Kaim::Vec3f(13.0f+x, 13.0f, 1.5f+x*0.5f), i++, genMetrics));
m_graphEdges.PushBack(GraphEdges(Kaim::Vec3f(13.0f+x, 16.0f, 0.0f), Kaim::Vec3f(13.0f+x, 13.0f, 1.5f+x*0.5f), i++, genMetrics));
}
}
m_volumeExtents.PushBack(Kaim::Vec2f(11.0f, 14.0f));
m_volumeExtents.PushBack(Kaim::Vec2f(11.5f, 14.0f));
m_volumeExtents.PushBack(Kaim::Vec2f(11.5f, 12.0f));
m_volumeExtents.PushBack(Kaim::Vec2f(11.0f, 12.0f));
// Create a NavigationProfile that will be used by our bot and register it into the World
Kaim::Ptr<Kaim::GameKitNavigationProfile> gameKitNavigationProfile = *KY_NEW Kaim::GameKitNavigationProfile;
m_navigationProfileId = GetWorld()->AddNavigationProfile(gameKitNavigationProfile);
// Create NavGraphs
Kaim::Ptr<Kaim::NavData> navDataForNavGraph = *KY_NEW Kaim::NavData(database);
{
Kaim::NavGraphBlobBuilder navGraphBuilder;
for (KyUInt32 i = 0; i < m_graphEdges.GetCount(); ++i)
{
KyUInt32 navTagIdx = navGraphBuilder.AddNavTag(&m_graphEdges[i].m_navTag.GetDynamicNavTag());
KyUInt32 vertexA = navGraphBuilder.AddVertexWithoutNavTag(m_graphEdges[i].m_a);
KyUInt32 vertexB = navGraphBuilder.AddVertexWithoutNavTag(m_graphEdges[i].m_b);
navGraphBuilder.AddBidirectionalEdgeWithNavTag(vertexA, vertexB, navTagIdx);
}
navGraphBuilder.Build(navGraphBlobHandler);
KT_RETURN_IF(navDataForNavGraph->AddNavGraph(&navGraphBuilder) == KY_ERROR, KY_ERROR);
m_crowdDispersionNavTagMap.Add(navDataForNavGraph);
}
KT_RETURN_IF(navDataForNavGraph->AddToDatabaseImmediate() == KY_ERROR, KY_ERROR);
Kaim::Ptr<Kaim::TagVolume> tagVolume = *KY_NEW Kaim::TagVolume;
{
Kaim::GameKitNavTag gameKitNavTag;
gameKitNavTag.SetLayerID(0);
gameKitNavTag.SetSmartObjectID(0);
Kaim::TagVolumeInitConfig tagVolumeInitConfig;
tagVolumeInitConfig.m_world = GetWorld();
tagVolumeInitConfig.m_points.PushBack(m_volumeExtents[0]);
tagVolumeInitConfig.m_points.PushBack(m_volumeExtents[1]);
tagVolumeInitConfig.m_points.PushBack(m_volumeExtents[2]);
tagVolumeInitConfig.m_points.PushBack(m_volumeExtents[3]);
tagVolumeInitConfig.m_altitudeMin = -1.f;
tagVolumeInitConfig.m_altitudeMax = 6.f;
tagVolumeInitConfig.m_navTag = gameKitNavTag.GetDynamicNavTag();
tagVolume->Init(tagVolumeInitConfig);
}
tagVolume->AddToWorld();
KyUInt32 safeGuard = 100;
while (tagVolume->GetIntegrationStatus() != Kaim::TagVolume::IntegrationStatus_Integrated)
{
Update();
KT_RETURN_IF(--safeGuard == 0, KY_ERROR);
}
m_crowdDispersionNavTagMap.Add(tagVolume, 0.5f, m_tagVolumeMaxCount); // tag volume are larger than graph accept more bot at a time
return KY_SUCCESS;
}
void VisualDebugNavTagCountHashMap(const char listName[64])
{
Kaim::ScopedDisplayList displayList(GetWorld());
displayList.InitUserControlledLifespan(listName, "NavTagCount");
for (KyUInt32 i = 0; i < m_graphEdges.GetCount(); ++i)
{
Kaim::Ptr<Kaim::GameKitCrowdDispersionNavTag> navTagCrowdDispersion;
Kaim::GameKitCrowdDispersionNavTagId navTagId(m_graphEdges[i].m_navTag);
if (m_crowdDispersionNavTagMap.Get(navTagId, &navTagCrowdDispersion) == false)
continue;
Kaim::Vec3f textPos((m_graphEdges[i].m_a + m_graphEdges[i].m_b) * 0.5f); textPos.z = 10.f;
//displayList.PushTextVarg(textPos, Kaim::VisualColor::Red, "%u / %u", navTagCrowdDispersion->m_count , navTagCrowdDispersion->m_maxCountWithNoRecompute);
std::stringstream strStream;
for (KyUInt32 i = 0; i < navTagCrowdDispersion->GetTimeIntervalSize(); ++i)
strStream << navTagCrowdDispersion->GetCountAtTimeInterval(i) << ".";
displayList.PushTextVarg(textPos, Kaim::VisualColor::Red, "%s", strStream.str().c_str());
}
navTagId.m_layerIndex = 0;
Kaim::Ptr<Kaim::GameKitCrowdDispersionNavTag> navTagCrowdDispersion;
if (m_crowdDispersionNavTagMap.Get(navTagId, &navTagCrowdDispersion) == false)
return;
Kaim::Vec3f textPos((m_volumeExtents[0] + m_volumeExtents[1] + m_volumeExtents[2] + m_volumeExtents[3]) * 0.25f, 10.f);
//displayList.PushTextVarg(textPos, Kaim::VisualColor::Red, "%u / %u", navTagCrowdDispersion->m_count , navTagCrowdDispersion->m_maxCountWithNoRecompute);
std::stringstream strStream;
for (KyUInt32 i = 0; i < navTagCrowdDispersion->GetTimeIntervalSize(); ++i)
strStream << navTagCrowdDispersion->GetCountAtTimeInterval(i) << ".";
displayList.PushTextVarg(textPos, Kaim::VisualColor::Red, "%s", strStream.str().c_str());
}
std::string GetName()
{
std::stringstream strstream;
strstream << m_crowdDispersionCheckDistance << (m_denseGraph ? "_dense" : "_sparse");
strstream << (m_botConfig.m_enableAvoidance == false ? "_noAv" :
(m_botConfig.m_avoidanceConfig.m_contactSolverConfig.m_enableSoftContactSolver == false ? "_av" : "_avCS"));
strstream << (m_noTimeSlicing ? "_noTS" : "_TS");
strstream << "_" << m_botLine << "x" << m_botColumn;
return strstream.str();
}
public: // internal
Kaim::GameKitCrowdDispersionNavTagHashMap m_crowdDispersionNavTagMap;
public:
bool m_enableVisualDebug;
KyUInt32 m_botColumn;
KyUInt32 m_botLine;
KyUInt32 m_tagVolumeMaxCount;
KyFloat32 m_crowdDispersionCheckDistance;
bool m_denseGraph;
bool m_noTimeSlicing;
Kaim::BotConfig m_botConfig;
private:
KyUInt32 m_navigationProfileId;
Kaim::KyArray<Kaim::Vec2f> m_volumeExtents;
};
#define TEST_ENV_CLASS CrowdDispersionEnv
TEST_ENV
{
m_noTimeSlicing = false;
m_denseGraph = true; m_crowdDispersionCheckDistance = 20.f;
m_botConfig.m_enableAvoidance = true; m_botConfig.m_avoidanceConfig.m_contactSolverConfig.m_enableSoftContactSolver = true;
}
TUTORIAL{ env.m_botLine = 2, env.m_botColumn = 2; KyUInt32 starts[] = { 1 }; CHECK(env.TestUp(starts, Kaim::ArraySize(starts))); }
TUTORIAL{ env.m_botLine = 4, env.m_botColumn = 5;
KyUInt32 starts[] = { 1,2,3,4,5 }; CHECK(env.TestUp(starts, Kaim::ArraySize(starts))); }
TUTORIAL
{
env.m_botLine = 10, env.m_botColumn = 10;
CHECK(env.TestDown(3));
}
}