충돌 시스템 사용

Gameware Navigation에는 레벨 형상에 대해 공간의 두 점 사이에서 고성능 충돌 테스트(광선 투사)를 수행할 수 있는 복잡하지 않은 경량 시스템이 포함되어 있습니다.

예를 들어 이 시스템을 사용하여 다음을 수행할 수 있습니다.

이러한 테스트는 물리적 하위 시스템에 의해 수행되는 경우가 자주 있습니다. 그러나 많은 게임(대부분 MMO)에서 네트워크 통신 속도 지연, 기술 복잡성 및/또는 런타임 성능 오버헤드는 완벽한 기능을 갖춘 물리 시스템을 통합하는 데 있어 문제 또는 비용 부담을 야기할 수 있습니다. 이러한 상황이 사용자의 프로젝트에 해당되지만 게임 레벨의 형상에 대해 이러한 종류의 충돌 테스트를 여전히 수행해야 하는 경우 Gameware Navigation 충돌 시스템을 활용할 수 있습니다.

정적 오브젝트만 해당

기본 제공 충돌 시스템은 지세에 대한 NavData와 구분되는 사전 생성된 데이터를 사용합니다. 따라서 데이터 생성 프로세스 중 제공한 지세의 정적 요소에서만 충돌을 감지할 수 있습니다. 이 시스템은 런타임 시 추가되는 동적 오브젝트를 지세에 대한 NavData에 통합하는 경우에도 해당 오브젝트를 고려하지 않습니다.

전제 조건

충돌 데이터를 생성하려면 C++ NavData 생성 API를 사용합니다. NavData 생성 API를 게임 편집기, 레벨 디자인 도구 또는 빌드 프로세스에 아직 통합하지 않은 경우 통합 단계 6: NavData 생성 API 사용을(를) 참조하십시오.

단계 1: 충돌 데이터 생성

NavData 생성 코드에서 지세의 원하는 각 섹터에 대해 충돌 데이터 생성을 활성화해야 합니다.

섹터에 대해 충돌 데이터 생성을 활성화하려면

섹터에 대해 NavData를 생성하면 해당 섹터에 대해 NavData 생성 시스템으로 전달하는 삼각형에서 충돌 데이터도 자동으로 생성됩니다. 각 섹터에 대한 데이터는 해당 섹터에 대해 생성되는 .NavData 파일과 동일한 파일 이름을 사용하여 동일한 위치에 .ColData 파일로 디스크에 기록됩니다.

예를 들면 다음과 같습니다.

// Create a sector...
Kaim::GeneratorSectorConfig sectorConfig(Kaim::KyGuid::GetDefaultGuid(), generationName);
Kaim::GeneratorSector* sector = env.m_generatorInputOutput.AddSector(sectorConfig);
...
// Activate collision data generation
sector->SetColDataBuildMode(Kaim::GenFlags::SECTOR_COLDATA_BUILD_ENABLED);
...
// Generate NavData and collision data
KyResult result = m_generator->Generate(m_generatorInputOutput);

옵션: 성능 향상을 위해 heightfields 사용

지세의 각 섹터를 구성하는 개별 삼각형에 NavData 생성 시스템을 제공하는 대신 heightfields 및 삼각형 메시를 제공할 수 있습니다.

heightfield는 간단한 단층 지세의 고도를 나타내는 최적화된 방법입니다. 지세 메시를 구성하는 모든 삼각형을 포함하는 대신 단일 원점과 원점 주변의 일반 그리드에서 샘플링된 고도 값 세트로 구성됩니다. 이 데이터는 전체 삼각형 메시보다 훨씬 더 간단하고 덜 복잡하기 때문에 런타임 메모리 사용이 줄고 충돌 테스트의 성능이 훨씬 더 향상됩니다.

heightfields 사용의 부정적인 측면은 일반적으로 그리드에 있는 샘플의 간격 내에서 발생하는 높이의 로컬 변형에 대해 정확성이 떨어질 수도 있다는 점입니다. 그러면 NavMesh가 heightfields에서 생성될 수 있으므로 이에 대해 수행되는 충돌 테스트의 정확성이 떨어질 수 있습니다. 이렇게 정확성이 떨어지는 문제를 해결하기 위해 지세의 더 작은 기능에 대해 보다 정확한 사실성이 요구되는 보다 복잡한 지세 영역에는 삼각형 메시를 제공할 수도 있습니다.

효율성과 정확성 사이에서 최상의 절충안을 얻으려면 heightfields를 사용하여 벌판과 같이 넓게 펼쳐진 영역을 나타내고 삼각형 메시를 사용하여 도시 블록과 같이 보다 많은 정확성이 필요한 영역을 나타냅니다. 예를 들면 다음과 같습니다.

섹터에 heightfields 및 인덱스 처리된 메시를 제공하려면 NavData 생성 파이프라인에서 사용하는 GeneratorInputProducer에서 GeneratorInputProducer::Produce()의 구현을 업데이트하여 NavData 생성 시스템에 지세 형상을 전달해야 합니다.

섹터에 heightfields를 제공하려면

  • 섹터에 제공하려는 각 heightfield에 대해 GeneratorInputProducerKaim::Heightfield 클래스의 인스턴스를 만들고 현재 섹터에 대한 데이터로 설정하며 ClientInputConsumer::ConsumeHeightField()를 호출하여 포인터를 전달해야 합니다.

섹터에 삼각형 메시를 제공하려면

  • 섹터에 제공하려는 각 메시에 대해 GeneratorInputProducerKaim::IndexedMesh 클래스의 인스턴스를 만들고 현재 섹터에 대한 데이터로 설정하며 ClientInputConsumer::ConsumeIndexedMesh()를 호출하여 포인터를 전달합니다.

