쿼리 시스템 사용

Gameware Navigation은 메모리로 로드한 NavData에서 사용할 고성능 런타임 쿼리 세트를 제공합니다. 이러한 쿼리를 사용하면 지세 및 캐릭터의 이동 기회에 대해 인식할 수 있는 광범위한 질문에 대답할 수 있습니다. 그 예는 다음과 같습니다.

쿼리 시스템 개요

쿼리 유형

사용 가능한 쿼리 유형은 다음과 같습니다.

  • CanGo 쿼리 - 광선, 구 또는 세그먼트가 NavMesh 경계와 충돌하지 않고 시작점에서 대상점으로 전달될 수 있는지 테스트합니다.
  • Cast 쿼리 - 광선, 구 또는 세그먼트가 NavMesh 경계와 충돌하지 않고 지정된 방향으로 시작점부터 얼마나 멀리 갈 수 있는지 테스트합니다.
  • NavMesh에서 유효한 위치를 찾는 쿼리
  • 지정한 특정 조건을 충족하는 NavData 요소를 찾는 쿼리(예: 지정된 위치에 가장 가까운 삼각형, 지정된 위치에 가장 가까운 경계)
  • A* 쿼리와 같이 시작점에서 대상점까지 NavMesh에 대해 경로를 계획하는 경로 찾기 쿼리
  • CollisionRayCastQuery - NavData에 대해 충돌 테스트를 수행합니다. 충돌 시스템 사용을(를) 참조하십시오.
  • 기타

각 유형의 쿼리는 자체 클래스에서 캡슐화됩니다. 전체 목록은 IQuery 클래스를 구현하는 클래스를 참조하십시오. 입력 및 출력을 포함하여 각 쿼리 유형에 대한 전체 설명은 해당 클래스 설명을 참조하십시오.

쿼리 및 데이터베이스

각 쿼리는 현재 단일 Database로 로드된 전체 NavData 세트를 고려합니다. 다른 Databases에 대해 병렬로 쿼리 사본을 여러 개 실행할 수 있지만 단일 쿼리는 여러 Databases에 대해 동시에 만들어질 수 없으며, 쿼리 또한 Database로 로드된 섹터의 하위 세트로 제한될 수 없습니다.

원자 대 시간 분할 쿼리

쿼리의 각 클래스는 원자이거나 시간 분할입니다.

  • 원자 쿼리는 단일 프레임에서 계산을 완료합니다. 계산 중에는 호출 스레드를 차단하고 해당 결과를 처리하도록 요청한 직후 사용할 수 있도록 보장합니다. 이러한 쿼리 클래스는 모두 IAtomicQuery에서 파생됩니다.
  • 일반적으로 계산이 좀 더 복잡한 시간 분할 쿼리는 CPU 사용이 집중되는 것을 방지하기 위해 여러 프레임으로 계산을 분할할 수 있습니다. 이러한 쿼리 클래스는 ITimeSlicedQuery에서 파생됩니다.

WorkingMemory

모든 쿼리에는 일부 작업 메모리(임시 데이터를 저장하기 위해 계산 중에 사용하는 버퍼)가 필요합니다. 예를 들어 AStarQuery는 NavData에서 경로를 찾을 때 탐색하는 후보 노드 목록을 저장하기 위해 해당 작업 메모리를 사용해야 합니다.

쿼리를 처리할 때마다 계산 중에 해당 쿼리가 사용할 WorkingMemory 클래스의 인스턴스를 제공할 수 있습니다. 제공하지 않는 경우 쿼리가 탐색하도록 구성된 Database에 의해 유지되는 공유 버퍼를 쿼리에서 사용합니다.

WorkingMemory 인스턴스는 언제든지 하나의 쿼리에서만 사용할 수 있습니다.

즉시 대 비동기 쿼리

