Life Archive
article thumbnail

INTRO

Unity AI Navigation을 이용하여 플레이어를 추적하는 몬스터 시스템을 Tag를 이용하여 구현하던 도중, 동일한 Tag의 플레이어가 다수일 경우 몬스터는 어떤 플레이어를 우선순위로 따라갈지 궁금해졌다.

 

여러 테스트를 통해  우선순위 기준이 Tag라는 시스템에 있었다는 것을 알았고, 그 과정에 대해 적어보려고 한다.

 


CONTENT

우선 몬스터 Control에 대한 기본적인 코드를 첨부하면 아래와 같다.

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

public class MonsterCtrl : MonoBehaviour
{
    private Transform monsterTr;
    private Transform playerTr;
    private NavMeshAgent agent;

    void Start()
    {
        monsterTr = GetComponent<Transform>();
        playerTr = GameObject.FindWithTag("PLAYER").GetComponent<Transform>();
        agent = GetComponent<NavMeshAgent>();
        
        agent.destination = playerTr.position;
    }
}

플레이어의 Transform을 FindWithTag를 이용하여 PLAYER Tag의 오브젝트에서 가져왔고,

엔진 내에서도 정상적으로 작동하였다. 

 

플레이어가 한 명인 싱글에서는 문제가 없겠지만,

이 기능을 플레이어가 여럿인 멀티에서 사용할 때 과연 어떤 플레이어를 우선으로 추적할 것인가가 궁금해졌다.

 

보통 게임에서는 몬스터로부터 가장 가까이 있는 플레이어 혹은 체력이 가장 적은 플레이어를 추적하고,

그 과정은 따로 조건 스크립트 처리가 필요하다.

별도의 스크립트 처리 없이 위 코드처럼 구현했을 때는 어떤 플레이어를 우선 순위를 추적할까?


① 오브젝트 이름 (Object Name)

 

PLAYER Tag를 가진 플레이어 오브젝트를 두 개 생성하고, 이름을 각각 a_Player, b_Player로 변경하여 실행해보았다.

Name - Test1

첫 테스트 때는 b_Player를 추적하였고, 만약 이름 기준이 맞다면 알파벳 순서가 뒤인 오브젝트를

우선으로 추적하겠거니 싶어 두 번째 테스트를 해보았다.

Name - Test2

각각의 오브젝트 이름을 d_Player, c_Player로 다시 변경하여 실행해본 결과, 이번에는 c_Player를 추적하였다.

만약 첫 번째 테스트에서 얻은 결과가 맞다면 당연히 알파벳 순서가 뒤인 d_Player를 추적해야하지만 그렇지 않았다.

 

이로부터 이름 기준이 아니라는 것을 알 수 있었다.


② Hierarchy 배치 순서 (Order)

 

두 플레이어 오브젝트를 Player1과 Player2라고 이름을 변경하고 Hierarchy 상에서 순서를 바꾸면서 테스트를 해보았다.

Hierarchy - Test1

첫 번째 테스트는 Player1 오브젝트가 Player2 오브젝트보다 상단에 위치시켰고,

그 결과 맨 아래에 있는 Player2 오브젝트를 추적하였다.

Hierarchy 에서의 순서가 기준이 맞다면 가장 아래에서부터 찾는다는 것이었다.

 

곧바로 순서를 바꾸어 두 번째 테스트를 진행하였다.

Hierarchy - Test2

두 번째 테스트에서는 Player1 오브젝트가 Player2 오브젝트보다 아래에 위치하도록 Hierarchy에서 수정하였고,

첫 번째 테스트에서 얻은 가설이 맞다면 몬스터는 Player1을 추적해야했다.

 

하지만, 첫 번째 테스트와 동일하게 Player2를 추적하였고, 이로써 두 번째 가설도 아니라는 것을 알 수 있었다.


③ 몬스터로부터의 거리 (Distance)

 

