[Unity] OnTriggerStay() 함수: 오브젝트의 변화가 감지되지 않으면 더 이상 호출되지 현상 해결법

2022. 1. 19. 20:42Game Development/Unity

 

적(Enemy) 게임 오브젝트에 탐지(Detect)를 담당하는 콜라이더를 달아 플레이어가 현재 적 오브젝트 탐지 영역 안에 있는지 체크하는 로직을 구현하려고 했었다. 탐지만 하고, 충돌 처리는 하면 안 되니 콜라이더의 Is Trigger 모드를 활용했다.

 

 

구현하고자 했던 로직 및 준비 작업

 

  1. 적은 평상시 상태(Idle)로 여기 저기를 돌아다님
  2. 플레이어가 적의 탐지 영역에 처음 들어온다면, 적은 경계 모드로 돌입
  3. 플레이어가 계속 적의 탐지 영역에 2초 이상 머무르고 있다면, 빠르게 돌진하여 공격하는 공격 모드 돌입
  4. 적이 공격 모드로 돌입 전에 탐지 영역에서 나간다면, 다시 평상시 상태로 돌입

 

리소스는 없고, 로직만 먼저 구현해보려고 해서 캡슐을 적(Enemy), 사각형을 플레이어(Player)로 설정했다.

 

큰 초록 사각형이 적이 플레이어를 탐지할 탐지 영역(Detect Area)이다.

 

적(Enemy) 오브젝트의 하위 오브젝트로 빈 게임 오브젝트(Empty Object)를 만들고, 여기에 탐지만 담당하는 콜라이더와 탐지를 제어할 스크립트(script)를 달아줬다.

 

적 계층구조

 

적(Enemy) 오브젝트는 여러 오브젝트들과 물리 상호 작용 및 충돌을 해야 하므로, Capsule Collider 2DRigidBody 2D 컴포넌트를 달아줬다. 중력을 적용하면 적 오브젝트는 아래로 떨어지니, 우선 Grivity Scale 값은 0으로 설정해뒀다.

 

플레이어(Player) 오브젝트에도 RigidBody 2DBox Collider 2D를 달아주고, 태그(Tag)를 Player로 설정했다.

/*  RigidBody 컴포넌트가 있어야 콜라이더로부터 충돌 처리 및 감지에 대한 보고를 받을 수 있다.  */

 

스크립트 내용은 다음과 같이 테스트용으로 간단하게 만들어 줬다.

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

public class Detector : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Player"))
        {
            Debug.Log("적이 감지되었습니다.");
        }
    }

    private void OnTriggerStay2D(Collider2D collision)
    {
        if (collision.CompareTag("Player"))
        {
            Debug.Log("적이 계속 감지되고 있습니다.");
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.CompareTag("Player"))
        {
            Debug.Log("적이 사라졌습니다.");
        }
    }
}

 

 

 

실험 및 문제점 파악

플레이어 오브젝트를 적의 탐지 영역 콜라이더에 넣어두고, 잠시 뒤에 빼는 실험을 해봤다.

 

제대로 동작한다.

 

이제 이걸 기반으로 구현하고자 했던 로직을 만들었다. 하지만, 적이 탐지를 하는 것까진 괜찮은데 2초 이상 탐지 후에 돌진하는 로직이 제대로 작동하지 않았다. 짰던 코드를 간단하게만 소개하자면 다음과 같다.

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

public class Detector : MonoBehaviour
{
   float startWarningTime;
   const float DELAY = 2f;

   private void OnTriggerEnter2D(Collider collision)
   {
      if(collision.CompareTag("Player"))
      {
         startWarningTime = Time.time; 
         // 경계 모드 돌입 코드
      }
   }

   private void OnTriggerStay2D(Collider collision)
   {
      if(collision.CompareTag("Player"))
      {
         float playerStayTime = Time.time;
         bool canRush = playerStayTime >= startWarningTime + DELAY; 
         if(canRush)
         {
            // 공격 모드로 돌입 코드
         }
      }
   }
   
   private void OnTriggerExit2D(Collider collision)
   {
      if(collision.CompareTag("Player"))
      {
         // 평상시 모드로 돌입 코드
      }
   }
}

 

코드가 이상한 것 같지는 않았다. Time.time 값이 제대로 들어가지 않는 것인가 싶어서 디버그를 해봤는데 제대로 들어가는 것을 확인했다. 그렇다면 생각해볼 수 있는 건, OnTriggerStay() 함수가 계속 호출되는지 여부였다. 플레이어 오브젝트를 적 탐지 영역에 넣어두고 가만히 둬 봤다.

 

OnTriggerStay() 함수가 일정 횟수 호출되고 더 이상 호출되지 않는다.

 

플레이어 오브젝트를 탐지 영역 내부에서 살짝 움직인 후, 다시 가만히 둬 봤다.

 

OnTriggerStay() 함수가 다시 몇 번 호출되다가 더 이상 호출되지 않았다.

 

 

이걸 보니, 왜 2초가 지나더라도 돌진을 안 했는지 알 수 있었다. 여러 번 호출되다가 어느 한 시점에서 돌진 모드로 들어가기 위한 조건이 충족되었지만, 다음 호출이 일어나지 않아 돌진 모드로 변경하는 코드들이 실행되지 않았던 것 같다.

 

그 증거로, 2초 이상 플레이어가 감지 되어도 돌진을 안 했던 적이 플레이어를 탐지 영역 내부에서 살짝 움직이니 돌진을 하기 시작했다. 해보지는 않았지만, OnCollisionStay() 함수도 비슷하지 않을까란 생각이 든다.

 

 

해결법

(2023.07.10 추가)

 

친철하신 분이 위 현상에 대한 해결 방법을 댓글로 알려주셨다.

Rigidbody 컴포넌트는 오브젝트의 변화가 일정 시간동안 없을 경우, 슬리핑(Sleeping) 모드로 돌입하는 것 같다.

(최적화 때문에 그러한 듯 하다.)

 

위 현상도 이러한 기능 때문에 발생하는 것이므로, 슬리핑 모드를 Never Sleep으로 바꿔주면 해결된다고 하신다.

좋은 정보 알려주셔서 감사합니다 :)

728x90
반응형