在运行时使用 NavData

在游戏中,NavData 是通过使用一个或多个数据库在运行时管理的。数据库是负责为共享相同物理维度和移动模型的角色维护相关 NavData 集的对象。

如果您已学完了本文档中的集成教程(请参见将 Navigation 集成到游戏引擎),应熟悉数据库在运行时加载和卸载 NavData 的基本用法。此页面以及此部分中的其他主题提供了一些其他详细信息和注意事项。

创建数据库

创建世界时,必须指定世界应创建和维护的数据库数量。请参见集成阶段 1c:设置世界

每个数据库只能加载使用同一组配置参数创建的 NavData。如果角色的物理维度或移动功能有很大差异以至于它们不能共享相同的 NavData,则您将需要为世界设置多个数据库。详细信息请参见使用多个数据库

创建世界后,无法更改该世界所维护的数据库数量。

将 NavData 加载到内存中

为了将地形或地形中某个地块的 NavData 添加到数据库中,首先必须创建一个 NavData 对象,通过从文件或内存缓冲区读取数据以使用这些数据设置 NavData,然后使用管理 NavData 的数据库对其进行初始化。

有关说明如何从文件加载的代码示例,请参见集成阶段 3:加载和卸载 NavData

有关说明如何从内存缓冲区加载的代码示例,请参见集成阶段 6a:将 NavData 集成到资源管线

在数据库中添加和移除 NavData

如果要将 NavData 添加到数据库中以使其可用于寻径、路径跟随和查询系统,或者当游戏中不再需要 NavData 时将其从数据库中移除,您可以选择两种方法:即时或异步。

即时

这是添加和移除 NavData 的最简单的方法。使用以下 NavData 类方法:

KyResult NavData::AddToDatabaseImmediate();
KyResult NavData::RemoveFromDatabaseImmediate();

通过此方法,将立即启动数据库的完全更新,并阻止调用线程,直到更新完成。在更新期间,新数据将完全缝合到现有 NavData 中或完全与其余 NavData 脱离。

在确保人物和查询可用的数据始终与最近一次添加或移除保持同步方面,此方法具有明显的优势。但是,由于此方法会致使在当前帧期间完成整个更新,因此在实际产品中使用大型数据集时可能会引起 CPU 峰值。此外,该方法还需要您注意多线程;如果您在另一个线程中执行查询,修改这些查询所依赖的数据可能会导致意外结果,甚至会出现崩溃。因此,即时方法主要适用于初始集成和生成原型阶段,并期望您最终会采用异步方法来添加和移除数据。

但是请注意,如果从游戏循环的上下文外部加载数据(如数据生成过程的后期处理阶段),则必须使用此方法。

异步

添加和移除 NavData 的安全方式是使用以下方法:

KyResult NavData::AddToDatabaseAsync();
KyResult NavData::RemoveFromDatabaseAsync();

使用此方法时,将注册添加或移除 NavData 的请求,并在更新世界期间在后续帧中处理这些请求。

  • 世界更新期间触发异步数据库更新后,在世界中的任何数据库内添加或移除 NavData 的更多请求只有在更新完成后才会进行处理。因此,当您请求异步添加或移除时,根据数据库的当前状态,可能会在下一帧开始异步处理,也可能延迟几帧。
    注意:这也意味着,为获得最佳性能,应尝试将每个世界的所有添加和移除请求分到一组中,并在同一帧中请求处理。
  • 根据数据库中数据的当前状态以及所涉及计算的复杂性(包括会对必须考虑在内的 NavMesh 进行动态修改的 TagVolume 数量),可能会在单个帧内完成处理,也可能跨多个帧根据时间片进行处理。
  • 唯一能保证的是,至少有一个帧完成后数据库才反映新的数据集。

数据库可应对所有涉及跟踪数据添加和移除过程的复杂性。但是,跟踪请求状态非常有用,有助于您了解人物和查询是否正在使用数据库中 NavData 集的最新更改。每个 NavData 对象都具有自己的状态值,该值可通过 NavData::m_databaseStatus(表示该 NavData 在其指定数据库内的当前状态)进行访问。

下图概述了如何设置这些状态值以响应添加和移除请求。状态值以红色显示,用户启动的操作以黑色显示,内部操作以蓝色显示:

从内存中卸载 NavData

数据库中移除 NavData 对象后,应始终将任何指向该 NavData 对象的指针设置为 KY_NULL。这会递减引用统计机制。