Integration Phase 4: Computing a Path at Runtime Using a Query

In this phase of your integration, you will learn how to use a query to compute a path between two points in your terrain. This is an introduction to the queries provided by the toolbox layer.

In the Navigation Lab

We'll first look at how the query is performed in the Navigation Lab.

  1. Drag and drop your .NavData file into the Navigation Lab.
  2. Right-click a starting position for your path on the NavMesh, and select Set Marker A from the contextual menu.
  3. Right-click a destination for your path, and select Set Marker B from the contextual menu.

  4. Open the Query Browser panel on the left side of the main Navigation Lab window, and select the Astar query.
  5. Open the Attribute Editor panel on the right side of the main Navigation Lab window. Here, you can set the parameters for the selected query.

    Set the Start and Dest controls to PosA and PosB respectively. This tells the query that you want to compute a path from the position A marker to the position B marker.

  6. If there is a path between these positions, it will be drawn in green in the 3D view:

Note that you can easily retrieve the start and end points for the query from the Navigation Lab. At the bottom of the main window, the Navigation Lab lists the positions of the A and B markers in 3D space (expressed in the Navigation coordinate system). Click the Copy buttons to copy these values to the clipboard.

In your game code

Now, we'll try to replicate the same query in your game at runtime, using an instance of the AstarQuery class.

[code from Tutorial_FirstIntegration.cpp]

#include "gwnavruntime/queries/astarquery.h"
...

class MyGameLevel
{
public:
    ...    
    void UpdateLogic(float deltaTimeInSeconds);
    void UpdatePhysics(float deltaTimeInSeconds);
    ...
    void TestAStarQuery(Kaim::World* world);
    ...
protected:
    ...
    Kaim::Ptr<Kaim::AStarQuery<Kaim::DefaultTraverseLogic> >  m_astarQuery; // Not used for the entity, but simply to demonstrate how to run a query
    ...
};
...

bool MyGameLevel::Initialize(Kaim::World* world)
{ 
    ...
    m_astarQuery = *KY_NEW Kaim::AStarQuery<Kaim::DefaultTraverseLogic>;
    m_astarQuery->BindToDatabase(world->GetDatabase(0));
    ...
}
...

void MyGameLevel::TestAStarQuery(Kaim::World* world)
{
    // Choose two positions (in Navigation space) you want to test.
    // You can use the Navigation Lab to choose them.
    Kaim::Vec3f startPos = Kaim::Vec3f(-11.5963f,1.06987f,10.4563f);
    Kaim::Vec3f destPos  = Kaim::Vec3f(-16.636f,-26.7078f,8.10107f);

    // Leverage the visual debugging system.
    Kaim::ScopedDisplayList displayList(world, Kaim::DisplayList_Enable);
    displayList.InitSingleFrameLifespan("Test AStar Query", "My Game");
    displayList.PushLine(startPos, startPos + Kaim::Vec3f::UnitZ(), Kaim::VisualColor::Blue);
    displayList.PushLine(destPos,  destPos  + Kaim::Vec3f::UnitZ(), Kaim::VisualColor::Blue);

    if(m_astarQuery->m_processStatus == Kaim::QueryNotStarted)
    { 
        // Each AstarQuery must be set up to run on a specific Database. 
        m_astarQuery->Initialize(startPos, destPos);

        // Blocking means that we want the result immediately.
        // Alternatively, a query can be asynchronous and time-sliced. 
        m_astarQuery->PerformQueryBlocking();

    } else if (m_astarQuery->m_processStatus == Kaim::QueryDone)
    {
        // When the query is done, we ask for the result.
        Kaim::AStarQueryResult result = m_astarQuery->GetResult();

        // Kaim::AStarQueryResult enumerates many possible query results.
        // For now, let's focus on success.
        switch( result )
        {
        case Kaim::ASTAR_DONE_PATH_FOUND:
            {
                displayList.PushText(destPos + Kaim::Vec3f::UnitZ(), Kaim::VisualColor(0, 255, 0), "AStar query success !");
                break;
            }
        default:
            displayList.PushLine(startPos,  destPos , Kaim::VisualColor::Red);
            displayList.PushText(destPos + Kaim::Vec3f::UnitZ(), Kaim::VisualColor(255, 0, 0), "AStar query failed !");
            break;
        }

        // Here, we build and send a query, so it can be displayed into the NavigationLab.
        // cf. IQuery::SendVisualDebug() for more details.
        m_astarQuery->SendVisualDebug();
    }
}
...

void MyGameLevel::UpdateLogic(float deltaTimeInSeconds)
{
    TestAStarQuery(m_entity.m_navBot->GetWorld());
    ...
}

void MyGameLevel::UpdatePhysics(float deltaTimeInSeconds)
{
    m_entity.UpdatePhysics(deltaTimeInSeconds);
}

...

void MyGameWorld::Update(float deltaTimeInSeconds)
{
    ...
    UpdateLogic(deltaTimeInSeconds);
    
    UpdateNavigation(deltaTimeInSeconds);
    
    UpdatePhysics(deltaTimeInSeconds);

}

Back in the Navigation Lab

When you connect to your game, you should now see the path computed by the query drawn in the 3D view. Assuming that your NavMesh is the same, and you are using the same positions for your A and B markers, the resulting path should be the same as the one shown when you ran the query for the first time inside the Navigation Lab: