Game Engine/Unity

[Unity] Navigation - NavMeshAgent

  • -

유니티에서 최단 거리를 탐색하여 추적할 수 있는 캐릭터를 생성하는데에 사용되는 Navigation에 대해 알아보자.

 

그 중, 이번 포스팅은 NavMeshAgent를 중점적으로 알아보고, NavMeshObstacle에 대해서도 간단하게 알아보고자 한다.

 

 

개념

유니티 Navigation 시스템을 사용하면 씬 내에서 지능적으로 탐색하여 이동할 수 있는 캐릭터를 만들 수 있다.

 

이때, 자동으로 씬에 생성되는 NavMesh 게임 월드에서 걸을 수 있는 표면을 뜻하며,

 

이를 이용하여 목적지까지 경로를 찾고 이동하는 캐릭터에 부착되는 컴포넌트가 NavMeshAgent이다.

 

 

내부 작업

 

 Navigation을 사용하여 캐릭터를 지능적으로 움직이려면 크게 두가지의 기능으로 나뉘는데,

 

첫째로 맵을 탐색하여 목적지까지의 경로를 추론하는 것이고, 둘째로 해당 목적지까지 이동하는 것이다.

 

각각 Global Navigation, Local Navigation이라고 부른다.

 

 

씬에서 두 위치 사이의 경로를 찾기 위해서는 먼저 시작 위치와 목적지 위치를 가장 가까운 폴리곤에 매핑한다.

 

그 다음, 시작 위치에서 탐색을 시작하여 모든 이웃 폴리곤을 거쳐 목적지 폴리곤에 도달하게 된다.

 

이때, 유니티의 Navigation은 경로를 찾는 알고리즘으로 A* 를 사용하고 있다.

 

 

시작 폴리곤에서 목적지 폴리곤까지의 경로를 정의하는 폴리곤의 시퀀스를 통로(Corridor)라고 한다.

 

에이전트는 통로를 따라 보이는 가장 가까운 코너를 향해 움직이는 방법으로 목적지에 도달한다.

 

