UDN
Search public documentation:

NavMeshConstraintsAndGoalEvaluatorsKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > AI와 내비게이션 > 내비메시 패쓰 컨스트레인트와 골 이밸류에이터

내비메시 패쓰 컨스트레인트와 골 이밸류에이터


문서 변경내역: Matt Tonks 작성. 홍성진 번역.

개요


길찾기와 일반 경로 탐색에 관련된 패쓰 컨스트레인트(constraint, 제약)와 패쓰 골 이밸류에이터(evaluator, 측정기)의 용도와 사용법에 대한 개요서입니다.

왜?

커스터마이징 가능한 모듈식 컨스트레인트의 도입을 통해, AI 프로그래머는 게임에 딱 맞춘 패쓰 질의를 아주 약간의 코드 부하만으로도 실행시킬 수 있게 되었습니다. 그런데 옛날에 적을 피해 A에서 B로 가는 길을 찾아라 같은 것을 하려 했다면? 길이 막히는 상황마다 네이티브 코드에 함수를 추가해야 했겠지? 앞으로도 그런 상황이 생길 때마다 계속 그렇게 해 줘야 했겠지? 그렇게 되면 금방 다루기 어려워 지겠지? 각기 다른 고려사항 조합(, 즉 컨스트레인트)마다 별도의 패쓰 검색이 필요할테고, 그러면 각각의 새로운 질의에 대해 새로운 코드를 고려해야 하겠지? 안돼~!

그 대안으로 PathConstraint (패쓰 컨스트레인트)라 불리는 독립형 오브젝트 속에다 관련된 것들을 모두 꾸려넣고 있는데, 이 패쓰 컨스트레인트는 그 내용 전부가 길찾기 도중 고려되는 목록에 추가됩니다. 즉 목적지까지 최단 경로를 찾고자 하는 경우라면 그냥 Path_TowardGoal 컨스트레인트만 추가시켜 주면 되며, AI 프로그래머가 선호하는 고전적인 직선 A* 휴리스틱이 나옵니다. 그러나 좀 더 복잡한, 예를 들면 A -> B 까지 불을 피해서 가는 최단 경로를 구해라 정도라 하더라도, 그냥 Path_TowardGoal 컨스트레인트에다 Path_AvoidFire (가제) 컨스트레인트를 추가시켜 주면 됩니다. 이는 스크립트로도 쉽게 접근 가능하기에, 커스텀 일회성 경로 질의도 상황에 딱 맞춰 손쉽게 해결할 수 있습니다.

그러나 컨스트레인트 만으로는 반쪽짜리일 뿐입니다. 패쓰 검색 완료 조건을 지정하는 기능도 있어야 하고, 탐색 도중 데이터를 질의하는 기능도 있어야 합니다. 그게 바로 패쓰 골 이밸류에이터가 하는 일입니다. 둘 다 언제 검색이 완료되었는지(, 즉 적합한 골 노드를 찾았는지)는 물론, 계산된 데이터의 저장 및 질의도 처리합니다. 예를 들어 Gears of War 에서 커버를 검색할 때, 커버 골 이밸류에이터 가 사용됩니다. 이 커스텀 골 이밸류에이터는 만족할 만한 커버 노드를 찾을 때까지 검색을 계속하면서, 여지껏 찾은 커버노드에 점수를 매겨 기록을 유지합니다. 가장 기본적인 (그리고 흔히 사용되는) 패쓰 골 이밸류에이터는 Goal_AtActor 로, 검색 골 지점이 포함된 내비메시 폴리곤이 검색되면 단순히 검색을 중지하는 것입니다. 바로 A -> B 최단 경로를 찾는 데 사용되는 골 이밸류에이터인 것입니다.

컨스트레인트 및 골 이밸류에이터는 내비게이션 핸들 속의 리스트 둘에 저장됩니다.

패쓰 컨스트레인트


앞서 말한 대로 컨스트레인트는 저장된 실제 거리(g)는 물론 휴리스틱 거리(h)도 변경할 수 있습니다. 끝으로 패쓰 컨스트레인트는 특정 노드에 거짓을 반환할 수도 있는데, 그 노드는 전혀 맞지 않아 열린 리스트에 추가해서는 안된다는 것을 가리킵니다.

패쓰 컨스트레인트를 사용해야 하는 경우:

  • 골을 향한 탐색 형태를 잡을 때 (골 쪽의 노드를 먼저 시도하여 검색 최적화)
  • 조건적 탐색불가능 영역을 통해 탐색을 제한할 때 (불, 용암, 아군 전용 문 등)
  • 바람직하지 않은 오브젝트의 탐색 회피 (적 플레이어 등)

