[Unity] 유니티 NavMesh
NavMesh 개념
Unity의 NavMesh(내비게이션 메시)는 게임 오브젝트의 이동 경로를 최적화하기 위해 사용되는 내비게이션 시스템입니다.
NavMesh는 게임 환경의 표면을 분석하여, 이동 가능한 영역과 장애물을 구별하고, 에이전트(agent)가 원활하게 이동할 수 있는 경로를 찾아줍니다.
NavMesh를 간단히 설명하자면, 길 찾기 앱과 같습니다.
길 찾기 앱은 도로와 장애물을 고려하여 목적지까지 최적의 경로를 찾아주는데, 이와 마찬가지로 NavMesh는 게임 환경에서 이동 가능한 영역과 장애물을 고려하여 에이전트가 목표 지점까지 최적의 경로를 찾아 이동할 수 있게 해줍니다.
NavMesh는 크게 4가지 구성 요소로 이루어져 있습니다.
각각 NavMesh, Agent, OffMeshLink ,Obstacle 이며, 각각에 대한 설명은 다음과 같습니다.
- NavMesh : 게임 환경의 지형을 분석하여 이동 가능한 영역
- NavMesh Agent : 이동할 오브젝트(캐릭터 등)를 위한 컴포넌트로, NavMesh를 통해 경로를 탐색하고 이동
- OffMeshLink : NavMesh에서 보행이 불가능한 위치를 연결하기 위한 장치
- NavMesh Obstacle : NavMesh에서 고려해야 할 장애물을 나타내는 컴포넌트
사용 방법
NavMeshSample 프로젝트-> NavMeshSample
생성자체는 코드가 필요 없지만 NavMesh를 활용하려면 관련 부분을 코드로 작성해야 됩니다.
먼저 NavMesh를 생성하는 방법 부터 입니다.
방법은 아래와 같이 4~5단계를 거쳐 NavMesh를 생성할 수 있으며, 이를 완료 하면 게임 환경에서 에이전트가 NavMesh를 사용해 목표 지점까지 최적의 경로로 이동할 수 있습니다.
이렇게 NavMesh를 사용하면 게임 오브젝트의 이동을 효율적으로 관리할 수 있으며, 더 복잡한 경로 계획 및 자동 회피 기능을 구현할 수 있기 때문에 익혀두시면 좋습니다.
- 유니티 에디터에서 Window > AI > Navigation을 선택하여 Navigation창을 엽니다.
- 이동 가능한 오브젝트의 레이어를 설정하려면 Object탭을 선택하고, Navigation Static 및 Walkable옵션을 활성화합니다.(이렇게 설정하면 NavMesh Surface가 해당 오브젝트를 고려하여 생성됩니다.)
- NavMesh의 상세 설정을 해줍니다.(Default로 두고 생략해도 상관없습니다.)
- Bake버튼을 클릭하여 NavMesh를 생성
1. Navigation창 열기
아래와 같이 Window > AI > Navigation을 선택하여 Navigation창을 엽니다.
그러면 아래와 같이 Navigation 탭이 생성됩니다.
2. Object탭 설정
여기서 중요한 설정 입니다.
맵에서 에이전트가 걸을 수 있는 곳은 Navigation Static에 체크하고 Navigation Area에서 Walkable로 설정하시면 됩니다.
벽이나 장애물처럼 걸어다닐 수 없는곳은 반대로 Not Walk able로 설정하시면 됩니다.
3. NavMesh의 상세 설정
Agents
기본적으로 Humanoid가 제공되며, Humanoid가 아닌 커스텀으로 만들어 지정하는것도 가능합니다.
- Name : 객체의 이름
- Radius : 객체의 크기
- Height : 객체의 높이
- Step Height : 만약 계단같은 영역이 있다면, 건너갈 수 있는 계단 폭의 크기
- Max Slope : 이동할 수 있는 비탈면(경사면)의 최대 각도
Bake
Bake를 할 때, 어떤 기준으로 Baking을 할지 설정합니다.
Agent와 비슷합니다.
상세 부분은 우측의 공식 문서를 참조해주세요 Unity 공식 문서
4. NavMesh 생성
Object탭과 Bake탭의 상세 설정을 마치고 난 뒤 아래 Bake탭의 오른쪽 하단에 있는 Bake 버튼을 눌러 NavMesh를 생성하면 됩니다.
그러면 아래와 같이 초록색 바닥 부분의 표면에 파란색으로 덮힌듯한 부분이 생성되는데 이것이 NavMesh입니다.
이제 Agent로 지정할 오브젝트에는 아래와 같이 NavMeshAgent 컴포넌트를 추가하고
- Agent Type : Navigation Mesh를 설정할 때, 생성했던 Agent 목록의 타입입니다.
- Base Offset : GameObject와 충돌 실린더의 높이 차이를 메꾸기 위해 사용됩니다. 즉 값이 높을 수록, 충돌 실린더가 아래로 내려갑니다.
Steering :
- Speed : GameObject의 최대 이동 속도
- Angular Speed : GameObject의 최대 회전 속도
- Acceleration : GameObject의 최대 가속도
- Stopping distance : 목표 위치에 가까워졌을 시 정지하는 거리
- Auto Braking : 활성화 상태라면, 목적지에 다다를 때 속도를 줄입니다. 해당 객체가 만약 순찰하는 것처럼 목표 지점을 순회해야한다면, 비활성화 해야합니다.
Obstacle Avoidance :
- Radius : 충돌 반경. 넓을 수록 좁은 폭을 지나갈 수 없습니다.
- Height : Agent가 장애물 밑으로 지나갈 수 있는 높이 간격입니다.
- Quality : 장애물 회피 품질입니다. 회피를 없음으로 설정하면 충돌만 체크되고 회피를 위한 동작을 하지 않습니다. 품질이 낮을 수록 CPU 자원이 절약됩니다.
- Priority : 회피의 우선순위입니다. 낮은 우선 순위의 Agent는 이 Agent의 회피 대상에서 제외되고 0 ~ 99의 범위를 갖습니다.
Path Finding :
- Auto Traverse OffMesh Link : 활성화 상태라면, Off-Mesh Link를 이용하여 자동으로 점프하거나, 낙하합니다. 애니메이션을 사용하거나, 오프 메시 링크를 특정한 방법으로 사용하고 싶을 때는 비활성화 해야 합니다.
- Auto Repath : 활성화 상태라면, 에이전트가 경로 일부분의 끝에 도달하면 경로를 재탐색 합니다. 목적지까지 경로가 없다면, 목적지에서 제일 가깝게 도달할 수 있는 위치까지 부분적인 경로를 생성합니다.
- Area Mask : Navigation 에서 Areas 탭에 있는 레이어 리스트 중 선택하여 이동 경로를 제외시킬 수 있습니다.
장애물이 될 오브젝트는 아래와 같이 NavMeshObstacle 컴포넌트를 추가하시면 됩니다.
코드
NavMeshAgent를 사용하는 샘플 코드는 다음과 같습니다.
using UnityEngine;
using UnityEngine.AI;
public class Movement : MonoBehaviour
{
public Transform target; // 에이전트의 목표 지점(Transform 컴포넌트)
private NavMeshAgent agent; // 에이전트의 NavMeshAgent 컴포넌트
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
void Update()
{
agent.SetDestination(target.position); // 에이전트에게 목표 지점까지의 경로를 계산하고 이동하도록 명령
}
}
OffMeshLink
기본적으로 NavMesh생성부분까지는 동일하기때문에 OffMeshLink로 연결하고 이동하는 부분만 짚도록 하겠습니다.
- OffMeshLink로 설정할 오브젝트를 선택후 네비게이션 탭의 Object에서 OffMeshLink에 체크후 Jump로 설정.
- Bake탭에서 높이와 거리를 설정후 Bake
- 해당 오브젝트에 OffMeshLink 컴포넌트를 추가하고 출발지와 도착지를 설정
- 스크립트 적용하고 OffMeshLink로 이동할 최종 목적지를 설정
1. OffMeshLink로 설정
OffMeshLink로 설정할 오브젝트를 선택후 아래와 같이 네비게이션 탭의 Object에서 OffMeshLink에 체크후 Navigation Area를 Jump로 설정해 주세요.
2. Bake탭설정
이후 Bake탭에서 아래와 같이 높이와 점프 거리를 설정후 오른쪽 하단의 Bake 버튼을 눌러주세요
그러면 아래와 같이 검은 원과 OffMeshLink설정한 오브젝트들이 서로 연결되 있는모습을 보실 수 있습니다.
3. OffMeshLink 컴포넌트 설정
이제 OffMeshLink로 설정한 오브젝트들에 OffMeshLink를 검색하여 OffMeshLink컴포넌트를 추가해 주세요
추가된 OffMeshLink컴포넌트는 다음과 같습니다.
이제 여기에 시작 지점과 종료 지점을 설정해주세요
예를들어 1번 지점의 OffMeshLink는 당연히 1번이 시작 지점이 되며 이동할 지점을 종료 지점에 넣으면 됩니다.
4. 스크립트 설정
예를들어 1번 지점의 OffMeshLink는 당연히 1번이 시작 지점이 되며 이동할 지점을 종료 지점에 넣으면 됩니다.
OffMeshLink를 사용하는 샘플 코드는 다음과 같습니다.
using System.Collections;
using UnityEngine;
using UnityEngine.AI;
public class NavOffMeshLinkController : MonoBehaviour
{
[SerializeField] GameObject targetObject;
private NavMeshAgent navAgent;
private OffMeshLink offmeshLink;
// Start is called before the first frame update
void Start()
{
navAgent = this.GetComponent<NavMeshAgent>();
offmeshLink = this.GetComponent<OffMeshLink>();
navAgent.destination = targetObject.transform.position;
}
// Update is called once per frame
void Update()
{
if (navAgent.isOnOffMeshLink) StartCoroutine(StartOffMeshLink());
}
IEnumerator StartOffMeshLink()
{
OffMeshLinkData _data = navAgent.currentOffMeshLinkData;
Vector3 _startPos = navAgent.transform.position;
Vector3 _endPos = _data.endPos + (Vector3.up * navAgent.baseOffset);
float _duration = (_endPos - _startPos).magnitude / 5.0f;
float _time = 0.0f;
while (_time < _duration)
{
_time += Time.deltaTime;
float tFactor = _time / _duration;
navAgent.transform.position = Vector3.Lerp(_startPos, _endPos, tFactor);
yield return null;
}
navAgent.CompleteOffMeshLink();
}
}