This page describes how to create, update and destroy CylinderObstacles in your game.
For an introduction to the role of the CylinderObstacles class in the obstacle management system, see Using Dynamic Obstacles and TagVolumes.
For background information on the NavTag system, see Tagging with Custom Data.
You need to make a class in your game manage the initialization and destruction of the CylinderObstacle. You may already have a class in your game that manages the lifespan of the object or player character that the CylinderObstacle is intended to represent within the Gameware Navigation World. If you do, you can use that class. You may also have an analogous object in your physics system, from which you can retrieve the position and velocity of the object at each frame.
For example: [code from Tutorial_ObstacleIntegration.cpp]
class MyGameCylinderObstacle { public: MyGameCylinderObstacle() : m_navCylinderObstacle(KY_NULL) {} void Initialize(Kaim::World* world, KyFloat32 radius, KyFloat32 height, const Kaim::Vec3f& position, const Kaim::Vec3f& linearVelocity); void Destroy(); void Update(KyFloat32 simulationStepsInSeconds); RigidBodyPhysics m_rigidBodyPhysics; Kaim::Ptr<Kaim::CylinderObstacle> m_navCylinderObstacle; };
To initialize your CylinderObstacle, you must provide an instance of the CylinderObstacleInitConfig configuration class, which you set up with the data needed by the CylinderObstacle. You must set up at a minimum:
For example: [code from Tutorial_ObstacleIntegration.cpp]
void MyGameCylinderObstacle::Initialize(Kaim::World* world, KyFloat32 radius, KyFloat32 height, const Kaim::Vec3f& position, const Kaim::Vec3f& linearVelocity) { // Initialize the object that represents this obstacle in the physics system. m_rigidBodyPhysics.Initialize(position, linearVelocity); // Set up the CylinderObstacleInitConfig Kaim::CylinderObstacleInitConfig cylinderObstacleInitConfig; cylinderObstacleInitConfig.m_world = world; cylinderObstacleInitConfig.m_radius = radius; cylinderObstacleInitConfig.m_height = height; cylinderObstacleInitConfig.m_startPosition = m_rigidBodyPhysics.GetPosition(); // This sets the NavTag as non-walkable: it will "cut a hole" in the NavMesh. cylinderObstacleInitConfig.m_navTag.SetAsExclusive(); // Create and initialize the CylinderObstacle, and add it to its World. m_navCylinderObstacle = *KY_NEW Kaim::CylinderObstacle; m_navCylinderObstacle->Init(cylinderObstacleInitConfig); // We want to draw a maximum amount of information m_navCylinderObstacle->SetCurrentVisualDebugLOD(Kaim::VisualDebugLOD_Maximal); }
Each time the object represented by the CylinderObstacle changes its position or velocity in your game, you need to update your CylinderObstacle to reflect the new state. You can use the functions in CylinderObstacle to reflect the new state.
Note that this does not modify the CylinderObstacle immediately; the update is deferred until the next update of the World.
For example: [code from Tutorial_ObstacleIntegration.cpp]
void MyGameCylinderObstacle::Update(KyFloat32 simulationStepsInSeconds) { ... // Update the object in the physics system. m_rigidBodyPhysics.Update(simulationStepsInSeconds); // Update the CylinderObstacle. m_navCylinderObstacle->SetPosition(m_rigidBodyPhysics.GetPosition()); m_navCylinderObstacle->SetVelocity(m_rigidBodyPhysics.GetLinearVelocity()); }
If your obstacle stops moving and becomes stable, you will likely want to integrate it into the NavMesh so that the Bots in your game can take its presence into account when planning paths through the terrain, instead of only reacting to its presence during the dynamic avoidance phase of their path following.
When your obstacle becomes stationary, call its CylinderObstacle::DoesTriggerTagVolume() method and pass true. During the next update of the World the obstacle will transparently spawn a TagVolume with the same position and dimensions, and with the NavTag that you set when you initialized the CylinderObstacle. This TagVolume is then integrated into the NavMesh, tagging the area with custom data or tagging it as "exclusive" and removing it from the NavMesh. Once the integration has been completed and the NavMesh is updated, the obstacle's volume is automatically removed from all dynamic avoidance calculations, since its presence is now managed through the NavMesh.
If your obstacle starts moving again, call CylinderObstacle::DoesTriggerTagVolume() and pass false in order to reverse the process.
For example: [code from Tutorial_ObstacleIntegration.cpp]
void MyGameCylinderObstacle::Update(KyFloat32 simulationStepsInSeconds) { const bool isStopped = m_rigidBodyPhysics.IsSleeping(); m_navCylinderObstacle->SetDoesTriggerTagVolume(isStopped); if (isStopped) return; ... }
To destroy the CylinderObstacle:
For example: [code from Tutorial_ObstacleIntegration.cpp]
void MyGameCylinderObstacle::Destroy() { m_navCylinderObstacle->RemoveFromWorld(); m_navCylinderObstacle = KY_NULL; }