This document is about: QUANTUM 2
SWITCH TO

Navmesh 에이전트 생성하기

Quantum 2.0 Navmesh 에이전트는 여러 컴포넌트로 분할됩니다. Navmesh와 스티어링으로 작업하는 개발자들이 최종 이동 결과를 제어하고 싶어 한다는 것을 알게 되었습니다. 이는 게임 경험에 매우 중요한 역할을 하는 경우가 많기 때문입니다. 새로운 navmesh 에이전트 파트는 개발자가 다중 스레드 성능을 저하시키지 않고 불필요한 부품을 실행하거나 불필요한 메모리를 낭비하지 않고 navmesh 지원 조합을 선택할 수 있도록 지원합니다.

에이전트 컴포넌트는 NavMeshPathfinder, NavMeshSteeringAgentNavMeshAvoidanceAgent입니다. 독립형 컴포넌트는 NavMeshAvoidanceObstacle입니다.

에이전트 엔티티는 유니티의 엔티티 프로토타입을 사용하거나 코드로 엔티티를 조립하는 두 가지 방법으로 작성할 수 있습니다. 그들은 여전히 NavMeshAgentConfig 퀀텀 에셋을 사용합니다.

유니티에서 엔티티 프로토타입으로 에이전트 생성하기

  • 유니티 메뉴 GameObject/Quantum/Empty Entity를 통해 빈 Quantum 프로토타입을 만듭니다.
  • 엔티티를 선택하고 Transform2D로 설정합니다.
  • NavMeshPathfinder 컴포넌트를 전환합니다.
    • 기본 NavMeshAgentConfig를 선택하십시오.
    • 초기 대상을 전환하고 유니티 씬에서 트랜스폼을 선택하여 이동할 초기 위치를 제공합니다.
    • 베이크 된 Quantum Navmesh를 선택합니다(Navmesh 워크플로우 참조).
  • NavMeshSteeringAgent를 설정합니다.
  • 경로 기즈모를 보려면 다음과 같이 하십시오.
    • 기본 NavMeshAgentConfig에서 Show Debug Steering를 활성화합니다.
    • QuantumEditorSettings에서 Navmesh Gizmo Draw Pathfinder Funnel을 활성화합니다.
  • 플레이를 누릅니다.
navmesh agent prototype

코드로 컴포넌트가 있는 에이전트 생성

또는 코드에서 에이전트 엔티티를 어셈블할 수 있습니다.

처음에 엔티티에는 Transform2D 또는 Transform3D 컴포넌트가 필요하고 View* 컴포넌트를 추가하면 씬에서 프리랩이 렌더링 됩니다.

가장 중요한 컴포넌트는 **NavMeshPathfinder**입니다. 경로 찾기를 수행하고 대상 위치와 사용자 정의 웨이포인트 수를 저장하며 웨이포인트 진행을 탐지합니다. 이 구성 요소는 NavMeshAgentConfig 내에서 NavMeshPathfinder.Create() 팩토리 메소드를 통해 생성해야 합니다.

NavMeshSteeringAgent 컴포넌트는 옵션이며 NavMeshPathfinder가 필요합니다. 실행 시간 동안 변경할 수 있는 최대 속도, 가속 및 회전 속도 변수가 있으며 경로를 따라 엔티티를 조정합니다. 이 컴포넌트를 사용하지 않는 것 외에도 개발자는 MovementTypeCallback으로 변경할 수 있으며 최신 회피 데이터를 가진 상태에서 자신의 이동을 주입합니다. 회전 속도 및 가속을 0으로 설정하여 사용하지 않도록 설정합니다.

**NavMeshAvoidanceAgent**를 사용하려면 NavMeshPathfinderNavMeshSteeringAgent 컴포넌트가 모두 필요하며 이 컴포넌트보다 먼저 엔티티에 Set()이 있어야 합니다. 이 에이전트는 마스크 및 레이어와 함께 우선순위 및 필터링을 사용하여 다른 이동 에이전트(HRVO)를 방지하기 위해 회피 계산을 수행합니다. 처음에 NavMeshAgentConfig 우선순위로 설정된 마스크 및 레이어는 구성 요소의 런타임 동안 변경될 수 있습니다.

에이전트가 정적 충돌에 침투하지 못하도록 물리적 물체에 의해 에이전트를 조종하려면 PhysicsCollider2D/3D 및 **PhysicsBody2D**가 필요합니다. 이를 활성화하려면 NavMeshAgentConfig에서 MovementTypeDynamicBody로 설정해야 합니다.

