내일배움캠프 5주차 3일차 TIL - 오브젝트 추적 1

백흰범·2024년 5월 16일
0
post-thumbnail

오늘 한 일

  • 챌린지반 과제 진행
  • 팀 프로젝트 계획 수립 및 진행
  • 객체 지향 특강 듣기
  • 기초적인 오브젝트 추적 구현해보기

오늘은 적들이 플레이어를 찾고 추적하는 방법의 기초적인 구현을 해보려고 한다.


오브젝트 추적

적이 플레이어 오브젝트를 찾아서 쫓아가기까지 필요한 과정

1. 플레이어 오브젝트를 식별한다

  • 적 오브젝트는 플레이어를 감지하는 기능 또는 플레이어의 정보를 어디선가 받아와야 된다.


2. 자신과 플레이어 오브젝트까지의 방향을 구한다.

  • 플레이어 Transform이 요구되고, normalized를 하여 단위 벡터를 구한다.


3. 구한 방향을 향해서 플레이어를 쫓아간다.

  • FixedUpdate를 활용해서 전 과정에서 구한 단위 벡터값을 이용해 플레이어의 방향으로 이동한다.



플레이어의 위치를 항상 가진 상태의 추적

플레이어의 Transform을 명확하게 아는 상태에서의 추적을 구현해보자

  • Player의 오브젝트를 싱글턴화할 수는 없으니 싱글턴된 게임 매니저를 이용해야한다.
    (스파르타 코딩클럽 스크립트를 참고해서 만들었습니다)

1. 플레이어 오브젝트 정보 받아오기

public static GameManager Instance;
{
    public Transform Player { get; private set; }	// 플레이어의 위치 참조
    [SerializeField] private string playerTag = "Player"; // 플레이어 태그

    private void Awake()
    {
        Instance = this;
        Player = GameObject.FindGameObjectWithTag(playerTag).transform; // 태그를 통해 플레이어 오브젝트를 찾는다.
    }
}

// Enemy.cs
  private Transform Target { get; private set; } // 플레이어 트랜스폼 참조할 변수

  private void Start()
  {
      Target = GameManager.Instance.Player; // 게임 매니저에 있는 플레이어 트랜스폼을 받아온다
  }
  • 게임 매니저로부터 플레이어의 Transform을 받아온다

2. 플레이어 오브젝트 쪽 방향 구하기

private void FixedUpdate()
{
    direction = DirectionToTarget(); // FixedUpdate로 실시간으로 플레이어의 위치를 계산한다.
    ...
}

private Vector2 DirectionToTarget()
{
    return (Target.position - transform.position).normalized; // 플레이어까지의 단위 벡터를 구한다.
}

3. 구한 방향 쪽으로 이동하기

private void FixedUpdate()
{
	...
    CallMoveEvent(direction); // CallMoveEvent 내에서 움직이는 메서드가 구현되어있습니다.
}
  • 구한 단위 벡터를 이용해서 플레이어 쪽으로 향한다.

결과물

  • 플레이어가 어디에 있든 항상 따라온다. (뱀서류에 어울리는 추적 방식)
  • 추가 구현 사항을 해주면 플레이어의 방향에 따라서 스프라이트를 뒤집거나 일정 거리 이상은 안 따라가게 하는 등의 기능을 넣을 수 있다.

Collider를 이용한 플레이어 추적

적 오브젝트가 고유의 Collider를 이용해 플레이어를 추격하는 로직을 구현해보자

  • 오브젝트 자체가 탐지 기능을 가지고 있기에 타 오브젝트의 의존도가 낮은 편이다.
    (사실 식별 말고는 2번째는 차이가 없으니 식별과 이동에만 집중해보자)

플레이어 식별

public class ColliderVision : TopDownController
{
	[SerializeField] private string targetTag = "Player";	// 
	private GameObject FindTarget;	// 찾은 적 오브젝트 정보를 담는다.

	[SerializeField] private Collider2D colliderVision; // 자식이 가지고 있는 Collider를 가져온다.

  	private void OnTriggerEnter2D(Collider2D collision) // 특정 오브젝트가 Collider에서 진입할 때 호출
    {
		if (collision.CompareTag(targetTag))	// 태그 확인
		{
			... (추후에 작성) ...
			FindTarget = collision.gameObject;	
		}
	}

    private void OnTriggerExit2D(Collider2D collision)	// 특정 오브젝트가 Collider에서 나갔을 때 호출
    {
		if (collision.CompareTag(targetTag))	// 태그 확인
		{
			... (추후에 작성) ...
		}
  }
  • Collider 설정