두 모드 중 하나에서 만든 각 쿼리를 실행할 수 있습니다.

  • 요청 시 즉시. 이 모드는 쿼리가 처리되는 동안 호출 스레드를 차단하지만 쿼리 결과에 즉시 액세스할 수 있도록 합니다.
  • 비동기적으로, 다음 Gameware Navigation 업데이트 단계 중. 이 모드를 사용하면 호출 스레드가 계산 중에도 다른 작업을 계속할 수 있고 CPU 집중 사용을 방지하기 위해 World에 내장된 시스템을 이용할 수 있습니다. 현재 프레임에서 쿼리를 처리하기 위해 예정된 CPU 시간이 경과한 경우 원자 쿼리의 처리는 다음 프레임으로 연기되고, 시간 분할 쿼리는 일시 중지되었다가 다음 업데이트에서 계속될 수 있습니다. 그러나 이 방법을 사용하면 최소 하나의 프레임에 대해, 경우에 따라 여러 프레임에 대해 쿼리 결과에 액세스할 수 없습니다.

게임 루프 밖에서(예: 데이터 생성 프로세스의 사후 처리 단계) 쿼리를 시작하려는 경우 차단 메서드를 사용해야 합니다.

쿼리 설정

쿼리를 설정하려면

  1. 실행하려는 쿼리 클래스의 인스턴스를 만듭니다.
  2. 해당 BindToDatabase() 메서드를 호출하여 쿼리가 처리 중 NavData를 사용할 Database와 연결합니다. 또한 이 호출에서는 모든 선택적 구성 매개변수의 값을 재설정하며 쿼리를 기본 상태로 반환합니다.
  3. 쿼리 클래스에서 제공하는 액세서 메서드를 호출하여 쿼리에 대한 모든 선택적 구성 매개변수를 설정합니다. 설정할 수 있는 구성 매개변수의 종류는 사용 중인 쿼리 유형에 따라 달라집니다. 자세한 내용은 쿼리 클래스에 대한 클래스 설명을 참조하십시오.
  4. 쿼리의 Initialize() 메서드를 호출하여 필요한 입력 데이터로 설정합니다. 자세한 내용은 쿼리 클래스에 대한 클래스 설명을 참조하십시오. 필요한 경우 게임의 좌표계에서 Gameware Navigation 좌표계로 입력 데이터를 변환하십시오.

    이 호출에서는 다른 모든 구성 매개변수의 값을 재설정하지 않으므로 새 입력 데이터로 쿼리를 초기화할 때마다 재설정하지 않고 동일한 쿼리를 여러 번 실행하는 동안 동일한 구성 값을 다시 사용할 수 있습니다.

예를 들어 다음 코드는 RayCastQuery를 설정합니다.

Kaim::RayCastQuery<Kaim::DefaultTraverseLogic> rayCastQuery;
rayCastQuery.BindToDatabase(m_world->GetDatabase(0));

// Optional configuration ...
rayCastQuery.SetDynamicOutputMode(Kaim::QUERY_SAVE_TRIANGLES);

rayCastQuery.Initialize(start, move2D);

동일한 Database에 대해 동일한 구성을 사용하여 쿼리를 재실행하려면 Initialize()를 호출하여 새 입력 값을 제공하면 됩니다.

NavTag 테스트

여러 유형의 쿼리가 탐색하는 NavData와 관련된 사용자 정의 NavTag를 고려할 수 있습니다. 이러한 쿼리는 모두 템플릿 인수로 술어를 허용합니다. NavTag 간의 전환을 탐지하는 경우 쿼리가 이 술어에서 클래스 메서드를 호출하여 새 NavTag 금지 여부, 전환을 교차할 수 있는지 여부를 결정합니다.

이 술어는 PathFollower 클래스에서 허용된 것과 동일하며 AStarQuery에서 사용된 사용자 정의자와 동일한 메서드를 대부분 사용합니다. 술어 클래스에서 구현해야 하는 인터페이스에 대한 요구 사항에 대한 자세한 내용은 NavTag 금지, 회피 및 선호을(를) 참조하십시오.

NavTag 설정에 대한 자세한 내용 사용자 정의 데이터로 태그 지정을(를) 참조하십시오.

쿼리 시작

즉시 또는 비동기적으로 실행하는지에 따라 쿼리를 다르게 시작합니다.

즉시 모드에서 쿼리 시작

즉시 모드에서 쿼리를 시작하려면 PerformQueryBlocking() 메서드를 호출합니다.

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

