Gameware Navigation 提供了一组可针对已加载到内存的 NavData 使用的高性能运行时查询。您可以使用这些查询回答各种有关地形和角色移动机会的感知问题,如:
每种类型的查询均封装在其自己的类中。有关完整列表,请参见可实现 IQuery 类的类。有关每个查询类型的完整说明(包括其输入和输出),请参见其类说明。
每个查询均会考虑当前已加载到单个数据库中的完整 NavData 集。您既不能同时针对多个数据库进行单个查询(尽管可以针对不同的数据库并行执行查询的多个副本),也不能将一个查询限于已加载到数据库中的地块子集。
每个查询均需要一些工作内存:在计算期间用于存储临时数据的缓冲区。例如,AStarQuery 需要使用其工作内存来存储在通过 NavData 查找路径时浏览到的候选节点列表。
每次处理某个查询时,您可以提供 WorkingMemory 类的实例,以供该查询在其计算期间使用。如果未提供实例,查询将使用由数据库保留的共享缓冲区(该查询配置为浏览该数据库)。
请注意,此调用不会重置任何其他配置参数的值,因此您可以在多次运行同一查询时重复使用相同的配置值,而无需在每次使用新的输入数据初始化查询时重新设置。
例如,以下代码设置了 RayCastQuery。
Kaim::RayCastQuery<Kaim::DefaultTraverseLogic> rayCastQuery; rayCastQuery.BindToDatabase(m_world->GetDatabase(0)); // Optional configuration ... rayCastQuery.SetDynamicOutputMode(Kaim::QUERY_SAVE_TRIANGLES); rayCastQuery.Initialize(start, move2D);
如果您想要使用相同的配置针对相同的 Database 重新运行某个查询,只需调用 Initialize() 以提供新的输入值。
许多类型的查询可以考虑与其浏览的 NavData 相关联的自定义 NavTag。这些查询都接受谓词作为模板参数。此查询检测到 NavTag 之间的过渡时会通过此谓词调用类方法,以确定是否应禁止新 NavTag,以及是否可以穿越过渡。
该谓词与 PathFollower 类接受的谓词相同,并与 AStarQuery 使用的自定义谓词命令大多数相同的方法相关。有关您必须在谓词类中实现的界面需求的详细信息,请参见禁止选择、避免选择和优先选择 NavTag。
有关设置 NavTag 的详细信息,另请参见使用自定义数据进行标记。
根据您希望是立即运行查询还是异步运行查询,您可以采用不同的方式启动查询。
要在世界下次更新期间异步启动查询,请调用 World::PushAsyncQuery() 方法并传递查询对象。世界将查询放置在队列中,并在 CPU 预算允许后立即执行该查询。
m_world->PushAsyncQuery(&rayCastQuery);
您可以使用自己的 CPU 预算设置自己的 QueryQueue 对象,以便管理查询的处理。处理 QueryQueue 时,它将按顺序从队列开头处执行查询,直到其 CPU 预算已耗尽。然后它将暂停计算,直到您再次处理(通常在下一帧中)。
有关详细信息,请参见 QueryQueue 类。
类似于以立即模式运行查询,只要不在世界更新时处理 QueryQueue,就可以安全地从任何线程对其进行处理。
您可以通过随时调用查询的 GetResult() 方法来检索其当前结果。
每个查询均可使用特定于查询类的枚举来表示其处理的状态和结果。此枚举与查询类同名,带有后缀 Result。例如,RayCastQuery 会生成 RayCastQueryResult。
请注意,如果异步启动查询,则可以使用结果代码来确定查询是否已成功完成处理。通常,包括 DONE 的任何枚举值均表示查询已完成计算。例如,RayCastQuery 将返回 RAYCAST_NOT_INITIALIZED 或 RAYCAST_NOT_PROCESSED,直到它已完成处理。然后,它可能会返回 RAYCAST_DONE_START_OUTSIDE、RAYCAST_DONE_ARRIVALPOS_FOUND_MAXDIST_REACHED、RAYCAST_DONE_LACK_OF_WORKING_MEMORY 等中的任何一个。
根据查询类型和处理的结果,您还可以从查询的其他数据成员中检索其他输出。请参见以下内容。
通过 NavMesh 传播的某些类型的查询可以存储其跨越的数据。例如,查询可以存储其在传播期间跨越的三角形列表,或跨越不同 NavTag 的子分段列表。在查询之后,您可以检索交叉数据的列表并搜索查询跨越的地形的特性。例如,您的角色可以基于与三角形、海拔高度的变化等关联的 NavTag 做出决策。
这些查询都包含 SetDynamicOutputMode() 方法,您必须调用该方法以告诉查询应该记录的输出数据类型。默认情况下,不会记录交叉数据;您必须显式指示查询通过调用 SetDynamicOutputMode() 并将所需值从 DynamicOutputMode 枚举传递给它来记录交叉数据。
您可以通过调用 GetQueryDynamicOutput() 方法来检索交叉数据。这会将指针返回到 QueryDynamicOutput 对象,该对象的方法可提供对已保存数据的访问权限。请记住将输出数据转换回游戏的坐标系(如有必要)。
例如,以下代码显示了如何设置 RayCastQuery 以检索其跨越的 NavMesh 三角形。
Kaim::RayCastQuery<Kaim::DefaultTraverseLogic> rayCastQuery; rayCastQuery.BindToDatabase(m_world->GetDatabase(0)); ... // enable saving triangles rayCastQuery.SetDynamicOutputMode(Kaim::QUERY_SAVE_TRIANGLES); rayCastQuery.Initialize(start, dir2D, maxdist); rayCastQuery.PerformQueryBlocking(); // retrieve data Kaim::QueryDynamicOutput* qoutput = rayCastQuery.GetQueryDynamicOutput(); // iterate through the list of triangles KyUInt32 numberOfTriangles = qoutput->GetNavTrianglePtrCount(); for (KyUInt32 i_triangle = 0; i_triangle < numberOfTriangles; i_triangle++) { const Kaim::NavTrianglePtr thisTriangle = qoutput->GetNavTrianglePtr(i_triangle); // handle the triangle here. }
通过在每一帧调用查询的 SendVisualDebug () 方法,您可以渲染任何查询的可视调试数据。当将 Navigation Lab 连接到游戏时,查询将自动构建显示列表,并将其发送到 Navigation Lab。
显示列表的内容以及相应在 Navigation Lab 中显示的数据随查询类型变化。要解释为查询显示的可视数据的重要性,请参见在 sdk\include\gwnavruntime\queries\blobs 目录中的 QueryDisplayListBuilder 类的内嵌实现。
当异步执行查询或将查询放置在队列中以供处理时,在查询已完成时接收通知会很有帮助,您无需轮询查询结果以确定处理的当前状态。
如果将查询放入到世界中以供执行,则在查询已处理之后,在世界更新期间会自动调用对象的 IOnDone::OnDone() 方法。如果使用自己的 QueryQueue,则在该查询已处理之后,在下次调用 QueryQueue::FlushQueries() 时会调用对象。
请注意,查询类的 m_onDone 成员会在调用其 IOnDone::OnDone() 方法之后被重置为 KY_NULL。如果希望重新启动相同的查询实例并再次接收通知,则需要再次设置 m_onDone 成员。
Gameware Navigation API 中的许多查询也会显示在 Navigation Lab 中,Navigation Lab 将运行查询并提供有关查询结果的视觉反馈。这可用于测试针对地形生成的 NavData,并了解每个查询的不同输入和输出值。
有关在 Navigation Lab 中运行查询的介绍,请参见 Navigation Lab 快速入门。