自定义路径跟随

您可以通过多种方法为角色自定义路径跟随系统,有简单的(如调用类方法),也有复杂的(重新实现核心类)。本主题以及本部分中的其他主题概述了用于控制路径跟随系统各种元素的操作的一些最常用的选项和策略。

自定义人物配置参数和类方法

每个人物都接受广泛的配置参数以控制其寻径和跟随路径的方式。

设置参数

您可以在 BotConfig 对象中设置 Bot 配置参数,该对象在 Bot::Init 调用初始化时提供。该方法允许您创建和设置单个配置对象,可用于通过一组相同配置参数来设置多个游戏角色。

BotConfig 类包含一些简单的数据成员,并按功能类别将其他参数分组为从属配置对象。

例如:

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);

也可以通过调用 Bot API 的方法,为每个角色动态设置 BotConfig 中的所有配置参数。

BotConfig 的简单数据成员将通过 Bot 类的方法直接公开。对于 BotConfig 公开的从属配置对象,Bot 类还公开了取值函数方法以获取并设置当前实例。

例如:

bot->SetEnableAvoidance(true);

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

物理尺寸

  • 您可以在 BotConfig::m_radius 中,或通过调用 Bot::SetRadius() 来设置 Bot 的半径。请注意,您在此处设置的值定义的角色宽度仅用于动态回避系统,以及 Navigation Lab 中的可视调试。对于寻径和路径跟随,角色的半径假定为用来生成 Bot 使用的 NavMesh 的半径。游戏角色的实际半径应始终接近用于生成 NavMesh 的值,否则在角色撞击或通过静态几何体时可能会遇到问题。
  • 您可以在 BotConfig::m_height 中,或通过调用 Bot::SetHeight() 来设置人物的高度。该值仅用于 Navigation Lab 中的可视调试。

寻径设置

路径前进设置

您可以使用 PathProgressConfig 类来设置一些参数,以确定路径跟随系统如何跟踪 Bot 沿其路径的前进情况。

例如,如果您广泛使用检查点系统,可能需要自定义 PathProgressConfig::m_checkPointRadius 值,用于确定允许 Bot 继续通过检查点前其必须达到的与检查点的接近程度。请参见使用检查点

您可以在 BotConfig::m_pathProgressConfig 中,或通过调用 Bot::SetPathProgressConfig() 来设置 PathProgressConfig

轨迹计算设置

  • 您可以在 BotConfig::m_trajectoryMode 中,或通过调用 Bot::SetTrajectoryMode() 来选择角色使用的轨迹计算系统。请注意,如果从捷径系统切换到通道系统,则需要启动新的路径重新计算以生成通道轨迹计算机所需的通道数据。从通道轨迹系统切换到捷径系统将立即生效。
  • 用于控制捷径轨迹系统的参数将收集到 ShortcutTrajectoryConfig 类中,您可以在 BotConfig::m_shortcutTrajectoryConfig 中,或通过调用 Bot::SetShortcutTrajectoryConfig() 进行设置。

    例如,您可以控制 Bot 检查新捷径的频率、捷径的最远距离、其沿路径检查的候选点的间距等。

  • 用于控制通道轨迹系统的参数将收集到 SplineTrajectoryConfig 类中,这样您可以在 BotConfig::m_channelTrajectoryConfig 中进行设置或通过调用 Bot::SetChannelTrajectoryConfig() 进行设置。

    例如,您可以控制 Bot 到通道边界对角的最小距离、重新计算其样条线的频率、每个样条线所需的长度等。

  • 两种轨迹计算系统均使用 LocomotionModelConfig 类的参数,以确定如何基于当前目标点或跟随的当前样条线计算 Bot 的最终速度。您可以在 BotConfig::m_locomotionModelConfig 中,或通过调用 Bot::SetLocomotionModelConfig() 设置该类。

    角色的最大期望速度 (LocomotionModelConfig::m_maxDesiredLinearSpeed) 也通过 Bot 类中的捷径取值函数 Bot::SetMaxDesiredLinearSpeed()Bot::GetMaxDesiredLinearSpeed() 公开。

  • 如果在上一次计算后,角色移动距离未超过最小距离阈值,两种轨迹计算系统都将跳过轨迹重新计算。您可以在 Bot 类中使用 Bot::SetTrajectoryMinimalMoveDistance()Bot:: GetTrajectoryMinimalMoveDistance() 设置该阈值。