(출처 : https://docs.unity3d.com/kr/2020.3/Manual/nav-InnerWorkings.html)

 

 

사용법

Navigation 시스템의 사용법부터 간단하게 알아보자.

 

 

먼저, Window - AI - Navigation을 눌러주면  Navigation 창이 하나 생긴다.

 

 

그 후, Navigation 창의 Object 탭에서 Scene Filter를 All로 해주고

 

Hierarchy 창에서 탐색에 사용할 맵 오브젝트를 모두 선택해준 후, Navigation Static을 체크해준다.

 

 

캐릭터가 이동 가능하게 할 오브젝트는 Navigation Area를 Walkable로 두고,

 

이동할 수 없게 할 오브젝트는 따로 선택 후 Not Walkable로 바꿔준다.

 

 

Bake 탭으로 넘어가서 Bake 버튼을 눌러주면, Scene 창에 하늘색으로 된 NavMesh가 자동으로 생성된다.

 

NavMesh OffMeshLink에 대한 자세한 글은 Navigation에 대한 고찰 1편 에 마련되어 있다.

 

 

이동시킬 캐릭터에 NavMeshAgent 컴포넌트를 추가하고 이를 조종할 스크립트도 추가한다.

 

MyAgent 스크립트는 다음과 같다.

 

using UnityEngine;
using UnityEngine.AI;

public class MyAgent : MonoBehaviour
{
    NavMeshAgent nav;
    [SerializeField] Transform target;

    void Start()
    {
        nav = GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
            nav.SetDestination(target.position);
    }
}

 

마우스 왼쪽 클릭시, Agent가 target 위치를 추적하게 하는 코드다.

 

Start나 Awake에서 NavMeshAgent 컴포넌트를 가져오고

 

SetDestination()으로 타겟 위치를 설정해주면 된다.

 

NavMeshAgent를 사용하려면 UnityEngine.AI 라이브러리를 불러와야 한다.

 

실행 결과는 다음과 같다.

 

타겟을 향해 길을 잘 찾아 가지만, 움직임이 둔한 모습을 볼 수 있다.

 

이를 더욱 세부적으로 설정할 수 있게 NavMeshAgent의 속성값들을 알아보자.

 

 

속성

NavMeshAgent 컴포넌트에는 여러가지 속성이 존재한다.

 

하나씩 알아보도록 하자.

 

1. Agent Type

각각의 타입에 따라 갈 수 있는 경로를 다르게 하기 위한 설정이다.

 

사용시 "SetDestination" can only be called on an active agent that has been placed on a NavMesh 라는 에러가 발생한다.

 

해당 기능을 정상적으로 이용하기 위해서는 NavMeshSurface 컴포넌트를 이용해야하고,

 

해당 컴포넌트는 유니티 GitHub에서 따로 다운로드하여 설치해야한다. 

 

더욱 자세한 사항은 유니티 공식 문서 참고

 

 

2. Base Offset

왼쪽부터 각각 Base Offset이 -1, 0, 1일 때

 

NavMeshAgent의 충돌 실린더와 해당 컴포넌트가 부착된 오브젝트의 수직 중심점을 맞추기 위한 속성이다.

 

 

3. Speed

최대 이동 속도 (초당 월드 단위)

 

좌측 Speed 3.5, 우측 Speed 10

 

Speed를 올렸지만, 빠른 속도를 주체하지 못하고 벽에 부딪치며 이동하는 모습이다.

 

 

4. Angular Speed:

최대 회전 속도 (초당 각도)

 

좌측 Angular Speed 30, 우측 Angular Speed  360

 

Angular Speed는 Agent의 실제 이동 경로와는 상관 없이, 보여지는 부분의 각도임을 알 수 있다.

 

따라서, Speed를 올려 주체하지 못하는 상황에서는 Angular Speed가 이를 해결해주지 못한다.

 

 

5. Acceleration

최대 가속 (제곱 초당 월드 단위)

 

좌측 Acceleration 8, 우측 Acceleration 50

 

두 실험 모두 Speed가 10, Angular Speed가 360일 때의 모습이다.

 

좌측은 위에서 언급했던 것처럼 Angular Speed가 빠른 Speed를 잡아주지 못하였고,

 

Acceleration을 올려주고 나서야 속도를 잘 컨트롤하며 빠릿빠릿하게 나아가는 모습을 볼 수 있다.

 

 

6. Stopping Distance

목적지에 도착하기 전, 어느 정도의 거리에서 멈출지 설정한다.

 

 

7. Auto Breaking

활성화 시, 에이전트는 목적지에 다다를 때 속도를 줄여서 목적지에 정확하게 도착하게 한다.

 

 

8. Radius

Agent의 장애물 회피 반경 반지름이다.

 

좌측 Radius 0.5, 우측 Radius 1.0

 

일반 장애물과 NavMeshObstacle에 직접적인 연관은 없고, 다른 Agent들과의 충돌 반경을 나타낸다.

 

 

9. Height

장애물을 통과하기 위한 Agent의 높이다.

 

Radius와 같이 일반 장애물과 NavMeshObstacle에 직접적인 연관은 없다.

 

 

10. Quality

장애물 회피 품질, 높을수록 경로가 더 세밀해진다.

 

좌측 Low Quality, 우측 High Quality

 

높은 Quality일 수록 더욱 세밀하게 피하는 모습을 볼 수 있다.

 

None으로 할 경우는 충돌만 해결하고, 다른 에이전트에 대한 회피는 하지 않는다.

 

Quality가 높을수록 CPU 사용량이 많아진다.

 

 

11. Priority

우선순위, 다른 Agent가 경로에 있다면 우선순위가 낮은 Agent는 회피 대상에서 제외된다.

 

Priority 값이 낮을 수록 우선순위는 올라간다.

 

 

다른 Agent의 Priority를 더 높였을 때의 결과이다.

 

Quality를 None으로 했을 때 처럼 다른 Agent들을 무시해버린다.

 

 

12. Auto Traverse Off Link

OffMeshLink를 자동으로 이용할지에 대한 여부다.

 

애니메이션을 사용하거나, OffMeshLink를 이용할 특정 방법을 사용하고 싶다면 꺼야한다.

 

 

13. Auto Repath

기존 경로가 잘못된 경우 Agent가 새 경로 획득을 시도할지에 대한 여부다.

 

활성화시, Agent가 경로 일부분의 끝에 도달하면 재탐색하고,

 

목적지까지 경로가 없다면 목적지에서 제일 가깝게 도달할 수 있는 위치까지 부분적인 경로가 생성한다.

 

 

14. Area Mask

이 Agent가 지나다닐 수 있는 Area를 따로 설정할 수 있다.

 

 

변수 및 함수 활용

hasPath 현재 Agent가 목적지를 가지고 있는지 반환 (bool)
isOnNavMesh 현재 AgentNavMesh 위에 있는지 반환 (bool)
isOnOffMeshLink 현재 AgentOffMeshLink 위에 있는지 반환 (bool)
remainingDistance Agent와 목적지가 직선 상에 있을 때 거리를 반환 (float)
중간에 장애물이 있을 시 Infinity 반환
SetDestination(Vector3 position) Agent에게 목적지를 설정하거나 업데이트하여 경로에 대한 계산을 하게 한다.
먼저 몇 프레임 동안 경로를 계산하고, 유효한 경로가 나오면 이동을 시작한다.
Warp(Vector3 position) Agent를 제공된 위치로 순간이동 시킨다.
Stop() Agent의 이동을 멈춘다.
Resume() Agent의 이동을 재개한다.
Move(Vector3 offset) Agentoffset 만큼 이동시킨다. Agent에게 경로가 있는 경우 경로가 조정된다.
GetAreaCost(int areaIndex) AgentareaIndex번째 AreaCost를 불러온다.
SetAreaCost(int areaIndex, float areaCost) AgentareaIndex번째 AreaCost를 설정해준다.

 

이외에도 위에서 설명한 속성값도 스크립트 내에서 변수를 활용하여 값을 불러오거나 설정할 수 있다.

 

더욱 자세한 변수 및 함수 정보는 유니티 공식 문서 참고

 

 

NavMeshObstacle

장애물에 NavMeshObstacle 컴포넌트를 추가하여 동적 장애물을 생성할 수 있다.

 

 

먼저, NavMeshObstacle 컴포넌트를 부착하지 않은 장애물을 만들어보자.

 

장애물을 경로 중간에 놓고 Y값을 올린 후에 RigidBody 컴포넌트를 추가하여

 

게임이 시작하면 경로에 정확히 떨어지게끔 설정했다.

 

실행 결과는 다음과 같다.

 

 

두 오브젝트 모두 Collider가 있음에도 장애물을 무시하고 Bake된 NavMesh 경로대로 이동하는 모습이다.

 

 

이번엔 장애물에  NavMeshObstacle 컴포넌트를 추가한다.

 

결과는 다음과 같다.

 

 

Agent가 장애물을 무시하지 못하고 길을 막혀버린다.

 

하지만 다른 경로를 찾지 못하고 그대로 멈춰있는 모습이다.

 

 

다음은 NavMeshObstacle의 속성 중 Carve라는 속성에 체크를 해보고 실행해본다.

 

 

NavMesh에 접촉하고 잠시 뒤, NavMesh가 동적으로 수정되면서 Agent가 다른 경로를 찾아가는 모습이다.

 

이처럼 Carve는 장애물이 NavMesh에 구멍을 내는 것이다.

 

NavMeshObstacle 컴포넌트의 Carve 속성에 대해 더 알아보자.

 

1. Move Threshold

설정된 거리 만큼 장애물이 움직인다면 다시 Carve를 진행한다.

 

좌측 Move Threshold 0.1, 우측 Move Threshold 0.5

 

값이 커질수록 조금 움직여도 Carve 계산을 다시 하지 않는다.

 

 

2. Time To Stationary

장애물이 멈췄을 때, 어느 정도의 시간 뒤에 Carve를 다시 진행할지 설정한다.

 

좌측 Time To Stationary 0.5, 우측 Time To Stationary 0.1

 

값이 작아질수록 바로바로 Carving이 진행된다.

 

 

3. Carve Only Stationary

장애물이 멈추지 않고도 바로바로 Carve가 진행되게 할 것인지에 대한 여부

 

좌측 Carve Only Stationary off, 우측 Carve Only Stationary on

 

Carve 작업을 많이 진행할 수록 성능에 좋지 않은 영향을 끼칠 수 있으므로

 

적절한 설정을 해서 Carve 주기를 조절해야한다.

 

 

참고 문헌

https://docs.unity3d.com/kr/2020.3/Manual/nav-InnerWorkings.html
https://docs.unity3d.com/kr/2020.3/Manual/class-NavMeshAgent.html
https://docs.unity3d.com/kr/530/ScriptReference/NavMeshAgent.html
https://docs.unity3d.com/kr/current/Manual/class-NavMeshObstacle.html
https://www.youtube.com/watch?v=6KgLxSxnwP0&t=1s

 

 

개인 공부용 포스팅인 점을 참고하시고, 잘못된 부분이 있다면 댓글로 남겨주시면 감사드리겠습니다.
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.