ここでは、ゲームで BoxObstacle を作成、更新および破棄する方法について説明します。
障害物の管理システムにおける BoxObstacle クラスの役割については、「動的な障害物や TagVolume を使用する」を参照してください。
NavTag システムのバックグラウンド情報については、「カスタム データでタグ付けする」を参照してください。
各 BoxObstacle に対して、次の 2 つの回転モデルのいずれかを選択することができます。作成した各 BoxObstacle に対して選択するモードは、ゲーム内に表示される、対応するオブジェクトで望まれた回転によって異なり、ダイナミック回避システムが各障害物を処理する精度に影響を与えます。
車両、ドアなど多くのオブジェクトは水平方向に回転することがほとんどで、ローカルのアップ軸に対して相対的な整合性が維持されます。この場合、障害物のボリュームが、いくつかの垂直円柱に内部的にサンプリングされます。ダイナミック回避システムは、このような円柱を使用して障害物の動きを予測し、回避軌道を生成します。
たとえば、次のイメージのヨー回転ボックスは、ダイナミック回避システムで 5 つの円柱として表現されています。
これにより通常、障害物を避ける必要のある Bot に対して、より精度の高い軌道が生成されます。障害物のローカル アップ軸がグローバル アップ軸に対してほぼ水平な場合に限り、この方法を使用してください。ピッチ & ロール回転にはある程度の許容範囲が認められるため、たとえば車が傾斜のある地形を移動する場合でもこの表現を使用できます。
爆発後の破片やコンクリート ブロック、崩れて転がる大量の木箱などのように、多くのオブジェクトが 3 次元でフリー回転します。この場合、オブジェクトのローカル アップ軸の整合性は維持されないため、オブジェクトを効果的に垂直円柱にサブサンプリングすることはできません。代わりに、障害物のボリュームを囲む垂直円柱が 1 つ作成されます。ダイナミック回避システムは、この円柱を使用して障害物の動きを予測し、回避軌道を生成します。
これにより、キュービック オブジェクトの動きがかなり正確になります。ただし、障害物の長さ、幅、高さの間の差が大きくなるため、上に示した横長のボックスのように、近似値の精度が低下します。このような場合、円柱では、少なくとも 1 方向でボックスが実際の範囲よりも大きく表示される可能性が非常に高くなります。
BoxObstacle の初期化と破棄を管理するクラスをゲーム内で作成する必要があります。ゲーム内に既に Gameware Navigation World 内で BoxObstacle が表しているオブジェクトのライフスパンを管理するクラスがある場合があります。その場合は、そのクラスを使用できます。また、各フレームでのオブジェクトの位置と速度を取得できる、類似オブジェクトが物理システム内に存在していることもあります。
例: [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 はすぐに変更されないことに注意してください。更新は World の次の更新まで遅延されます。
例: [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 に統合することが望ましくなります。これは、ゲーム内の Bot が、パス フォローイングのダイナミック回避フェーズ中にだけ障害物の存在に反応するのではなく、地形を通るパスをプランする場合にもその存在を考慮できるようにするためです。
障害物が静止状態になったら、その障害物の BoxObstacle::DoesTriggerTagVolume() メソッドを呼び出し、true を渡します。World の次の更新時に、障害物によって TagVolume が透過的にスポーンされます。これは位置と寸法が同じで、BoxObstacle を初期化した際に設定した 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; }