C#

public override void OnInit(Frame f) {
    base.OnInit(f);

    var entity = f.Create();
    f.Set(entity, new Transform3D() { Position = FPVector3.Zero, Rotation = FPQuaternion.Identity });
    var config = f.FindAsset<NavMeshAgentConfig>(NavMeshAgentConfig.DEFAULT_ID);
    var pathfinder = NavMeshPathfinder.Create(f, entity, config);

    // find a random point to move to
    var navmesh = f.Map.NavMeshes["Navmesh"];
    if (navmesh.FindRandomPointOnNavmesh(FPVector2.Zero, FP._10, f.RNG, *f.NavMeshRegionMask, out FPVector2 randomPoint)) {
    pathfinder.SetTarget(f, randomPoint, navmesh);
    }

    f.Set(entity, pathfinder);
    f.Set(entity, new NavMeshSteeringAgent());
}

NavMesh 에이전트 기즈모 Draw Nav Mesh Agents를 활성화하여 장면 윈도우에서 에이전트 기즈모 그리기를 활성화합니다.

중요한 에이전트 설정

Pathfinder

**NavMeshPathfinder.SetConfig()**는 컴포넌트 생성 및 런타임 중에 실행할 수 있습니다. 에이전트가 현재 경로를 따르고 있고 새 구성과 경유지 수가 다를 경우 경로가 재설정됩니다. 엔티티의 NavMeshSteeringAgentNavMeshAvoidanceAgent 컴포넌트에 대한 구성이 자동으로 업데이트되고 Speed, AccelerationPriority, Layer 및 Mask 값이 구성 값으로 재설정됩니다.

**NavMeshAgentConfig.MaxRepathTimeout**은 시간에 경유지에 도달하지 않은 경우 에이전트 경로 찾기를 트리거하는 시간(초)입니다. 이것은 고착된 에이전트를 완화하기 위한 페일 세이프에 가깝습니다. 사용하지 않으려면 값을 0으로 설정합니다.

NavMeshAgentConfig.LineOfSightFunneling 메인 navmesh 가운데에 위치한 navmesh 영역을 사용할 때 활성화되어야 합니다. 예를 들어 파괴될 수 있는 건물입니다. 영역에 의해 도입된 추가 삼각형은 활성 영역 근처의 약간 홀수 경로로 나타날 수 있습니다. 이 옵션을 사용하면 영역 근처의 불필요한 경유지가 제거됩니다.

NavMeshAgentConfig.DynamicLineOfSight 에이전트는 각 틱에서 경유지를 건너뛸 수 있는지 여부를 확인할 수 있습니다. 이 옵션은 비용이 많이 들지만 경로에 불필요한 경유지가 제거됩니다.

NavMeshAgentConfig.DynamicLineOfSightWaypointRange 반면 이 값이 설정된 경우에는 각 틱이 웨이포인트(범위)에 가까울 때만 시선 검사가 실행됩니다. DynamicLineOfSight를 활성화하지 않아도 작동합니다.

**NavMeshAgentConfig.FindValidTargetCellRange**는 SetTarget() 중에 Navmesh 밖의 위치를 선택할 때 도움이 됩니다. 범위 매개변수는 인접 셀을 검색하여 내비게이션 내부에 있는 대상을 최적의 상태로 대체합니다.

다음 이미지에서 노란색 x로 표시된 대상을 고려하십시오. 특정 셀 범위를 검색한 셀은 숫자로 표시됩니다. 예를 들어 셀 범위 0은 대상을 찾지 못합니다. 반면에 범위 1은 노란색 점선으로 묘사된 나브메쉬의 닫힘 위치를 찾을 수 있습니다. 셀 범위를 무리하게 높은 숫자로 늘리는 것은 성능에 큰 영향을 미친다는 것을 알아두세요.

navmesh agent waypoint reached detection axis

**NavMeshAgentConfig.CachedWaypointCount**를 사용하여 NavMeshPathfinder에 캐시되는 경유지 수를 설정합니다. 과도하지 않은 데이터를 더 많이 저장하면 시뮬레이션 속도가 느려집니다. 캐시에 저장된 첫 번째 웨이포인트는 SetTarget()이 호출되었을 때의 에이전트의 현재 위치이며, 도달 웨이포인트 탐지를 향상시키는 데 사용됩니다. 에이전트가 마지막 경유지 쪽으로 방향을 전환하기 시작하면 자동으로 경로 찾기를 다시 실행하여 프레임을 계산할 때 에이전트에 유효한 경유지가 없는 상황을 완화합니다.

