Customizing Path Following

There are many possible ways for you to customize the path following system for your characters, ranging from simple (such as calling class methods) to complex (re-implementing core classes). This topic, and the other topics in this section, outline some of the most common options and strategies for controlling the operation of the various elements of path following system.

Customizing Bot configuration parameters and class methods

Each Bot accepts a wide range of configuration parameters that control the way it finds and follows paths.

Setting parameters

You can set your Bot configuration parameters in a BotConfig object, which you provide at initialization time in your call to Bot::Init(). This approach allows you to create and set up a single configuration object, which you can use to set up multiple game characters with an identical set of configuration parameters.

The BotConfig class contains some simple data members, and groups other parameters by functional category into subordinate configuration objects.

For example:

Kaim::BotConfig botConfig;
botConfig.m_enableAvoidance = true;
botConfig.m_shortcutTrajectoryConfig.m_maxDistanceFromBot = 20.0f;
...

Kaim::Ptr<Kaim::Bot> bot = *KY_NEW Kaim::Bot();
bot->Init(botInitConfig, botConfig);

All of the configuration parameters in the BotConfig can also be set dynamically for each character, by calling methods of the Bot API.

Simple data members of the BotConfig are exposed directly through methods of the Bot class. For the subordinate configuration objects exposed by the BotConfig, the Bot class also exposes accessor methods to get and set the current instances.

For example:

bot->SetEnableAvoidance(true);

Kaim::ShortcutTrajectoryConfig shortcutConfig;
shortcutConfig.SetDefaults();
shortcutConfig.m_maxDistanceFromBot = 20.0f;
bot->SetShortcutTrajectoryConfig(shortcutConfig);

Physical dimensions

Path finding settings

Path progress settings

You can use the PathProgressConfig class to set some parameters that determine how the path following system tracks the progress of the Bot along its path.

For example, if you use the check point system extensively, you may want to customize the PathProgressConfig::m_checkPointRadius value, which determines how close the Bot must come to a check point before it is allowed to continue past the check point. See also Using Check Points.

You can set up the PathProgressConfig in BotConfig::m_pathProgressConfig or by calling Bot::SetPathProgressConfig()

Trajectory computation settings

Dynamic avoidance settings

Customizing the NavigationProfile

A NavigationProfile is an object factory that provides instances of classes used during path computations and path following. It has three main purposes:

The World creates and maintains a default NavigationProfile at initialization time, which provides access to instances of all the default path finding and path following classes supplied with the SDK. Every Bot is automatically set up at initialization time to use this default NavigationProfile, unless you specify otherwise.

Note that the default NavigationProfile is fully functional out of the box, and the set of default objects that it serves is highly customizable (see the previous section for details). You can certainly continue to use this default profile for your project. You only need to use a different NavigationProfile if you have a particular need to override the default implementation for one or more of the classes it serves.

The most common needs for user customization in the NavigationProfile include:

In all of these cases, you would need to set up a custom class of NavigationProfile to serve your custom implementation.

Setting up a custom NavigationProfile

NavigationProfiles are managed centrally by the World. Each time you set up a new NavigationProfile, the World assigns it a unique ID. You use this ID value to set up other objects (typically Bots and path finding queries) with the profile they should use.

Note that the unique ID of the default NavigationProfile is always 0.

To set up a custom NavigationProfile for a Bot:

  1. Write your custom implementation of the interface that you want your NavigationProfile to serve, or your custom implementation of the TraverseLogic interface.
  2. Write a new class that derives from NavigationProfile, and template the base class to the class of TraverseLogic you want it to use. This may be your custom class if you want to control forbidding and preferring NavTags, otherwise it should be the default SimpleTraverseLogic class supplied with the SDK.
  3. If you want to use a custom implementation of any of the interfaces served by the NavigationProfile, re-implement the appropriate virtual method to return a pointer to an object of your custom class.
  4. After you initialize your World, create an instance of your NavigationProfile class, and pass a pointer to it in a call to World::AddNavigationProfile(). This method returns the unique ID assigned to the profile. Keep track of this ID value.
  5. You can set up a Bot to use your new profile in BotInitConfig::m_startNewPathNavigationProfileId, or by calling Bot::SetNewPathNavigationProfileId(). Any time you request a path calculation by calling Bot::ComputeAStarQueryAsync(), your custom profile will now be used. See also the "basic" path calculation method under Getting a Static Path.

    Note, however, that if you set up a Bot with a custom path finding query or your own pre-planned path, as described in the "advanced" path calculation methods on that page, the Bot will automatically use the profile ID set for the query or the Path object while it follows that path. If you want the Bot to use your custom profile, you must also set up your custom query or Path object with the ID of your custom profile by calling its IPathFinderQuery::SetNavigationProfileId() or Path::SetNavigationProfileId() method.