패쓰 컨스트레인트를 사용하지 말거나 해서는 안되는 경우:

  • 데이터 수집/저장하기 (예전 컨스트레인트가 거짓을 반환하면 컨스트레인트가 호출되지 않을 수 있음)

패쓰 골 이밸류에이터


패쓰 골 이밸류에이터는 시작/끝 노드 결정, 어떤 이유로 검색이 중지됐을 때 '골'을 호출할 노드 결정과 같은 기본 길찾기 함수성을 더 많이 다루기에 약간 더 복잡합니다. 골 이밸류에이터는 검색을 구성한 다음 언제 완료될지를 결정하고, 패쓰가 검색되면 그것을 내비게이션 핸들에 다시 저장하기도 합니다.

ALERT! 주: 목록의 pathgoalevaluator는 특별한 의미를 갖습니다. 마스터 측정기로 간주되어, (seedworkingset, initializesearch 등의) 검색 초기화에 대한 제어권을 갖습니다.

전형적인 패쓰 골 이밸류에이터의 생애는 이와 같습니다:

  1. InitializeSearch() - 검색 초기화 함수는 패쓰 검색이 시작될 때 호출됩니다. 검색을 진행되게 하기 위해 필요한 곳에 골 이밸류에이터를 구성해 줍니다. 디폴트 함수성은 SearchStart 를 포함하는 폴리곤을 검색하여 AnchorPoly 로 설정하는 것입니다.
  2. SeedWorkingSet() - 시드 워킹 세트 함수가 다음에 호출됩니다. 이 함수는 검색을 시작할 폴리곤을 하나 이상 열린 리스트에 채워넣는 것을 담당합니다. 디폴트 함수성은 단순히 앵커 폴리곤을 워킹 세트에 추가시키는 것입니다.

이제 패쓰 검색의 메인 루프에 들어갑니다.
  1. EvaluateGoal() - 골 측정 함수는 워킹 세트에서 최적의 노드를 뽑아낸 이후 호출됩니다. 이 함수는 패쓰 골 이밸류에이터 목록을 돌아가며 그 각각에서 EvaluateGoal() 함수를 호출합니다. 이를 통해 목록의 각 측정기는 폴리곤을 질의할 수도, 적절한 노드를 찾았을 경우 검색을 중지시키기 위한 호출을 할 수도 있습니다.
  2. DetermineFinalGoal() 최종 골 결정 함수는 (골을 찾았거나 검색에서 테스트할 노드가 더이상 없거나 해서) 패쓰가 완성되면 호출됩니다. 이를 통해 골 이밸류에이터가 마지막으로 계산하여 실제 최적의 골을 (있다면) 결정합니다. 여기서 디폴트는 골을 찾았는지만 검사하는 것이나, Goal_AtCover 같은 복잡한 측정기에서는 가장 잘 들어맞는 골을 결정하기 위해 지금까지 방문한 폴리곤 저장소를 취하기도 합니다.
  3. SaveResultingPath() 결과 패쓰 저장 함수는 측정기의 가장 마지막에 호출되는 함수입니다. 거의 대부분의 골 이밸류에이터에서 이 함수는 단순히 지나온 길을 거꾸로 걸어 시작부터 끝까지의 길을 저장하는 것이었습니다. 그러나 이런 방식으로 커스텀 측정기가 여기서 다른 작업을 하게 할 수 있습니다. 예를 들어 (골에서 시작까지의 길찾기를 하는) Goal_ClosestActorInList 는 시작으로 되돌아가기에 패쓰를 정상 순서로 저장합니다 (자세한 것은 아래 측정기 참고).
  4. NotifyExceededMaxPathVisits() 최대 패쓰 방문 수를 넘으면 알림 함수는 예외적인 경우에 호출되는 또하나의 함수입니다. 방문한 폴리곤의 수가 내비게이션 핸들에 있는 MaxPathVisits 파라미터를 초과할 때 호출됩니다. 보통 이런 상황은 실패임을 (, 즉 최대 샘플 크기 내에서 골을 찾지 못했다는 것을) 나타냅니다. 그러나 가끔은 단지 검색이 완료되었음을 뜻하기도 합니다. (예로 Goal_Null 같은 것은 더이상 노드가 없거나 최대 제한에 달할 때까지 검색하는 것으로, 한 구역에서 최적의 노드를 찾을 때 좋습니다.)

