728x90

이 글은 인프런 [따라하면서 배우는 고박사의 유니티 기초] 강의를 수강하고 정리한 글입니다. 

 

[지금 무료] 따라하면서 배우는 고박사의 유니티 기초 | 고박사 - 인프런

고박사 | 유니티로 게임을 개발하고 싶은 초보자를 대상으로 하며, 유니티 설치부터 2D/3D 게임 개발에 필요한 기초 지식까지 자세하게 설명합니다. (강의에 사용되는 모든 리소스는 영상 상단의

www.inflearn.com


Navigation Mesh

Navigation Mesh는 바닥, 장애물과 같은 게임월드의 정보를 미리 저장해두고 에이전트 이동 시 저장된 정보를 바탕으로 장애물을 피해 목표 위치까지 이동하는 경로 탐색을 제공한다.

이를 위해 게임 월드에 Navigation Mesh를 설정하고 이동 에이전트에게 NavMeshAgent 컴포넌트를 기반으로 이동하도록 한다. 또한 offMeshLink 나 NavMeshObstacle 컴포넌트도 사용한다.

 

인프런 강의 자료가 아닌 유튜브에 있는 게임월드 패키지를 다운받아 진행하도록 한다.

 

3DGameBasic02-01.zip

 

drive.google.com

 

게임 월드 prefab을 Hierarchy View로 이동하여 배치한다. 


Navigation View

 

현재 2022.3..22f1 버전에서 Navigation View는 보이지 않기 때문에 강의와는 다르게 다운을 받아야 한다. Window > Package Manager > Unity Registry > AI Navigation 들어가서 AI Navigation 을 설치한다.

Navigation이 잘 보인다. 

 

Agents

네비게이션 메시 정보를 바탕으로 움직이는 이전트에 대한 설정 (NavMeshAgent 컴포넌트)

Agent Type

에이전트 속성으로 “+”로 새로운 에이전트 속성을 추가할 수 있다.

Agnet 정보

  • Name : Agent Type에 보여지는 이름
  • Radius : 에이전트의 반지름
  • Height : 에이전트의 높이(키)
  • Step Height : 오르내릴 수 있는 계단의 높이
  • Max Slope : 올라갈 수 있는 경사 각도

 

 

Areas

네비게이션 메시로 사용되는 오브젝트들의 구역 설정

Name

구역 이름으로 기본으로 Walkable(이동 가능), Not Walkable(이동 불가능), Jump(뛰기)가 제공되고, User 3부터 원하는 구역을 추가로 설정할 수 있다.

Cost

구역과 함께 등록. 이동하는데 소요되는 비용 (1 이상) 경로를 탐색할 때 Cost 정보를 기준으로 최단거리를 찾게 된다.

Cost가 2인 Jump는 Cost가 1인 Walkable을 지나갈 때보다 2배의 시간이 걸린다는 뜻으로 사용된다. (실제 오브젝트의 물리적인 이동 속도가 느려 지는 것이 아닌 경로를 계산할 때 활용된다)

 

 

Bake

네비게이션 메시 데이터를 생성

Baked Agent Size

  • Agent Radius : 에이전트가 지나갈 수 있는 반지름
  • Agent Height : 에이전트가 아래로 지나갈 수 있는 높이
  • Max Slope : 에이전트가 올라갈 수 있는 경사 각도
  • Step Height : 에이전트가 오르내릴 수 있는 계단의 높이
  •  

Generated Off Mesh Links

  • 오프 메시 링크는 올라가기 힘든 언덕, 사다리, 절벽 등을 연결해서 이동 가능하게 만드는 옵션
  • Drop Height : 이동할 수 있는 절벽 아래의 높이
  • Jump Distance : 뛰어서 넘을 수 있는 절벽 거리

Bake 버튼

Navigation에 설정된 옵션들을 바탕으로 네비게이션 정보를 데이터로 굽는다

 
 
 
 

Object

현재 씬(Scene)에 있는 오브젝트 설정 (하나 or 다수)

Scene Filter

현재 씬에서 원하는 오브젝트만 선택해서 볼 수 있다. (Mesh Renderer 컴포넌트, Terrain 컴포넌트 선택 가능)

선택된 오브젝트

  • Navigation Static : 네비게이션 메시로 사용할지 설정
  • Generate OffMeshLinks : 자동으로 Off Mesh를 생성할지 설정
  • Navigation Area : 해당 오브젝트의 구역 설정 (설정되는 구역에 따라 해당 오브젝트의 Cost가 설정됨)
 

 


Navigation Mesh 데이터 생성

게임 오브젝트의 Navigation static 설정하는 방법으로 원하는 게임 오브젝트 선택 후 Inspector View의 static > Navigation static 선택한 후 Object tab에 있는 Navigation Static 변수를 선택한다. 

Navigation View > Bake 에서 Bake 버튼을 누르면 현재 설정된 정보를 바탕으로 Navigation Mesh 데이터가 생성된다.

 

Scene view를 보면 하늘색으로 표시되는 부분이 이동할 수 있는 이동 경로이고, 하늘색 없으면 이동 불가능한 구역이다. Navigation View가 활성화 되어 있어야 Navigation Mesh가 출력된다.

또한 맵에 새로운 바닥 혹은 장애물을 배치했다면 Navigation Static 설정과 Bake를 다시 진행해야한다. 

 


경사각(Max Slope)과 계단 높이(Step Height) 설정

bake tab에서 Max slope가 45로 설정되어 있어서 경사각 45도까지는 이동할 수 있다. 그렇기 때문에 slope02 경로가 이어지지 않았다. 

 

 Step height는 오르내릴 수 있는 계단의 최대 높이이다. 현재 Step Height는 0.4로 인접한 두 계단 사이의 높이가 0.4 이하 차이가 나는 것 까지만 오를 수 있어 오른쪽의 경우 이동 경로가 생성되지 않았다. 

플레이어 오브젝트 생성

플레이어 오브젝트를 생성하고자 capsule 오브젝트를 추가한 뒤에 색상 설정을 위해 meterial 생성한다.

 

Player 오브젝트의 설정은 아래와 같이 했다.

 
 

 


Navigation Mesh 기반의 캐릭터 이동

player 오브젝트에 NavMeshAgent 컴포넌트를 등록한다. 

NavMeshAgent component

네비게이션 메시 정보를 기반으로 이동하는 에이전트 

 

 

에이전트 이동 (Steering)

  • Speed : 이동 속도
  • Angular Speed : 방향을 바꿀 때의 회전 속도
  • Acceleration : 가속도 (정지 상태에서 이동속도가 될 때까지 적용)
  • Stopping Distance : 목적지가 이 값까지 가까워지면 이동을 멈추게 된다
  • Auto Braking : 목적지에 가까워지면 멈추는 기능
  • (목적지에 도착해도 에이전트를 멈추지 않을 때 사용)
  • (여러 목적지를 계속 탐색하는 Patrol에 주로 사용)

장애물 회피 (Obstacle Avoidance)

  • Radius : 장애물을 회피할 때 에이전트의 반지름
  • Height : 에이전트의 높이
  • Quality : 장애물과 충돌 수준 (None이면 뚫고 지나간다)
  • Priority : 장애물과 충돌했을 때의 우선순위 (낮을 수록 높다)
  • (이동 중인 두 에이전트의 모든 조건이 동일할 때 Priority가
  • 낮은 에이전트가 더 우선권을 가지고 경로를 탐색하게 된다)

경로 탐색 (Path Finding)

  • Auto Traverse Off Mesh Link : 오프 메시 링크가 있을 경우
  • 자동으로 탐색해서 찾아갈지 설정
  • Auto Repath : 이동 중에 경로 탐색을 다시 할지 설정
  • (true : 이동 중에 장애물 등으로 막혔을 때 자동으로 재 계산)
  • Area Mask : 해당 에이전트의 이동 가능한 구역 지정

이동을 제어하는 스크립트 Movement3D

using System.Collections;
using UnityEngine;
using UnityEngine.AI;

public class Movement3D : MonoBehaviour
{
	[SerializeField]
	private	float		 moveSpeed = 5.0f;
	private	NavMeshAgent navMeshAgent;

	private void Awake()
	{
		navMeshAgent = GetComponent<NavMeshAgent>();
	}

	public void MoveTo(Vector3 goalPosition)
	{
		// 기존에 이동 행동을 하고 있었다면 코루틴 중지
		StopCoroutine("OnMove");
		// 이동 속도 설정
		navMeshAgent.speed = moveSpeed;
		// 목표지점 설정 (목표까지의 경로 계산 후 알아서 움직인다)
		navMeshAgent.SetDestination(goalPosition);
		// 이동 행동에 대한 코루틴 시작
		StartCoroutine("OnMove");
	}

	IEnumerator OnMove()
	{
		while ( true )
		{
			// 목표 위치(navMeshAgent.destination)와 내 위치(transform.position)의 거리가 0.1미만일 때
			// 즉, 목표 위치에 거의 도착했을 때
			if ( Vector3.Distance(navMeshAgent.destination, transform.position) < 0.1f )
			{
				// 내 위치를 목표 위치로 설정
				transform.position = navMeshAgent.destination;
				// SetDestination()에 의해 설정된 경로를 초기화. 이동을 멈춘다
				navMeshAgent.ResetPath();

				break;
			}

			yield return null;
		}
	}
}

NavMeshAgent와 같이 유니티에서 만든 인공지능을 사용하기 위해서는 UnityEngine.AI가 필요하다.

  • NavMeshAgnet.SetDestination(Vector3 position) : position을 목표지점으로 설정 (목표지점만 설정되면 경로를 탐색해서 자동 이동함)
  • float distance = Vector3.Distance(Vector3, Vector3) ;a와 b 두 벡터 사이의 거리 값을 구한다.
  • NavMeshAgnet.destination : SetDestination()으로 설정한 목표지점 Vector3 정보가 저장되어 있다.
  • NavMeshAgent.ResetPath() : 현재 설정되어 있는 이동 경로를 초기화하여 이동을 멈추게 한다 .

Physics.Raycast()를 이용한 오브젝트 선

우리가 바라보는 화면은 2차원 모니터이고, 게임 세상은 3차원으로 이루어져 있다. 즉, 2차원(2D)을 통해 3차원(3D)의 오브젝트를 제어해야 한다.

구현 원리

  1. 카메라로부터 플레이어가 클릭한 마우스 위치를 관통하는 광선을 생성하여 발사한다.
  2. 현재 카메라에 보이는 화면을 관통해 뻗어나가는 광선은 우리가 지정한 길이에 도달하거나 오브젝트에 부딪히면 멈춘다.
  3. 오브젝트에 부딪혀 멈추게 되면 부딪힌 오브젝트의 정보를 반환한다.(광선에 부딪혔다 = 마우스로 선택한 오브젝트이다.)

플레이어를 제어하는 스크립트 PlayerController

using UnityEngine;

public class PlayerController : MonoBehaviour
{
	private	Movement3D movement3D;

	private void Awake()
	{
		movement3D = GetComponent<Movement3D>();
	}

	private void Update()
	{
		// 마우스 왼쪽 버튼을 눌렀을 때
		if ( Input.GetMouseButtonDown(0) )
		{
			RaycastHit hit;
			// Camera.main : 태그가 "Camera"인 오브젝트 = "Main Camera"
			// 카메라로부터 마우스 좌표(Input.mousePosition) 위치를 관통하는 광선 생성
			// ray.origin : 광선의 시작 위치
			// ray.direction : 광선의 진행 방향
			Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

			// Physics.Raycast() : 광선을 발사해서 부딪히는 오브젝트를 검출
			// (광선에 부딪히는 오브젝트가 있으면 true 반환)
			// ray.origin 위치로부터 ray.direction 방향으로 무한한 길이(Mathf.Infinity)의 광선 발사
			// 광선에 부딪히는 오브젝트의 정보를 hit에 저장
			if ( Physics.Raycast(ray, out hit, Mathf.Infinity) )
			{
				// hit.transform.position : 부딪힌 오브젝트의 위치
				// hit.point : 광선과 오브젝트가 부딪힌 세부 위치
				
				// hit.point를 목표위치로 이동!
				movement3D.MoveTo(hit.point);
			}
		}
	}
}
 

 

 
카메라가 게임월드를 전체적으로 볼 수 있도록 카메라의 위치와 회전값을 수정한 뒤에 실행한다. 선택한 위치로 player 오브젝트가 이동하지만 이동경로가 없는 사다리나 높은 경사면, 절벽 등을 선택하는 경우 근처까지만 이동하고 더 이상 이동하지 못한다. 

+ Recent posts