For example:

// The NavigationProfile in this example uses a custom TraverseLogic and a custom IPathEventListObserver.

class MyTraverseLogic : public Kaim::DefaultTraverseLogic
{
public:
    static KY_INLINE bool CanTraverseNavTriangle(void* userData, const Kaim::NavTriangleRawPtr& triangleRawPtr,
        const Kaim::NavTag& navTag, KyFloat32* costMultiplier)
    { ... }
};

class MyPathEventListener : public Kaim::IPathEventListObserver
{
public:
    virtual void OnPathEventListBuildingStageDone(Kaim::Bot* bot, Kaim::PathEventList& pathEventList, KyUInt32 firstIndexOfNewEvent, FirstIntervalStatus firstIntervalStatus)
    { ... }
    virtual void OnPathEventListDestroy(Kaim::Bot* bot, Kaim::PathEventList& pathEventList, DestructionPurpose destructionPurpose)
    { ... }
};

class MyNavigationProfile : public Kaim::NavigationProfile<MyTraverseLogic>
{
public:
    virtual Kaim::Ptr<Kaim::IPathEventListObserver> GetSharedPathEventListObserver()
    {
        if (m_myPathEventListener == KY_NULL)
            m_myPathEventListener = *KY_NEW MyPathEventListener();
        return m_myPathEventListener;
    }

public:
    Kaim::Ptr<MyPathEventListener> m_myPathEventListener;
};

...

// in your World initialization code:
const KyUInt32 databaseCount = 1;
m_world = *KY_NEW Kaim::World(databaseCount);

Kaim::Ptr<MyNavigationProfile> myNavigationProfile = *KY_NEW MyNavigationProfile();
KyUInt32 navigationProfileId = m_world->AddNavigationProfile(myNavigationProfile);
...

// in your Bot initialization code:

m_navBot = *KY_NEW Kaim::Bot;

Kaim::BotInitConfig botInitConfig;
botInitConfig.m_database = m_world->GetDatabase(0);
botInitConfig.m_startPosition = m_position;
botInitConfig.m_startNewPathNavigationProfileId = navigationProfileId;
m_navBot->Init(botInitConfig);

// We add the Bot to our Database.
m_navBot->AddToDatabase();
...

Taking temporary control over the Bot

At times, you may want to temporarily take complete control over the movement of your character in order to play a particular animation (climbing a ladder, jumping, etc.), in order to play a cutscene, etc.

To take temporary control, you could simply continue your path following as normal, but ignore the velocity computed by the path following system.

However, it is recommended that you temporarily disable path following by calling both Bot::SetDoValidateCheckPoint() and Bot::SetDoComputeTrajectory(). Call them again to re-activate path following when you no longer need total control over the movements of your character. For example:

void MyJumpObject::ManageTraversing()
{
    // Take control
    m_navBot->SetDoValidateCheckPoint(false);
    m_navBot->SetDoComputeTrajectory(false);
  
    ... // manage playing the jump animation

    if (animationIsFinished)
    {
        // Release control
        m_navBot->SetDoValidateCheckPoint(true);
        m_navBot->SetDoComputeTrajectory(true);
    }
}

Note that while the path following system is disabled in this way, your Bot is rendered in blue while visual debugging in the Navigation Lab instead of the default yellow.

Note also that depending on how long you keep control of your character's movement, and how far you drive it, you may need to force the current target point forward along the path when you release control. See also Creating Smart Objects.