새로운 패쓰 골 이밸류에이터에서 구현해 줘야 하는 유일한 함수는 EvaluateGoal() 입니다. 이 함수가 골 이밸류에이터(, 즉 적절한 골을 찾은 시점을 결정)의 주요 함수입니다.

EvaluateGoal() 작동 방식에 영향을 끼치는 불리언이 둘 있습니다. 탐색의 99%는 디폴트 세팅을 사용하(고 실제로 대부분의 탐색에는 하나의 골 이밸류에이터만 있기에 다른 옵션이 등장할 여지조차 없기는 하)나, 골 측정기 구성 작업을 쉽게 하기 위해 다음의 불리언 둘을 추가하게 되었습니다:

  • bUseORforEvaluateGoal (UNavigationHandle 에서) EvaluateGoal에 OR 사용 - 디폴트로 모든 골 이밸류에이터가 EvaluateGoal() 에서 참을 반환해야 검색이 완료됩니다. 이는 내비게이션 핸들의 bUseORforEvaluateGoal 을 통해 변경할 수 있습니다. (참이면 EvaluateGoal() 의 아무 골 이밸류에이터나 참을 반환해도 검색이 중지됩니다.)
  • bAlwaysCallEvaluateGoal (GoalEvaluator 에서) EvaluateGoal() 항상 호출 - 특정 골 이밸류에이터에 이 옵션이 참이 되면, 현재 노드가 최종 골이라고 혹은 아니라고 결정이 났어도 그 골 이밸류에이터의 EvaluateGoal() 함수가 호출됩니다. 이는 정보를 저장해야 하거나 각 패쓰 반복처리에 뭔가를 계산할 필요가 있는 골 이밸류에이터에 좋습니다.

컨스트레인트/골측정 풀링


쓸데없이 패쓰 컨스트레인트/골 이밸류에이터를 항시 new 하지 않게 하기 위해, 요청시에만 new 하여 캐시합니다. 이를 통해 많은 수의 컨스트레인트를 가비지 컬렉팅 및 new 를 하지 않고도 캐시된 컨스트레인트와 골 측정을 재사용할 수 있게 됩니다.

이 목적을 위해서는 절대로 PathConstraint 또는 GoalEvaluator 를 직접 new 하지 말아야 합니다. 대신 NavigationHandle 의 CreatePathConstraint() 또는 CreatePathGoalEvaluator() 를 호출해야 합니다. 없을 경우 그 컨스트레인트의 사본을 새로 만들거나, 캐시된 사본을 구해 옵니다.

캐싱 프로세스의 일부로 컨스트레인트가 핸들에서 비워질 때 Recycle() 이벤트가 호출됩니다. 이 함수 내에서 모든 상태가 비워지며 디폴트로 리셋됩니다. PathConstraint 및 GoalEvaluator 를 덮어쓸 때, 꼭 이 함수를 덮어쓰고 파생된 클래스의 일부일 수도 있는 새로운 상태는 무엇이건 비워주는 것이 중요한데, 그렇지 않으면 재사용되는 컨스트레인트에서 묵은(stale) 데이터가 나오게 됩니다.

사용 예제


컨스트레인트/이밸류에이터 상에 스태틱 함수로 구현해야 컨스트레인트나 이밸류에이터를 핸들 목록에 추가할 때 쉽게 호출할 수 있습니다. Path_TowardGoal 컨스트레인트에 대한 스태틱 함수의 예를 하나 들자면 이렇습니다:

Path_TowardGoal.uc
static function bool TowardGoal( NavigationHandle NavHandle, Actor Goal )
{
   local NavMeshPath_Toward Con;

   if( NavHandle != None && Goal != None )
   {
      Con = NavMeshPath_Toward(NavHandle.CreatePathConstraint(default.class));
      if( Con != None )
      {
         Con.GoalActor = Goal;
         NavHandle.AddPathConstraint( Con );
         return TRUE;
      }
   }

   return FALSE;
}

보시는 바와 같이 CreatePathConstraint() 를 호출하여 그 클래스의 캐시된 버전을 구합니다. 그런 다음 AddPathConstraint() 를 호출하여 그것을 검색 도중에 사용할 컨스트레인트 목록에 넣습니다. AddPathConstraint(), AddGoalEvaluator(), ClearConstraints() 등은 탐색에 대한 컨스트레인트를 지정하는 인터페이스입니다.

ALERT! 주: FindPath() 호출 이후 컨스트레인트는 자동으로 비워지기에, ClearConstraints() 는 필수 사항이 아닙니다.