rayCastQuery.PerformQueryBlocking();

World의 업데이트가 진행되는 동안 쿼리를 시작하지 않는 한 스레드에서 즉시 쿼리를 시작하는 것이 안전합니다.

비동기적으로 쿼리 시작

World를 다음에 업데이트하는 동안 비동기적으로 쿼리를 시작하려면 World::PushAsyncQuery() 메서드를 호출하고 쿼리 오브젝트를 전달합니다. World는 대기열에 쿼리를 배치하고 예정된 CPU 시간이 허용하는 즉시 쿼리를 수행합니다.

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

m_world->PushAsyncQuery(&rayCastQuery);

자체 QueryQueues 사용

자체적으로 예정된 CPU 시간에서 자체 QueryQueue 오브젝트를 설정하고 쿼리의 처리를 관리할 수 있습니다. QueryQueue를 처리할 때 예정된 CPU 시간이 소모되기 전까지 순서대로 대기열의 앞에서 쿼리를 수행합니다. 그런 다음 다시 처리할 때까지 계산을 일시 중지합니다(일반적으로 다음 프레임까지).

자세한 내용은 QueryQueue 클래스를 참조하십시오.

World의 업데이트가 진행되는 동안 쿼리를 처리하지 않는 한, 즉시 모드에서 쿼리를 실행하는 것과 같이 스레드에서 QueryQueues를 처리하는 것이 안전합니다.

그러나 비동기 모드에서 쿼리 실행과 같이 현재 프레임에서 대기열이 처리를 완료한 직후 각 쿼리의 결과를 사용할 수 있도록 보장하지 않습니다.

쿼리 결과 검색

언제든지 GetResult() 메서드를 호출하여 쿼리의 현재 결과를 검색할 수 있습니다.

모든 쿼리는 쿼리 클래스별 열거를 사용하여 처리 상태 및 결과를 나타냅니다. 이 열거는 쿼리 클래스와 동일한 이름을 가지며 접미사로 Result가 붙습니다. 예를 들어 RayCastQueryRayCastQueryResult를 생성합니다.

비동기적으로 쿼리를 시작하는 경우 결과 코드를 사용하여 쿼리가 성공적으로 처리를 완료했는지 여부를 확인할 수 있습니다. 일반적으로 DONE을 포함하는 열거 값은 쿼리에서 계산이 완료되었다는 것입니다. 예를 들어 처리가 완료될 때까지 RayCastQueryRAYCAST_NOT_INITIALIZED 또는 RAYCAST_NOT_PROCESSED를 반환하고 그런 다음 RAYCAST_DONE_START_OUTSIDE, RAYCAST_DONE_ARRIVALPOS_FOUND_MAXDIST_REACHED, RAYCAST_DONE_LACK_OF_WORKING_MEMORY 등을 반환할 수 있습니다.

쿼리 유형 및 처리 결과에 따라 쿼리의 다른 데이터 구성원에서 추가 출력을 검색할 수도 있습니다. 아래를 참조하십시오.

교차된 데이터 검색

NavMesh를 통해 전파한 일부 쿼리 유형은 교차하는 데이터를 저장할 수 있습니다. 예를 들어 쿼리는 전파하는 동안 교차하는 삼각형 목록 또는 다른 NavTag를 교차하는 하위 세그먼트 목록을 저장할 수 있습니다. 쿼리 후 교차된 데이터 목록을 검색하고 쿼리에서 교차된 지세의 속성을 검색할 수 있습니다. 예를 들어 캐릭터는 고도 등의 변화, 삼각형과 관련된 NavTag를 기반으로 결정을 내릴 수 있습니다.

이러한 쿼리에는 모두 SetDynamicOutputMode() 메서드가 들어 있으며, 기록해야 하는 출력 데이터 유형을 쿼리에게 알려 주려면 이를 호출해야 합니다. 기본적으로 교차된 데이터는 기록되지 않으므로 SetDynamicOutputMode()를 호출하고 DynamicOutputMode 열거에서 원하는 값을 전달하여 명시적으로 쿼리가 기록하도록 지시해야 합니다.

