设置 BoxObstacle

此页面介绍了如何创建、更新和销毁您游戏中的 BoxObstacle

有关障碍物管理系统中 BoxObstacle 类的作用的介绍,请参见使用动态障碍物和 TagVolume

有关 NavTag 系统的背景信息,请参见使用自定义数据进行标记

旋转模型

您可以为每个 BoxObstacle 选择两种可能的旋转模型之一。应为您创建的每个 BoxObstacle 选择的模式取决于相应对象将在游戏中显示的预期旋转,所选的模式将影响动态回避系统处理每个障碍物时的精度。

偏航(水平)旋转

许多对象(如车辆或门)多半水平旋转,保持相对一致的局部向上轴。在这种情况下,障碍物的体积将在内部采样到几个垂直圆柱体。动态回避系统使用这些圆柱体来预测障碍物的移动,以便生成回避轨迹。

例如,下图中的偏航旋转长方体在动态回避系统中表示为五个圆柱体:

这通常会为需要回避障碍物的人物生成更精确的轨迹。只要障碍物的局部向上轴与全局向上轴大致保持对齐,您就应该使用此方法。进行上下倾斜和滚动旋转时存在一些容差,这样即使(例如)汽车在倾斜的地形上移动时,您也可以使用这种表示方法。

自由旋转

许多对象需要在所有三个维度中自由旋转,如爆炸之后的碎石或混凝土块,或者一堆可进行翻滚和滚动的木箱。在这种情况下,对象不能有效地子采样到垂直圆柱体,因为对象的局部上方向轴不会保持一致。而一个垂直圆柱体将创建用于封闭障碍物的体积。动态回避系统使用此圆柱体来预测障碍物的移动,以生成回避轨迹。

这会为立方体对象生成相当精确的轨迹。但是,由于障碍物(如上所示的长方体)的长度、宽度和高度存在很大差异,因此降低了近似的精确度。在这些情况下,圆柱体很有可能至少在一个方向过高预测长方体的实际范围。

游戏端类

您需要在游戏中创建一个用于管理 BoxObstacle 的初始化和销毁的类。您的游戏中可能已经有一个类在管理 BoxObstacle 计划在 Gameware Navigation 世界中表示的对象或玩家角色的寿命。如果是这样,则可以使用该类。您可能在物理系统中也有类似对象,用于检索对象在每一帧的位置和速度。

例如:[Tutorial_ObstacleIntegration.cpp 中的代码]

class MyGameBoxObstacle
{
public:
	MyGameBoxObstacle(): m_navBoxObstacle(KY_NULL) {}

	void Initialize(Kaim::World* world, const Kaim::Vec3f& boxHalfExtents, const Kaim::Vec3f& position, const Kaim::Vec3f& linearVelocity, const Kaim::Vec3f& angularVelocity);
	void Destroy();
	void Update(KyFloat32 simulationStepsInSeconds);

	RigidBodyPhysics m_rigidBodyPhysics;
	Kaim::Ptr<Kaim::BoxObstacle> m_navBoxObstacle;
};

初始化

要初始化 BoxObstacle,您必须提供 BoxObstacleInitConfig 配置类的实例,您可以使用 BoxObstacle 所需的数据进行设置。您必须以最小值设置:

例如:[Tutorial_ObstacleIntegration.cpp 中的代码]

void MyGameBoxObstacle::Initialize(Kaim::World* world, const Kaim::Vec3f& boxHalfExtents, const Kaim::Vec3f& position, const Kaim::Vec3f& linearVelocity, const Kaim::Vec3f& angularVelocity)
{
	// Initialize the object that represents this obstacle in the physics system.
	m_rigidBodyPhysics.Initialize(position, linearVelocity, angularVelocity);

	// Set up the BoxObstacleInitConfig.
	Kaim::BoxObstacleInitConfig boxObstacleInitConfig;
	boxObstacleInitConfig.m_world = world;
	boxObstacleInitConfig.m_localHalfExtents = boxHalfExtents;
	boxObstacleInitConfig.m_startPosition = m_rigidBodyPhysics.GetPosition();
	boxObstacleInitConfig.m_rotationMode = Kaim::BoxObstacleRotation_Yaw;
	// This sets the NavTag as non-walkable: it will "cut a hole" in the NavMesh.
	boxObstacleInitConfig.m_navTag.SetAsExclusive();

	// Create and initialize the BoxObstacle, and add it to its World
	m_navBoxObstacle = *KY_NEW Kaim::BoxObstacle;
	m_navBoxObstacle->Init(boxObstacleInitConfig);
	m_navBoxObstacle->AddToWorld();

	...
}

更新

BoxObstacle 表示的对象每次在游戏中更改其位置、旋转或速度时,您需要更新 BoxObstacle 以反映新的状态。请注意,这包括障碍物在空间中移动时的线性速度,以及旋转时的角速度。您可以使用 BoxObstacle 中的函数反映新的状态。

请注意,此操作不会立即修改 BoxObstacle;更新将推迟到世界的下次更新。

例如:[Tutorial_ObstacleIntegration.cpp 中的代码]

void MyGameBoxObstacle::Update(KyFloat32 simulationStepsInSeconds)
{
	...
	// Update the object in the physics system.
	m_rigidBodyPhysics.Update(simulationStepsInSeconds);

 	// Update the BoxObstacle.      
	m_navBoxObstacle->SetTransform(m_rigidBodyPhysics.GetTransform());
	m_navBoxObstacle->SetLinearVelocity(m_rigidBodyPhysics.GetLinearVelocity());
	m_navBoxObstacle->SetAngularVelocity(m_rigidBodyPhysics.GetAngularVelocity());
}

在运动与静止之间切换

如果障碍物停止运动并静止下来,您可能会希望将其集成到 NavMesh,以便游戏中的人物在规划通过地形的路径时可以考虑到它的存在,而不是仅在路径跟随的动态回避阶段对其存在做出反应。

障碍物变为静止状态时,请调用其 BoxObstacle::DoesTriggerTagVolume() 方法,并传递 true。下次更新世界时,障碍物将以相同的位置、尺寸透明地生成 BoxObstacle,并带有初始化 TagVolume 时设置的 NavTag。然后此 TagVolume 会集成到 NavMesh 中,并以自定义数据对区域进行标记或将其标记为“排他”然后从 NavMesh 中移除。集成完成且 NavMesh 更新后,障碍物的体积会自动从所有动态回避计算中移除,因为其存在现在是通过 NavMesh 进行管理。

如果障碍物再次开始移动,请调用 BoxObstacle::DoesTriggerTagVolume() 并传递 false 以反转过程。

例如:[Tutorial_ObstacleIntegration.cpp 中的代码]

void MyGameBoxObstacle::Update(KyFloat32 simulationStepsInSeconds)
{
	const bool isStopped = m_rigidBodyPhysics.IsSleeping();
	m_navBoxObstacle->SetDoesTriggerTagVolume(isStopped);
	if (isStopped)
		return;
	...
}

销毁

销毁 BoxObstacle

例如:[Tutorial_ObstacleIntegration.cpp 中的代码]

void MyGameBoxObstacle::Destroy()
{
	m_navBoxObstacle->RemoveFromWorld();
	m_navBoxObstacle = KY_NULL;
}