FindPath() 관련 상세 정보는 Navigation Mesh Technical Guide KR 페이지를 참고해 주시기 바랍니다. Navigation Mesh Path Debugging KR 페이지에서는 패쓰 검색 디버깅에 관한 정보를 찾아보실 수 있습니다.

현재 패쓰 컨스트레인트와 그 용도에 대한 간단 요약


NavMeshPath_AlongLine

지정된 방향에서 멀리 떨어진 노드에 휴리스틱 비용을 추가합니다.

NavMeshPath_EnforceTwoWayEdges

상응하는 역에지가 없는 에지를 걸러냅니다. (AI가 빠져나올 수 없는 상황을 방지합니다.)

NavMeshPath_MinDistBetweenSpecsOfType

특정 유형의 에지 사이에 최소 거리를 확보하기 위해 각 스텝의 선행 체인을 따라 돌아 나옵니다. (예를 들어 맨틀 사이의 최소 거리를 유지하기 위해)

NavMeshPath_SameCoverLink

지정된 CoverLink 에서의 커버를 포함하는 폴리곤만 허용합니다.

NavMeshPath_Toward

전달된 골 지점까지의 거리에 따라 휴리스틱 비용을 추가합니다. 보통의 패쓰 검색을 특정 골로 편향시킬 때 사용합니다.

NavMeshPath_WithinDistanceEnvelope

사용자가 적합한 패쓰를 구역을 지정할 수 있게 해 주어, 이 구역 이외의 노드를 버리거나 페널티를 부과하게 합니다. (예로 일정 거리 안의 지점을 찾고 싶을 때)

NavMeshPath_WithinTraversalDist

지정된 값을 초과하는 패쓰는 버립니다.

현재의 패쓰 골 이밸류에이터 및 그 목적에 대한 간단 요약


NavMeshGoal_At

가장 단순하고 흔한 골 측정입니다. 골 지점을 포함하는 폴리곤을 찾으면 검색을 마칩니다.

NavMeshGoal_ClosestActorInList

흥미로운 골 이밸류에이터 중 하나입니다. 이녀석은 (패쓰 거리 내의) 요청 개체에 가장 가까운 액터를 찾습니다. 이 작업은 역순 검색을 통해 이루어집니다. 워킹 세트는 목록의 검색 대상 액터가 포함된 폴리곤 전부로 시딩되며, 그리고서 검색시작을 포함하는 폴리곤을 찾을 때까지 계속해서 검색합니다. 이 시점에서 패쓰가 정방향으로 저장되는데, 이미 역순으로 가고 있기 때문입니다.

NavMeshGoal_Filter

NavMeshGoal_GenericFilterContainer 에 필터로 사용하도록 고안된 헬퍼 베이스 클래스입니다. 이 골들은 "최종 골에 적합한가?" 하는 질문에 답변하기만 할 뿐, 그 외 아무것도 하지 않습니다.

NavMeshGoal_GenericFilterContainer

이 골 이밸류에이터는 경로를 벗어날 때까지 멈추지 않으며, 단순히 최소 비용의 노드를 반환합니다.

NavMeshGoal_Null

이 골 이밸류에이터는 반복처리 최대 횟수에 달할 때까지, 또는 더이상 검색할 노드가 없을 때까지 계속해서 단순 검색을 합니다. 한 구역에서 최적의 노드를 찾을 때 좋습니다. (예를 들어 일정한 거리 내에서 가장 멀리 떨어져 있는 폴리곤을 찾을 때 NavMeshPath_WithinDistanceEnvelope 컨스트레인트와 함께 사용할 수 있습니다.)

NavMeshGoal_PolyEncompassesAI

이 골 이밸류에이터는 개체 검색에 꼭 맞출 수 없는 폴리곤을 던져버립니다. (반경 밖의 폴리곤을 찾는 등) 확장 가능 경로 검색에 좋은데, 그 이유는 에지가 폴리곤에 들어가는 탐색을 허용하는 개체를 지원할 수는 있으나, 그 개체가 폴리곤을 통과해 이동할 수 있다 하더라도 그 안에 꼭 맞지는 않아도 되기 때문입니다.

NavMeshGoal_Random

이 골 이밸류에이터는 경로를 벗어날 때까지 멈추지 않으며, 탐색한 노드 중 하나를 임의로 반환합니다.

NavMeshGoal_WithinDistanceEnvelope

이 골 이밸류에이터는 지정된 거리 범위 밖에 있는 노드(폴리곤)를 던져버립니다.