이 방법을 사용할 경우 heightfields 및 삼각형 메시에 NavData가 생성됩니다. 따라서 개별 삼각형을 ClientInputConsumer로 직접 제공할 필요가 없습니다.

단계 2: 런타임 World 설정

WorldCollisionWorld 클래스의 인스턴스를 해당 World::m_collisionWorld 클래스 구성원에서 유지합니다. 이 클래스는 메모리에 로드되는 충돌 데이터 집계와 해당 데이터에 대해 수행하는 충돌 테스트 관리를 담당합니다. 이 작업은 World를 초기화한 후 제공해야 하는, ICollisionInterface를 구현하는 클래스 인스턴스의 메서드를 호출하여 수행합니다.

Gameware Navigation에는 오픈 소스 Bullet 물리학 및 충돌 테스트 시스템을 사용하는 ICollisionInterface 구현이 포함되어 있습니다. 자세한 내용은 www.bulletphysics.org를 참조하십시오.

기본 ICollisionInterface 구현을 사용하려면

  1. integration₩gwnavruntimeglue₩bulletcollisioninterface 디렉토리에 있는 모든 헤더 및 소스 파일을 프로젝트에 추가하고 해당 디렉토리에서 포함 파일을 찾도록 컴파일러를 구성합니다.
  2. 런타임 초기화 코드에서 CollisionInterface 클래스의 새 인스턴스를 만듭니다.
  3. World를 만든 후에는 CollisionWorld::SetCollisionInterface() 메서드를 호출할 때 전달하여 인터페이스 오브젝트를 사용하도록 World::m_collisionWorld를 설정합니다.

예를 들면 다음과 같습니다.

m_navigationWorld = *KY_NEW Kaim::World(databaseCount);
Kaim::Ptr<Kaim::ICollisionInterface> visInterface = *KY_NEW CollisionInterface(m_navigationWorld);
m_navigationWorld->m_collisionWorld->SetCollisionInterface(visInterface);

단계 3: 런타임 시 충돌 데이터 로딩

지세에 대해 만든 충돌 데이터에 대해 충돌 테스트를 수행해야 하는 경우 충돌 데이터를 메모리로 로드하고 충돌 표준에 추가해야 합니다. 일반적으로 이 작업은 메모리 내부 및 외부의 해당 섹터에 필요한 NavData 및 다른 종류의 데이터를 스트리밍할 때 동시에 지세의 각 섹터에 대해 수행합니다. 이 프로세스는 Database 내부 및 외부의 NavData 스트리밍과 매우 유사합니다.

런타임 시 충돌 표준으로 충돌 데이터를 로드하려면

  1. CollisionData 오브젝트를 만듭니다.
  2. 충돌 데이터를 오브젝트로 로드합니다. 사전 생성된 NavData로 NavData 오브젝트를 채우는 데 사용하는 방법과 정확히 동일한 방법을 사용할 수 있습니다.

    • CollisionData::Load()를 호출하여 데이터를 디스크의 .ColData 파일에서 직접 로드하거나 파일 스트림에서 로드합니다.
    • 충돌 데이터를 메모리로 로드하고 CollisionData::LoadFromMemory()를 호출하여 제공한 버퍼에서 CollisionData 오브젝트를 초기화합니다. 이 메모리 버퍼는 파일에서 직접 읽거나 통합 단계 6a: 자산 파이프라인으로 NavData 통합에서 설명한 대로 섹터와 관련된 다른 자산과 함께 패키지에 포함하여 가져올 수 있습니다.

    두 방법 모두 동일합니다. 두 방법 중 자산 파이프라인에 더 적합한 방법을 사용할 수 있습니다.

  3. World::m_collisionWorld에서 유지되는 CollisionWorld 오브젝트를 검색하고 해당 CollisionWorld::AddCollisionData() 메서드를 호출한 다음 CollisionData 오브젝트에 대한 포인터를 전달합니다.

예를 들면 다음과 같습니다.

Kaim::Ptr<Kaim::CollisionData> colData = *KY_NEW Kaim::CollisionData;
if (Kaim::Result::Fail(colData->Load(GetAbsoluteInputFileName(fileName).c_str(), &m_fileOpener)))
		return KY_NULL;

GetWorld()->m_collisionWorld->AddCollisionData(colData);

충돌 표준에서 충돌 데이터를 제거하려면

예를 들면 다음과 같습니다.

GetWorld()->m_collisionWorld->RemoveCollisionData(colData);

단계 4: 충돌 쿼리 수행

World로 로드한 충돌 데이터에 대해 충돌 테스트를 실행하려면 CollisionRayCastQuery 클래스의 인스턴스를 설정하고 시작합니다.

예를 들어 다음 코드는 점 A와 점 B 사이의 직선 경로를 따라 충돌을 테스트합니다.

Kaim::CollisionRayCastQuery query;
query.BindToWorld(m_navigationWorld);
query.Initialize(pointA, pointB);
query.PerformQuery();

if (query.GetResult() == Kaim::RayHit)
    ... // A collision was found
if (query.GetResult() == Kaim::RayDidNotHit)
    ... // No collision was detected

또한 쿼리 시스템에서 제공하는 메커니즘을 사용하여 World에서 유지하는 대기열 또는 직접 만들고 프레임당 최대 시간 예산으로 설정한 사용자 정의 대기열에서 비동기로 CollisionRayCastQuery를 실행할 수 있습니다. 게임이 동일한 프레임에서 여러 가지 충돌 테스트를 요청해야 하는 경우 이러한 방법을 사용하면 CPU 피크를 방지할 수 있습니다.

자세한 내용은 쿼리 시스템 사용을(를) 참조하십시오.