集成阶段 6:使用 NavData 生成 API

在大多数项目中,预计您将把生成的 NavData 直接集成到自己的关卡设计工具或游戏编辑器中。此集成旨在确保当美工人员或设计人员对关卡进行更改时,最新的 NavData 始终自动生成并滚动到构建中。

本主题介绍以编程方式生成 NavData 的主要步骤。

设置项目

请遵循集成阶段 1a:包含、库和预处理器定义中为运行时游戏提供的说明。此外,还必须链接 gwnavgeneration 库,该库与 gwnavruntime 位于同一目录。

您必须将库链接到关卡编辑器和游戏运行时,如下图所示:

在本教程中,DefaultGeneratorGlue 类将用于为生成系统提供并行处理服务。该类依赖于来自 Intel 的线程构建块 (TBB) 库在多个处理器间分布计算。要避免在 Gameware Navigation SDK 的核心库内引入第三方依存关系,该类及其相关类的头文件和源代码应保留在 integration 目录下。要使用 DefaultGeneratorGlue,您必须执行如下操作:

编写 GeneratorInputProducer

您必须编写 GeneratorInputProducer 类的实现。该类的目的是为 NavData 生成系统提供构成地形的三角形。

它包含一个您必须实现的关键虚拟方法:GeneratorInputProducer::Produce()。生成系统每次为地形的某个地块生成 NavData 时,都会调用该方法,从而传递要处理的地块说明以及向其提供三角形的 ClientInputConsumer 对象。GeneratorInputProducer::Produce() 的实现可以使用保留在 GeneratorSector 参数中的任何数据来确定应提供哪些三角形,包括创建该地块时为其指定的名称或 GUID。

有关将 NavData 分割为地块的详细信息,请参见将地形划分为地块。如果要创建可反映整个游戏地形的单个 NavData 地块(这可能是初始集成情况),GeneratorInputProducer::Produce() 的实现只需向 ClientInputConsumer 提供地形网格中的所有三角形。

例如:

#include "gwnavgeneration.h"

class MyProducer : public Kaim::GeneratorInputProducer
{
 
public:
    virtual KyResult Produce(
            const Kaim::GeneratorSector& sector, 
            Kaim::ClientInputConsumer& inputConsumer);
};

KyResult MyProducer::Produce(
            const Kaim::GeneratorSector& /* sector */, 
            Kaim::ClientInputConsumer& inputConsumer )

{
    // A sector can contain your whole level, or a part of a level that you plan
    // to stream in and out at runtime. You can set up as many sectors as you need.
    // See the section on running a generation below.
    
    // A NavTag is an array of blind data that you can add on each triangle.
    // You can use this data to distinguish different materials that you want to
    // take into account during your runtime path finding and path following.
    Kaim::DynamicNavTag aNavTag;
    aNavTag.m_blindDataArray.PushBack(42);


    // Set up a CoordSystem object to match the system used in your editor.
    KyFloat32 oneMeterInClientUnits = 1.0f; 
    Kaim::CoordSystem::ClientAxis clientRightAxis = Kaim::CLIENT_X;
    Kaim::CoordSystem::ClientAxis clienFrontAxis = Kaim::CLIENT_Y;
    Kaim::CoordSystem::ClientAxis clientUpAxis = Kaim::CLIENT_Z;

    Kaim::CoordSystem myCoordSystem;
    myCoordSystem.Setup( 
        oneMeterInClientUnits, 
        clientRightAxis, 
        clienFrontAxis, 
        clientUpAxis ); 

    // For each triangle in the sector, feed it to the input consumer.
    // How you retrieve the triangles is specific to your system.
    // If you have set up a CoordSystem as shown above, you can send your
    // triangles in your own coordinate system. The conversion is done internally
    // by the generation system.
    for(KyUInt32 index=0; index < myTriangleCount; index++)
    {
        inputConsumer.ConsumeTriangle(
                        myTriangle.Vertex[0], 
                        myTriangle.Vertex[1], 
                        myTriangle.Vertex[2], 
                        aNavTag,
                        myCoordSystem);
    }
    return KY_SUCCESS;
}

ClientInputConsumer 对象还包含可在各个地块之间共享的种子点列表和 TagVolume 列表。如果需要提供具有种子点的生成系统或者在 3D 体积内标记 NavMesh,建议您将种子点和体积添加到这些列表中。有关详细信息,请参见标识可导航区域

