이 페이지에서는 게임에서 BoxObstacles를 생성, 업데이트 및 삭제하는 방법을 설명합니다.
장애물 관리 시스템에서 BoxObstacles 클래스의 역할에 대한 설명은 동적 장애물 및 TagVolume 사용을(를) 참조하십시오.
NavTag 시스템에 대한 배경 정보는 사용자 정의 데이터로 태그 지정을(를) 참조하십시오.
각 BoxObstacle에 대해 가능한 두 가지 회전 모델 중 하나를 선택할 수 있습니다. 사용자가 만든 각 BoxObstacle에 대해 선택해야 하는 모드는 해당 오브젝트가 게임에 표시되는 데 필요한 회전에 따라 다르며, 동적 회피 시스템에서 각 장애물을 처리하는 정밀도에 영향을 줍니다.
차량이나 문과 같은 많은 오브젝트는 비교적 일관된 로컬 상향 축을 유지하면서 주로 수평으로 회전합니다. 이 경우 장애물의 볼륨은 내부적으로 여러 수직 원통으로 샘플링됩니다. 동적 회피 시스템에서는 이러한 원통을 사용하여 장애물의 이동을 예측해 회피 궤적을 생성합니다.
예를 들어, 다음 이미지의 수평 회전 상자는 동적 회피 시스템에서 5개의 원통으로 표시됩니다.
이는 장애물을 피해야 하는 Bot에 대한 보다 정확한 궤적을 생성합니다. 장애물의 로컬 상향 축이 전역 상향 축에 거의 정렬된 상태로 유지되는 한 이 방법을 사용해야 합니다. 피치 및 롤 회전에는 약간의 공차가 있으므로 예를 들어 차량이 경사진 지형에서 이동 중인 경우에도 이 표현을 사용할 수 있습니다.
폭발 후의 잡석 또는 콘크리트 블록이나 무너지고 구를 수 있는 상자 더미와 같은 많은 오브젝트는 세 차원 모두에서 자유롭게 회전해야 합니다. 이러한 경우에는 오브젝트의 로컬 상향 축이 일정하게 유지되지 않으므로 오브젝트를 수직 원통으로 효과적으로 하위 샘플링할 수 없습니다. 대신 단일 수직 원통을 만들어 장애물의 볼륨을 둘러싸야 합니다. 동적 회피 시스템에서는 이 단일 원통을 사용하여 장애물의 이동을 예측해 회피 궤적을 생성합니다.
이 방법은 정육면체 오브젝트에 적절합니다. 그러나 위에 표시된 긴 상자와 같이 장애물의 길이, 폭 및 높이 간의 차이가 큰 경우에는 근사치의 정확성이 떨어집니다. 이 경우 원통은 하나 이상의 방향에서 상자의 실제 범위보다 과대 평가될 가능성이 높습니다.
게임에서 BoxObstacle의 초기화 및 파괴를 관리하는 클래스를 만들어야 합니다. BoxObstacle이 Gameware Navigation World 내에서 나타내는 오브젝트의 수명을 관리하는 클래스가 게임에 이미 있을 수도 있습니다. 이 경우 해당 클래스를 사용할 수 있습니다. 또한 각 프레임에서 오브젝트의 위치 및 속도를 가져올 수 있는 유사 오브젝트가 물리 시스템에 있을 수도 있습니다.
예: [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을 초기화하려면 BoxObstacle에 필요한 데이터를 사용하여 설정하는 BoxObstacleInitConfig 구성 클래스의 인스턴스를 제공해야 합니다. 적어도 다음을 설정해야 합니다.
예: [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());
}장애물의 이동이 중지되고 안정된 경우 게임의 Bot이 경로 따르기의 동적 회피 단계 중에 장애물의 존재에 반응하는 대신 지형을 통과하는 경로를 계획할 때 장애물의 존재를 고려할 수 있도록 NavMesh에 장애물을 통합할 수 있습니다.
장애물이 정지되면 해당 BoxObstacle::DoesTriggerTagVolume() 메서드를 호출하고 true를 전달합니다. World의 다음 업데이트 중에 동일한 위치 및 치수와 BoxObstacle을 초기화할 때 설정한 NavTag가 지정된 TagVolume이 장애물에 투명하게 생성됩니다. 그런 다음 이 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;
}