GetQueryDynamicOutput() 메서드를 호출하여 교차된 데이터를 검색할 수 있습니다. 이렇게 하면 QueryDynamicOutput 오브젝트에 대한 포인터를 반환하며 해당 메서드는 저장된 데이터에 대한 액세스 권한을 제공합니다. 필요한 경우 게임의 좌표계로 출력 데이터를 다시 변환해야 합니다.

예를 들어 다음 코드는 교차하는 NavMesh 삼각형을 검색하도록 RayCastQuery를 설정하는 방법을 보여줍니다.

Kaim::RayCastQuery<Kaim::DefaultTraverseLogic> rayCastQuery;
rayCastQuery.BindToDatabase(m_world->GetDatabase(0));
...
// enable saving triangles
rayCastQuery.SetDynamicOutputMode(Kaim::QUERY_SAVE_TRIANGLES);

rayCastQuery.Initialize(start, dir2D, maxdist);
rayCastQuery.PerformQueryBlocking();

// retrieve data
Kaim::QueryDynamicOutput* qoutput = rayCastQuery.GetQueryDynamicOutput();

// iterate through the list of triangles
KyUInt32 numberOfTriangles = qoutput->GetNavTrianglePtrCount();
for (KyUInt32 i_triangle = 0; i_triangle < numberOfTriangles; i_triangle++)
{
   const Kaim::NavTrianglePtr thisTriangle = qoutput->GetNavTrianglePtr(i_triangle);
   // handle the triangle here.
}

시각적 디버깅

각 프레임에서 해당 SendVisualDebug() 메서드를 호출하여 모든 쿼리에 대한 시각적 디버깅 데이터를 렌더링할 수 있습니다. Navigation Lab이 게임에 연결되어 있는 동안 쿼리가 자동으로 표시 목록을 구성하고 이를 Navigation Lab에 전송합니다.

표시 목록의 내용 및 그에 따라 Navigation Lab에 표시되는 데이터는 쿼리 유형에 따라 다릅니다. 쿼리에 표시된 시각적 데이터의 중요성을 해석하려면 sdk₩include₩gwnavruntime₩queries₩blobs 디렉토리의 QueryDisplayListBuilder 클래스에 있는 인라인 구현을 참조하십시오.

쿼리 완료 알림 수신

비동기적으로 쿼리를 수행하거나 쿼리를 처리하기 위해 대기열에 배치하는 경우 현재 처리 상태를 확인하기 위해 쿼리 결과를 폴링하지 않고 쿼리가 완료될 때 알림을 받는 것이 유용할 수 있습니다.

쿼리 완료에 대해 알림을 받으려면

  1. IOnDone 인터페이스를 구현하는 사용자 정의 클래스를 작성합니다.
  2. 쿼리를 시작하기 전에 사용자 정의 클래스의 새 인스턴스를 만들고 쿼리 클래스의 m_onDone 구성원을 새 인스턴스에 대한 포인터로 설정합니다.

실행하기 위해 쿼리를 World로 미는 경우 쿼리가 처리된 후 World를 업데이트하는 동안 오브젝트의 IOnDone::OnDone() 메서드가 자동으로 호출됩니다. 자체 QueryQueue를 사용하는 경우 해당 쿼리가 처리된 후 다음에 QueryQueue::FlushQueries()를 호출할 때 오브젝트가 호출됩니다.

해당 IOnDone::OnDone() 메서드가 호출된 후 쿼리 클래스의 m_onDone 구성원이 KY_NULL로 다시 설정됩니다. 동일한 쿼리 인스턴스를 다시 시작하고 알림을 다시 받으려는 경우 m_onDone 구성원을 다시 설정해야 합니다.

Navigation Lab의 쿼리

Gameware Navigation API의 여러 쿼리도 Navigation Lab에 제공되어 쿼리를 실행하고 쿼리 결과에서 시각적 피드백을 제공합니다. 이는 지세에 생성된 NavData 테스트 및 각 쿼리의 다른 입력 및 출력 값을 파악하는 데 유용할 수 있습니다.

Navigation Lab에서 쿼리 실행에 대한 소개는 Navigation Lab 시작하기을(를) 참조하십시오.