设置 BaseSystem

您需要初始化并关闭应用程序中的 BaseSystem,关闭方式与运行时游戏中相同。请参见集成阶段 1b:设置 BaseSystem

您可以使用 GeneratorBaseSystem 类正确初始化生成 NavData 的 BaseSystem。如果不调用 Kaim::BaseSystem::Init(config),您可以简单调用 Kaim::GeneratorBaseSystem::Init(config)。在生成过程中,Gameware Navigation 使用仅对 NavData 生成有用的数据结构。因此,建议使用 GeneratorBaseSystem 以正确注册生成唯一的类。

运行生成

启动生成运行所涉及的关键组件包括 Generator 类及其 GeneratorInputOutput 配置类。

创建 GeneratorInputOutput 对象,使用配置参数对其进行设置,并添加要为其生成 NavData 的地形的每个地块或地形块的定义。然后,在对 Generator::Generate() 的调用中传递此对象,从而启动生成过程。

例如:

#include "gwnavgenerationglue/defaultgeneratorglue.h"

bool Generate()
{
    // Create an instance of your GeneratorInputProducer class.
    Kaim::Ptr<MyProducer> producer = *KY_NEW MyProducer;
 
    // Create an instance of the DefaultGeneratorGlue.
    KyGlue::DefaultGeneratorGlue glue;
 
    // Set up a Generator to use your producer.
    Kaim::Generator generator(producer, &glue);
 
    // Set the output directory within which the .NavData and other output files
    // are created. You must use the same absolute root directory every time
    // you create a Generator, but you might set up a different relative directory for
    // each different level. This keeps all of your NavData easily accessible during
    // visual debugging.
    generator.SetOutputDirectory("c:/MyGame/NavData", "MyTestLevel");
 
    // Create a GeneratorInputOutput
    Kaim::GeneratorInputOutput generatorInputOutput;

    // Alternatively, you can create a GeneratorInputOutput by loading it from a
    // .GenIO file created during a previous generation run. This retains all
    // settings from the last run. You can also explicitly save your GeneratorInputOutput
    // object at any time by calling Kaim::GeneratorInputOutput::Save().


    // Use the Kaim::GeneratorParameters and Kaim::GeneratorAdvancedParameters
    // classes that are accessible through generatorInputOutput.m_params and
    // generatorInputOutput.m_params.m_advancedParameters to configure settings
    // such as, the dimensions and movemement capabilities of your characters.
    generatorInputOutput.m_params.m_entityRadius = 0.8f;
    generatorInputOutput.m_params.m_advancedParameters.m_navTagMinPixelArea = 4;

    // Use the Kaim::GeneratorRunOptions class that is accessible through
    // generatorInputOutput.m_runOptions to configure the operation of the
    // generation process itself.
    generatorInputOutput.m_runOptions. m_doMultiCore = true;

    // Add a sector.
    // It is recommended to create a unique GUID for each sector and 
    // even to save it along with your own level definition. This ensures that you
    // always generate with the same GUID for a given level or block of terrain.
    // If you want to generate a random GUID, you can use the
    // KyGlue::DefaultGuidGeneratorInterface class.
    Kaim::Ptr<Kaim::GeneratorSector> sector = *KY_NEW Kaim::GeneratorSector(
            Kaim::KyGuid("AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA"), 
            "MyGameLevel");
 
    generatorInputOutput.AddSector(sector);

 
    // Run a generation using the configuration we have set up.
    if (Kaim::Result::Fail(generator.Generate(generatorInputOutput)))
    {
        return false;
    }
    return true;
}

对于各个地块,生成系统提供了一组相同的输出文件作为 Navigation Lab。它们放在为 Generator 设置的根目录下的子目录中,并根据为 GeneratorSectorConfig 中的地块设置的名称进行命名。您可以从该位置检索 .NavData 文件,以便将其包含在您的游戏中。

使用输出文件的一种替代方法是检索作为原始数据缓冲区的 NavData,该数据可以与您关卡中的其他数据一起打包,以便在运行时游戏中简化数据进出内存的过程。请参见集成阶段 6a:将 NavData 集成到资源管线