경유지에 도달한 탐지 도움말 NavMeshAgentConfig.EnableWaypointDetection 에이전트가 웨이포인트에 도달하는 데 문제가 있는 경우(예: 회전 속도가 느리거나 회피됨)입니다. 후속 파라미터 Axis ExtendAxis Offset 은 웨이포인트 도달 검출 축(검은색 선)을 정의합니다. 에이전트가 노란색 영역에 들어가면 경유지에 도달하는 것으로 간주됩니다.

navmesh agent waypoint reached detection axis
Waypoint Reached Detection Axis

경로 파인더 컴포넌트에 함께 제공된 스티어링 구성 요소가 없는 경우 **DefaultWaypointDetectionDistance**는 웨이포인트 도달 감지를 수행하는 데 사용되며 에이전트 최대 속도 델타 시간으로 설정해야 합니다. 경유지 도달 탐지 기능을 향상시키는 방법은 "유용한 Navmesh 에이전트 구성" 섹션을 참조하십시오.

스티어링 에이전트

NavMeshAgentConfig.StoppingDistance **AutoBraking**는 최종 대상에 접근하는 에이전트에 적용됩니다. StoppingDistance는 에이전트가 대상 앞에서 멈추는 절대 거리로, 이 값을 설정하면 에이전트가 오버슈트를 하지 않고 안정화될 수 있습니다. 남은 거리가 틱당 에이전트의 현재 이동 거리보다 작을 때는 에이전트가 항상 중지됩니다.

AutoBraking는 목적지에 도달하기 전에 에이전트의 속도를 늦추는 시각적 기능에 가깝고 에이전트 중지 동작을 안정시키는 데에도 사용할 수 있습니다. AutoBrakingDistance는 에이전트 속도가 느려지기 시작하는 대상 주변의 반지름을 정의합니다. 내부적으로 제동을 부드럽게 하기 위해 제곱근을 사용합니다.

이동을 사용하여 형상에 의해 navmesh 에이전트가 접근하지 못하면 MovementType을 PhysicsBody로입력합니다. 특히 회피 기능을 사용할 경우 에이전트가 탐색기 밖으로 이동합니다. 이 문제를 완벽하게 방지하고 에이전트가 Navmesh 테두리를 따라 미끄러지도록 하려면 **NavMeshAgentConfig.ClampAgentToNavmesh**를 사용가능하도록 해야 합니다.

에이전트는 잠재적으로 내비게이션의 'MinAgentRadius'보다 더 큰 반경을 가질 수 있습니다. Quantum은 에이전트 웨이포인트를 경계에서 더 멀리 이동시켜 이를 지원하지만, 이렇게 하면 에이전트를 Navmesh에 고정하는 것이 훨씬 더 복잡해지고 매개 변수 **'ClampAgentToNavmeshRadius가 파라미터로 전환됩니다.Threshold'**는 선택할 매개 변수를 선택하는 데 도움이 됩니다. 소형 에이전트가 내브메시 밖으로 이동하는 경향이 있을 때 반지름을 늘립니다.

보정을 안정화하기 위해 에이전트는 전체 침투 깊이의 백분율(ClampAgentToNavmeshCorrection)만 이동합니다.

업데이트 간격

성능 최적화를 위해 모든 시뮬레이션 틱이 아닌 경로 검색 및 회피를 실행하도록 각 개별 에이전트를 구성할 수 있습니다. NavMeshAgentConfig.UdpateInterval를 1보다 큰 값으로 설정하여 업데이트 받는 양을 줄입니다. 이렇게 하면 에이전트의 응답이 줄어들 뿐만 아니라 CPU 시간도 절약됩니다. 에이전트 엔터티 인덱스는 업데이트할 정확한 틱을 정의하는 데 사용되므로 모든 엔터티가 동일한 틱에 업데이트되지는 않습니다.

식은 다음과 같습니다:

C#

updateAgent = entity.Index % agentConfig.UpdateInterval == f.Number % agentConfig.UpdateInterval
  • 1 = 매 틱 마다 업데이트
  • 2 = 2번 틱 마다 업데이트
  • 8 = 8 번째 틱 마다 업데이트 등

에이전트의 모든 콜백은 기본 스레드에서 호출되며, 다른 컴포넌트 및 엔티티에 접근할 때 다중 스레드 문제가 발생하지 않습니다.

