Tutorial_WorldInit.cpp

Tutorial_WorldInit.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/basesystemenv.h"
#include "LabEngine/gamebot.h"
#include "LabEngine/base/kaimlogimplementation.h"
#include "LabEngine/base/kaimfileopener.h"
#include "LabEngine/utils/labengineutils.h"
// This tutorial covers use of WorldInit in your game/level.
//RUN_THIS_FILE
namespace
{
class MinimalGameBot
{
public:
// Define two parameters (velocity and position) of a minimal Game Bot
// that uses Kaim::Bot, and has a destination.
MinimalGameBot()
: m_gameVelocity()
, m_gamePosition()
, m_gameDestination(50.f, 0.f, 0.f)
, m_navBot(KY_NULL)
{}
void Init(Kaim::World* world/*, LabEngine::UseChannelMode channelActivation*/)
{
// Initialize Kaim::Bot. NOTE: Bots are ref-counted.
// A database MUST be set to Kaim::BotInitConfig.
// By default, Kaim::BotInitConfig instructs the bot to use the default radius and height (for the database).
// It also binds a bot to all databases.
// DatabaseBinding defines in which databases the Bot needs to be spatialized to be taken into account when queries are run.
// NOTE: Init() and AddToDatabase() are uncorrelated: a Bot does not need to be added when it is initialized; it can be added at a later time.
m_navBot = *KY_NEW Kaim::Bot;
Kaim::BotInitConfig botInitConfig;
botInitConfig.m_database = world->GetDatabase(0);
botInitConfig.m_startPosition = m_gamePosition;
m_navBot->Init(botInitConfig);
m_navBot->AddToDatabase();
}
void Destroy()
{
m_navBot->RemoveFromDatabase();
m_navBot = KY_NULL;
}
void Goto()
{
// Compute a Path toward a predefined destination using the AStarQuery we provide during the Init function.
// Once the query has completed, the result Path is automatically set to the bot to be the followed path.
// NOTE: the AStarQuery is run during the World::Update
m_navBot->ComputeNewPathToDestination(m_gameDestination);
// we should test the return code of this function but as this tutorial is pretty simple, we are sure it cannot fail :
// - We correctly set the AStarQuery to the bot in the MinimalGameBot::Init function.
// - We do not ask for a path computation while an other path computation is in process.
}
void Update(KyFloat32 simulationStepsInSeconds)
{
// Move the Game Bot
// Retrieve the velocity recommended.
// The game will apply its physics and gameplay-specific settings to the Bot.
m_gameVelocity = m_navBot->GetBotOutput().m_outputVelocity;
m_gamePosition += m_gameVelocity*simulationStepsInSeconds;
// If the game Bot's position or velocity change, these changes must be applied to Kaim::Bot.
m_navBot->SetPosition(m_gamePosition);
m_navBot->SetVelocityAndFrontDirection(m_gameVelocity);
if (HasArrived())
m_navBot->ClearFollowedPath();
}
bool HasArrived()
{
KyFloat32 arrivalPrecisionRadius = m_navBot->GetConfig().m_pathProgressConfig.m_checkPointRadius;
if (m_navBot->HasReachedPosition(m_gameDestination, arrivalPrecisionRadius))
{
return true;
}
return false;
}
public:
Kaim::Vec3f m_gameVelocity;
Kaim::Vec3f m_gamePosition;
Kaim::Vec3f m_gameDestination;
Kaim::Ptr<Kaim::Bot> m_navBot;
};
class NavDataLoader
{
public:
NavDataLoader() : m_file(KY_NULL), m_memory(KY_NULL) {}
~NavDataLoader()
{
if (m_file)
{
m_file->Close();
m_file = KY_NULL;
}
delete m_memory;
}
void LoadToMemory()
{
int length = m_file->GetLength();
m_memory = new char[length];
m_file->Read((Kaim::UByte*)m_memory, length);
}
Kaim::Ptr<Kaim::File> m_file;
char* m_memory;
};
class MinimalGameWorld
{
public:
bool Init(NavDataLoader& navDatanavDataLoader/*, LabEngine::UseChannelMode botChannelActivation = LabEngine::DontUseChannel*/)
{
// Worlds are ref-counted, and stored with Kaim::Ptr
// (Generally, Kaim::Ptr always prefixes assignements with * when constructing the object).
// The World creates and maintains Databases.
// A Database is a container of NavData that represents the World from the point of view of a specific type of Bots.
// One or many NavData generated with the same GeneratorParameters can be added to a Database.
const KyUInt32 databaseCount = 1;
m_world = *KY_NEW Kaim::World(databaseCount);
// Load NavData (from a file or from memory) for Bots to be able to navigate in the world.
// NOTE: loading from a file prevents the sharing of Kaim::NavData among several databases.
// To share NavData: load the file in memory, then pass this memory to the NavData.
// At the start, Kaim::NavData::AddToDatabaseImmediate() can be used
// because Kaim::World is not yet running any asynchronous queries;
// during the course of the game, use Kaim::NavData::AddToDatabase() which defers adding
// NavData until the next Kaim::World::Update()
m_navData = *KY_NEW Kaim::NavData;
KyResult loadingResult = KY_ERROR;
if (navDatanavDataLoader.m_memory == KY_NULL) // if not loaded to memory
loadingResult = m_navData->Load(navDatanavDataLoader.m_file); // load from file, this will allocate memory each time
else
loadingResult = m_navData->LoadFromMemory(navDatanavDataLoader.m_memory); // here we use a pointer to a memory already allocated, thus sharing NavData
if (KY_FAILED(loadingResult))
return false;
m_navData->Init(m_world->GetDatabase(0));
m_navData->AddToDatabaseImmediate();
// Create a minimal game bot using Kaim::Bot,
// and set it to go to a given position.
m_minimalGameBot.Init(m_world); //, botChannelActivation);
m_minimalGameBot.Goto();
return true;
}
void Update()
{
const KyFloat32 simulationStepsInSeconds = 0.016f;
// Each frame, the game must call Kaim::World::Update.
// This updates the database, spatialization of bots, points of interest, bot pathfollowing, and queries.
// If VisualDebugServer is started, it will receive and send data here as well.
m_world->Update(simulationStepsInSeconds);
// The game updates its bots.
m_minimalGameBot.Update(simulationStepsInSeconds);
// Simulate game components.
LabEngine::Utils::TimedBusyWait(simulationStepsInSeconds*1000.f);
}
void Destroy()
{
m_minimalGameBot.Destroy();
// Kaim::NavData::RemoveFromDatabaseImmediate() can be used if Kaim::World is not running any asynchronous queries.
// During the course of the game, use Kaim::NavData::RemoveFromDatabase(), which will remove the NavData
// at the next Kaim::World::Update().
m_navData->RemoveFromDatabaseImmediate();
m_navData = KY_NULL;
// Destroy the world before destroying the BaseSystem.
m_world = KY_NULL;
}
public:
Kaim::Ptr<Kaim::NavData> m_navData;
Kaim::Ptr<Kaim::World> m_world;
MinimalGameBot m_minimalGameBot;
};
// Use BaseSystemEnv to initialize Kaim::BaseSystem in this tutorial.
// For more information on initializing the BaseSystem, see Tutorial_BaseSystem.cpp.
#define TEST_ENV_CLASS BaseSystemEnv
TEST_ENV {}
TUTORIAL // Basic initialization and update of Kaim::World with one database and one bot.
{
KT_LOG_TITLE_BEGIN("TUTORIAL - Kaim::World Initialization and update");
MinimalGameWorld gameWorld;
// Initialization includes:
// - Kaim::World initialization
// - Kaim::NavData (loading from a file)
// - Creating Kaim::Kaim::Bot
NavDataLoader navDataLoader;
LabEngine::KaimFileOpener fileOpener; // NOTE: the SDK comes with a default file opener: Kaim::DefaultFileOpener.
navDataLoader.m_file = fileOpener.OpenFile(env.GetAbsoluteInputFileName("GeneratedNavData/plan/plan.NavData").c_str(), Kaim::OpenMode_Read);
CHECK(navDataLoader.m_file);
bool initSucceeded = gameWorld.Init(navDataLoader);
CHECK(initSucceeded);
// Game loop
for (KyUInt32 gameFrame = 0; gameFrame < 10; ++gameFrame)
{
// Each iteration updates:
// - Kaim::World
// - Kaim::Bot
gameWorld.Update();
}
// Proper destruction of previously initialized elements.
gameWorld.Destroy();
}
TUTORIAL // Multi-World and NavData sharing
{
KT_LOG_TITLE_BEGIN("TUTORIAL - Sharing NavData between several worlds");
MinimalGameWorld gameWorldInstances[10];
// Load the NavData file in memory once,
// and then reference this memory in all game world instances.
// Sharing and save the memory.
NavDataLoader navDataLoader;
LabEngine::KaimFileOpener fileOpener; // NOTE: the SDK comes with a default file opener: Kaim::DefaultFileOpener.
navDataLoader.m_file = fileOpener.OpenFile(env.GetAbsoluteInputFileName("GeneratedNavData/plan/plan.NavData").c_str(), Kaim::OpenMode_Read);
CHECK(navDataLoader.m_file);
navDataLoader.LoadToMemory(); // Only load the file to memory once.
for (KyUInt32 i = 0; i < 10; ++i)
{
bool initSucceeded = gameWorldInstances[i].Init(navDataLoader); // Init all game world instances with NavData located at the same place in memory
CHECK(initSucceeded);
}
// Game Loop on all instances
for (KyUInt32 gameFrame = 0; gameFrame < 10; ++gameFrame)
{
for (KyUInt32 i = 0; i < 10; ++i)
gameWorldInstances[i].Update();
}
for (KyUInt32 i = 0; i < 10; ++i)
gameWorldInstances[i].Destroy();
}
}