두 플레이어 오브젝트 중 몬스터로부터 가장 가까이 있는 플레이어를 우선 순위로 하여 추적할 수도 있겠다 싶어

거리를 다르게 하여 테스트를 진행해보았다.

Distance - Before Play

실행하기 전 위에서 바라본 오브젝트들의 위치이다.

Player1이 Player2보다 몬스터와 더 가까이 위치해있는 상태에서 첫 번째 테스트를 진행하였다.

Distance - Test1

첫 번째 테스트에서는 더 멀리 배치된 플레이어를 추적하였기에,

만약 거리 기준이 맞다면 더 멀리 있는 오브젝트를 찾아 우선 순위로 잡는다는 것이었다.

 

다음은 반대로 Player1을 더 멀리 배치했을 때 과연 Player1을 추적하는지 테스트해보았다.

Distance - Test2

하지만 두 번째 테스트에서는 몬스터와 더 가까이 배치된 Player2를 추적하였고,

이를 통해 거리도 기준이 아니라는 것을 알 수 있었다.


④ 장애물의 유무 (Obstacle)

 

플레이어와 몬스터 사이에 장애물이 있을 경우도 혹시 몰라서 테스트를 해보았다.

몬스터와 동일한 거리로 두 플레이어 오브젝트를 배치하고 장애물을 설치하고 없애기를 반복하여 테스트를 두 번 하였다.

Obstacle - Before Play

장애물을 놓기 전 몬스터와 플레이어의 위치를 0, 0, 0으로 모두 초기화하고

두 플레이어는 각각 x방향으로 +10, -10만큼 거리를 벌렸다.

 

그리고 첫 번째 테스트는 Player1 방향에 장애물을 설치했을 때를 확인해보았다.

물론 NavMesh를 생성할 때 장애물도 Navigation Static으로 지정하여 Mesh가 생성되도록(장애물로 인식하도록) 하였다.

Obstacle - Test1

첫 번째 테스트에서는 장애물이 없는 Player2를 추적하였다.

만약 이 가설이 맞다면 장애물이 없는 오브젝트를 우선으로 찾을 것이다. 

 

이번에는 반대로 Player2 방향에 장애물을 설치하여 테스트해보았다.

Obstacle - Test2

두 번째 테스트에서는 장애물이 있더라도 몬스터는 Player2를 추적하였고, 이 가설도 틀렸음을 입증되었다.


⑤ Tag System

 

도저히 어떤 것이 기준이 되는지 모르겠어서 구글에 검색을 해보았고,

오브젝트에게 Tag를 지정할 때 Stack처럼 쌓인다는 것을 찾을 수 있었다.

 

PLAYER라는 Tag에 오브젝트를 지정할 때 Stack 자료 구조처럼 쌓이게 되고,

FindWithTag를 사용하면 마지막으로 쌓인(지정된) 오브젝트를 찾게 된다는 것이다.

 

그래서 여태껏 테스트 동안 플레이어 오브젝트 복사본만을 계속 몬스터가 추적했던 것이다.

 

확실하게 하기 위해 이번에는 플레이어 오브젝트 복사본을 5개를 만들어 각각의 오브젝트 이름을

Player1 ~ Player5까지 지정하여 테스트를 진행하였다.

Tag - Test

정말로 마지막으로 복사되어 Tag에 저장된 Player5를 추적하였고 이 사실이 옳다는 것을 알 수 있었다.


RESULT

굳이 Navigation을 사용하지 않더라도 Tag를 이용하여 게임 오브젝트를 찾을 때,

Tag라는 System이 Stack 자료 구조처럼 사용된다는 것을 인지하고 개발을 해야한다.

 

멀티에서 이 기능을 사용할 때, 몬스터가 추적해야하는 대상을 내가 정하고 싶다면

Tag를 마지막으로 바꿔준 후 스크립트를 실행한다면 정상적으로 작동할 것임을 알 수 있었다.