내비게이션 에이전트 콜백을 선택해야 합니다. 시뮬레이션 구성을 열고 Enable Navigation Callbacks를 전환합니다.

simulation config
Enable Navigation Agent Callbacks in the Simulation Config

다음 신호는 에이전트를 추가로 제어하는 데 사용할 수 있는 즉각적인 피드백을 제공합니다.

C#

namespace Quantum {
  public unsafe partial class NavMeshAgentTestSystem : SystemMainThread,
                                              ISignalOnNavMeshSearchFailed,
                                              ISignalOnNavMeshWaypointReached,
                                              ISignalOnNavMeshMoveAgent { 
    }
}

**ISignalOnNavMeshSearchFailed**는 에이전트가 현재 위치와 SetTarget()에 설정된 대상 사이에 경로를 생성할 수 없을 때 호출됩니다. 예를 들어 목적지를 Navmesh와 일치시킬 수 없습니다. 이 콜백 중에 SetTarget()을 실행할 때 resetAgent 파라미터를 false로 설정합니다.

**ISignalOnNavMeshWaypointReached**는 에이전트가 대상에 대한 경로에서 경유지에 도달하면 호출됩니다. 경유지에 대한 자세한 내용은 Target, LinkStart, LinkEnd를 참조하십시오.

**ISignalOnNavMeshMoveAgent**는 NavMeshAgentConfig.MovementTypeCallback일 때만 호출되며 에이전트에 NavMeshSteeringAgent 컴포넌트가 있습니다. desiredDirection 파라미터는 내부 스티어링 및 회피에서 에이전트 이동 벡터가 되어야 한다고 생각하는 정규화된 방향입니다.

C#

public void OnNavMeshMoveAgent(Frame f, EntityRef entity, FPVector2 desiredDirection) {
    var agent = f.Unsafe.GetPointer<NavMeshSteeringAgent>(entity);

    // simple demonstration how to move the agent.
    if (f.Has<Transform2D>(entity)) {
        var transform = f.Unsafe.GetPointer<Transform2D>(entity);
        transform->Position.X.RawValue = transform->Position.X.RawValue + ((desiredDirection.X.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
        transform->Position.Y.RawValue = transform->Position.Y.RawValue + ((desiredDirection.Y.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
        transform->Rotation = FPVector2.RadiansSignedSkipNormalize(FPVector2.Up, desiredDirection);
    } else if (f.Has<Transform3D>(entity)) {
        var transform = f.Unsafe.GetPointer<Transform3D>(entity);
        transform->Position.X.RawValue = transform->Position.X.RawValue + ((desiredDirection.X.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
        transform->Position.Z.RawValue = transform->Position.Z.RawValue + ((desiredDirection.Y.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
        var desiredRotation = FPVector2.RadiansSignedSkipNormalize(FPVector2.Up, desiredDirection);
        transform->Rotation = FPQuaternion.AngleAxis(desiredRotation * FP.Rad2Deg, -FPVector3.Up);
    }
}

Path-find 만

MapNavMeshPathfinder 컴포넌트를 사용하여 다중 스레드 경로 찾기를 수행하고 대상과 웨이포인트를 저장하고 웨이포인트 인덱스 진행을 수행합니다. 자신의 시스템에서 스티어링을 제어하고 회피 및 움직임을 직접 제어하십시오.

웨이포인트 진행이 작동하려면 패스파인더 구성 요소가 웨이포인트에 얼마나 빨리 도달하고 있는지에 대한 정보가 필요합니다. WaypointDetectionDistanceSqr 속성을 각 프레임에 설정합니다.

회피 없음

Pathfinder 및 SteeringAgent 컴포넌트만 사용하십시오. 회피 코드가 실행되지 않으며 컴포넌트는 회피 관련 데이터를 저장하지 않습니다. CPU 시간을 절약을 위해 SimulationConfig.Navigation.EnableAvoidance를 해제합니다.

사용자 지정 이동 Quantum 회피 기능 있음

세 가지 구성 요소(Pathfinder, SteeringAgent 및 AvoidanceAgent)를 모두 사용합니다. AvoidanceAgents는 SteeringAgent의 일부에 따라 달라지지만 재정의할 수도 있습니다. NavMeshAgentConfig에서 MovementTypeCallback으로 설정하고 ISignalOnNavMeshMoveAgent 시그널을 구현합니다(이전 섹션 참조). desiredDirection 파라미터는 변경된 이동 방향을 회피합니다.

Back to top