动态回避设置

自定义 NavigationProfile

NavigationProfile 是提供用于路径计算和路径跟随的类实例的对象工厂。有三种主要用途:

World 可以在初始化时创建并维护默认 NavigationProfile,以提供对 SDK 随附的所有默认寻径和路径跟随类的实例的访问。除非另外指定,否则每个 Bot 都会在初始化时自动设置为使用该默认 NavigationProfile

请注意,默认 NavigationProfile 是开箱即用的完备功能,其服务的默认对象集可以高度自定义(有关详细信息,请参见上一部分)。当然,您可以继续在项目中使用此默认轮廓。如果因特定原因需要针对其服务的一个或多个类覆盖默认实现,您只需使用不同的 NavigationProfile

NavigationProfile 中最常见的用户自定义需求包括:

在所有这些情况下,您都需要设置 NavigationProfile 的自定义类以为自定义实现提供服务。

设置自定义 NavigationProfile

NavigationProfileWorld 集中管理。每次设置新的 NavigationProfileWorld 都会为其指定一个唯一 ID。您可以使用该 ID 值通过应该使用的轮廓来设置其他对象(通常为 Bot 和寻径查询)。

请注意,默认 NavigationProfile 的唯一 ID 始终为 0

设置人物的自定义 NavigationProfile:

  1. 编写您希望 NavigationProfile 提供服务的接口的自定义实现,或 TraverseLogic 接口的自定义实现。
  2. 编写派生自 NavigationProfile 的新类,并将基类作为您希望其使用的 TraverseLogic 类的模板。如果要控制禁止或首选 NavTag,您可能需要使用自定义类,否则应使用 SDK 随附的默认 SimpleTraverseLogic 类。
  3. 如果想要使用 NavigationProfile 提供服务的任何接口的自定义实现,请重新实现相应的虚拟方法以将指针返回到自定义类的对象。
  4. 初始化 World 后,创建 NavigationProfile 类的一个实例,然后通过调用 World::AddNavigationProfile() 向其传递指针。此方法将返回指定给轮廓的唯一 ID。跟踪此 ID 值。
  5. 您可以在 BotInitConfig::m_startNewPathNavigationProfileId 中,或通过调用 Bot::SetNewPathNavigationProfileId() 设置 Bot 以使用新的轮廓。现在,不管何时通过调用 Bot::ComputeNewPathToDestination() 请求路径计算,都将使用您的自定义轮廓。请参见获取静态路径下的“基本”路径计算方法。

    但是,请注意,如果您使用自定义寻径查询或自己预先计划好的路径设置 Bot(如该页面上的“高级”路径计算方法中所述),Bot 在跟随该路径时,将自动使用为查询或 Path 对象设置的轮廓 ID。如果您希望 Bot 使用自定义轮廓,则还必须通过调用其 IPathFinderQuery::SetNavigationProfileId()Path::SetNavigationProfileId() 方法设置具有自定义轮廓 ID 的自定义查询或 Path 对象。

例如:

// 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();
...

临时控制人物

有时,可能需要临时完全控制角色的移动,以便播放特别的动画(爬梯子、跳跃等)、播放过场动画等。

若要实施临时控制,您只需正常继续路径跟随,而忽略路径跟随系统计算的速度。

但是,建议您通过调用 Bot::SetDoValidateCheckPoint()Bot::SetDoComputeTrajectory() 来临时禁用路径跟随。当不再需要完全控制角色的移动时,请再次调用以上方法以重新激活路径跟随。例如:

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);
    }
}

请注意,如果通过以上方式禁用了路径跟随系统,则人物会在 Navigation Lab 中进行可视调试时渲染为蓝色,而非默认的黄色。

还请注意,根据您控制角色移动的时长和距离,可能需要在解除控制时强制当前目标点沿路径向前移动。请参见创建智能对象