  • Collider를 이와 같이 설정하였다.

플레이어 추적

private Vector2 targetDirection;	// 타겟 방향 (계산은 전 방법과 동일하니 생략)

private bool isTargetFind = false;	// 타겟 식별 유무
private Vector2 stopMove;			// 정지를 위한 벡터

[SerializeField] private Transform colliderTransform;	// 충돌체 Transform

void Start()
{
    stopMove = Vector2.zero; // 0 벡터 할당
}

private void FixedUpdate()	// 물리 처리 업데이트
{
    if (!isTargetFind) 	// 타겟을 찾지 못했을 경우
    { 
        return; 
    }
        targetDirection = DirectionToTarget();	// 타겟 목표 방향 최신화
        CallMoveEvent(targetDirection);		// 이동 이벤트 호출
        ChangeRotation(targetDirection);	// 충돌체 방향 돌리기
}

private void OnTriggerEnter2D(Collider2D collision)	// 충돌체 진입 시
{
    if (collision.CompareTag(targetTag))	// 태그 확인
    {
        isTargetFind = true;	// 타겟 식별 true
        FindTarget = collision.gameObject;	// 충돌체에 진입한 오브젝트를 할당한다
    }
}

 private void OnTriggerExit2D(Collider2D collision)	// 충돌체 이탈 시
 {
     if (collision.CompareTag(targetTag))	// 태그 확인
     {
         isTargetFind = false;	// 타겟 식별 false
         targetDirection = Vector2.zero;	// 0벡터 할당
         CallMoveEvent(stopMove);	// 이동 이벤트 멈추기
     }
 }

구현 결과

  • 콜라이더에 플레이어가 진입하게 되면 적이 추적을 하기 시작한다.
  • 플레이어의 위치를 최대한 놓치지 않기 위해서 콜라이더도 플레이어의 방향에 따라 움직이게 만들었다.

+ 추가 구현 (탐색 기능, 스프라이트 뒤집기)

  • 추가 구현을 통해 적 오브젝트에게 매우 간단한 탐색 기능을 만들어서 주변을 탐색하게 만들어봤다.

상시 추적법과 Collider 추적법의 장단점

상시 추적법

  • 장점

    • 매우 빠르고 간단하게 구현할 수 있다.
    • 참조를 통해 플레이어의 정보를 가져오기 때문에 메모리가 덜 든다.
  • 단점

    • 플레이어 정보를 주는 오브젝트에게 의존해야한다.
    • 추적 방식의 확장성이 떨어진다.
    • 플레이어 중간에 가려지는 오브젝트가 있을 경우의 식별 처리가 힘들다

Collider 추적법

  • 장점

    • 탐색과 추적 방식의 확장성이 높다.
    • 타 오브젝트에게 의존하지 않아도 된다.
  • 단점

    • 오브젝트가 많을 경우 그만큼 연산량이 많아진다.
    • 플레이어 중간에 가려지는 오브젝트가 있을 경우의 식별 처리가 힘들다.



그 외에 알게된 점

FixedUpdate와 이벤트

FixedUpdate에서 이벤트에게 한번이라도 direction을 전달할 경우 그 이벤트는 direction 값이 변경되거나 다른 명령으로 인해 중지될 때까지 계속 활성화된다.

버그 리포트)

private void OnTriggerExit2D(Collider2D collision)
{
    if (collision.CompareTag(targetTag))
    {
        isTargetFind = false;
        targetDirection = Vector2.zero;
        // CallMoveEvent(stopMove); <- 이를 제거할 경우
    }
}

  • 플레이어를 놓쳤음에도 플레이어를 식별했던 마지막 방향으로 떠나버린다.

Transform.rotation와 Transform.eulerAngles의 반환값

  • Transform.rotation는 쿼터니언으로 사실상 우리가 해석할 수 없는 값을 반환하고
  • Transform.eulerAngle는 0~360도 값을 반환해준다.
    (atant2 * Rad2Deg와 유니티에 보이는 rotation이랑 전혀 다른 값을 반환하니 주의하자)


사용한 에셋




작성하면서 느낀점

내가 만들고 싶은 게임을 구현하기 위해서 더 많은 기능 구현 방법에 대해서 알고 싶어졌다. 내가 아는 선 안에서는 최대한의 기능 구현이 가능한 상태이지만, 아직까지도 모르는 것이 많다고 느껴졌다. 좀 더 많은 학습을 통해서 게임 내의 모든 로직을 구현해 구동이 가능한 나만의 게임을 출시해보고 싶다.

profile
게임 개발 꿈나무

0개의 댓글