RPG게임을 하다보면 캐릭터를 이동시킬 때 클릭한 좌표로 캐릭터가 장애물들을 피해 이동하게 된다. 이처럼 플레이어가 장애물을 피해 특정 좌표를 향해 최단거리로 이동하는 기능을 유니티에서 제공하고 있다.
유니티에서 제공하는 NavMesh AI를 활용하여 장애물 오브젝트들과 Enemy를 피해 움직이는 player 미니게임을 만들어보기러 했다. 플레이 범위는 Walls 내로 좁혔고, Sphere/Cube/Cylinder 장애물들을 배치했다.
먼저 PlayMode에서 좌클릭 시 해당 좌표로 플레이어가 이동하는데 배치된 장애물들을 피해 최단 동선으로 찾아가는 Navigation AI를 사용해야 한다. Window - AI - Navgation에 들어가면 우측 Inspector 창 옆에 Navigation 창이 새로 생성된다.
Static에서 Navigation으로 바꾸고 Bake 탭에 들어가보면 Agent Radius, Agent Height, Max slope, Step Height 등이 있다. 이 기능은 플레이어의 이동이 허용되는 공간 범위를 지정해 주는 것으로 하늘색 부분이 이동 가능한 지역이다. 요소들의 비율을 조정해 주면서 장애물 위를 건너게 할 수도 있고, 장애물 사이에 플레이어가 끼는 등의 문제를 해결할 수도 있다. 원하는 만큼 이동 공간을 설정한 후 Bake를 해주면 된다.
플레이어 인스펙터 창에서 추가해준 컴포넌트들이다. 플레이어를 실린더 형태로 만들었기 때문에 Capsule Collider을 설정해 주었다. 그리고 네비게이션 AI 활성화를 위해 Nav Mesh Agent를 추가해주었다. NavMesh Agent는 이동 가능한 영역을 허용하는 권한이라고 보면 된다. TargetPos는 플레이 모드 시 클릭한 위치로 캐릭터가 이동하는 것을 시각적으로 보여주고, Create Empty 또는 아무 오브젝트나 생성해서 material을 입힌 후 추가해 주었다.
using UnityEngine;
using System.Collections;
public class player : MonoBehaviour {
public GameObject targetObj = null; // 위치 표시 게임 오브젝트
RaycastHit hit; // 레이 충돌체 정보
UnityEngine.AI.NavMeshAgent agent; // 네비게이션 매쉬 에이전트
void Start () {
agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
}
void Update() {
if (Input.GetButtonDown("Fire1")) // 마우스 버튼 클릭(왼쪽) 했을때
{
MoveTarget();
}
}
void MoveTarget()
{
// 현재 마우스 위치를 월드 좌표로 변환 하여 Ray에 담음
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) //레이를 쏴서 충돌체에 닿았을때
{
if(hit.transform.gameObject.name == "Floor_")
{
// 위치 표시 오브젝트 이동
targetObj.transform.position = new Vector3(hit.point.x,targetObj.transform.position.y, hit.point.z);
agent.destination = hit.point; // 에이전트 클릭 지점으로 이동
}
}
}
}
그리고 Player 스크립트를 작성하여 추가해준다. 코드 해석은 위에 설명을 보고 참고. 그리고 여기서 Raycast개념이 나오는데 Navigation AI를 활용하는 대부분의 게임에서 사용되기 때문에 매우 중요한 내용이다. Target pos에 설정된 오브젝트를 hit하게 하겠다는 내용이다. Raycast에 대한 자세한 설명은 여기를 참고 https://gameforfun.tistory.com/entry/Unity-Ray%EC%9D%98-%EC%82%AC%EC%9A%A9
그 다음으로는 enemy 오브젝트를 설정한다. player과 컴퍼넌트는 동일하다. 스크립트는 플레이어와 enemy가 특정 거리 안으로 좁혀졌을 때 enemy가 자동적으로 player을 따라가게 만드는 코드를 작성하였다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class enemy : MonoBehaviour
{
GameObject playerObj = null;
UnityEngine.AI.NavMeshAgent agent = null;
public float distanceValue = 3f;
float distance = 0;
void Start()
{
agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
playerObj = GameObject.Find("Player");
}
void Update()
{
DistanceCheck();
}
void DistanceCheck()
{
distance = Vector3.Distance(transform.position, playerObj.transform.position);
if (distance < distanceValue)
agent.destination = playerObj.transform.position;
}
}
Find()함수를 사용해서 특정 오브젝트를 찾게 하는 기능을 추가했는데 다양한 게임에서 종종 사용되니 알아두면 좋을 것 같다. player과 enemy 사이의 거리는 distanceValue를 public으로 선언해놓았기 때문에 인스펙터 창에서 조정해줄 수 있다.
Find와 Find(tag)의 차이점이 있다면 Find해야할 오브젝트들이 무수히 많다면(예를 들어, 총알) tag를 달아서 깔끔하게 정리할 수 있다. 여러 오브젝트에게 쉽게 명령을 내리기 위한 함수. Find()는 특정 오브젝트를 다이렉트로 찾기 위한 함수.
(Lookat함수는 쳐다보기만 하고 따라오거나 물리적 충돌은 X)
플레이 모드를 실행했을 때 거리가 좁혀지면 enemy가 player을 잘 따라오는 것을 볼 수 있다. Main camera를 player에 가까이 줌하여 Alligned with view하고, main camera를 player에 종속시킨 후 플레이 해보면 좀더 실감나는 1인칭 게임 느낌을 낼 수 있다.
내비메시 | 에이전트가 걸어다닐 수 있는 표면 |
내비메시 에이전트 | 내비메쉬 위에서 경로를 계산하고 이동하는 캐릭터 또는 컴포넌트 |
내비메시 장애물(Obstatcle) | 에이전트의 경로를 막는 장애물 |
오프메시 링크(off Mesh Link) | 끊어진 내비메시 영역 사이를 잇는 연결 지점 |
'게임 프로그래밍 > 유니티 프로젝트' 카테고리의 다른 글
유니티를 활용한 3D 적 피하기 게임 만들기(1) (0) | 2021.07.31 |
---|---|
[Unity Error]유니티 안드로이드 빌드 시 "Package Name has not been set up correctly" 오류 해결 방법 (0) | 2021.07.29 |
유니티를 활용한 2D Plane Shooting 게임 만들기(2) (0) | 2021.07.29 |
유니티를 활용한 2D Plane Shooting 게임 만들기(1) (0) | 2021.07.24 |
무료 3D 캐릭터 애니메이션 다운로드 사이트 - Mixamo 캐릭터 FBX 파일 Unity 임포트, 애니메이션 세팅 방법 (0) | 2021.07.24 |