[Unity3D] 충돌 함수 OnTrigger와 OnCollision의 개념과 고찰

 


유니티에는 충돌 처리를 위한 함수가 내부적으로 존재하는데, OnTriggerOnCollision이다. 왜 충돌 처리에 2가지 종류의 함수가 존재하며, 각 함수가 어떤 역할을 하는지 알아보자.


충돌 처리 조건


1. 두 오브젝트가 collider를 가지고 있어야 한다.

Circle Collider2D, Box Collider, Capsule Collider 등 여러 종류가 있는데, 어떤 것이든 상관 없이 collider를 충돌을 하는 두 오브젝트 모두 가지고 있어야 한다.

2. 적어도 하나의 오브젝트는 Rigidbody를 가지고 있어야 한다.

충돌을 하는 두 오브젝트 모두 Rigidbody를 가져도 되며, 하나만 가지고 있어도 된다. Rigidbody는 물리 법칙에 관한 기능이므로 한 오브젝트가 움직인다면 해당 오브젝트에 Rigidbody를 부착시켜야 한다.





충돌 함수 - OnTrigger


두 오브젝트가 물리 연산을 하지 않고 충돌을 하려고 할 때 사용한다. 두 오브젝트 중 하나는 Collider의 Is Trigger가 true 상태여야 한다. Trigger를 사용하는 두 오브젝트는 부딪혔을 때 물리 법칙의 영향을 받지 않고 통과하게 된다.

OnTrigger 함수에는 6가지 종류의 함수가 있다.

    private void OnTriggerEnter(Collider other) { }

    private void OnTriggerEnter2D(Collider other) { }

    private void OnTriggerStay(Collider other) { }

    private void OnTriggerStay2D(Collider other) { }

    private void OnTriggerExit(Collider other) { }

    private void OnTriggerExit2D(Collider other) { }

OnTriggerEnter, OnTriggerEnter2D
두 오브젝트가 처음으로 충돌하는 순간에 한번 호출된다. 이 때 OnTriggerStay도 같이 호출되기 시작한다. 

OnTriggerStay, OnTriggerStay2D
두 오브젝트가 충돌하고 있는 동안 매 프레임마다 호출된다... 라고 설명이 되어있는데, 영구적으로 지속되는 것은 아닌 듯 하다. 이렇게 생각한 이유는 아래에서 설명하겠다.

OnTriggerExit, OnTriggerExit2D
두 오브젝트가 충돌에서 벗어났을 때 호출하게 된다. 







충돌 함수 - OnCollision


두 오브젝트가 물리 법칙에 영향을 받을 때 사용한다. 두 오브젝트가 부딪혔을 때 충돌을 감지하며, 적어도 하나의 오브젝트가 Rigidbody의 Body Type이 Dynamic 으로 설정되어야 한다.
OnTrigger 역시 6가지 종류의 함수가 있다.
    private void OnCollisionEnter(Collision collision) { }
    private void OnCollisionEnter2D(Collision collision) { }
    private void OnCollisionStay(Collision collision) { }
    private void OnCollisionStay2D(Collision collision) { }
    private void OnCollisionExit(Collision collision) { }
    private void OnCollisionExit2D(Collision collision) { }

OnCollisionEnter, OnCollisionEnter2D
두 오브젝트가 충돌하는 순간에 한번 호출 된다. 역시 OnCollisionStay도 이 순간부터 호출되기 시작한다.

OnCollisionStay, OnCollisionStay2D
두 오브젝트가 충돌하는 동안 계속 호출된다. 테스트를 해보지는 않았지만 OnTriggerStay와 같은 방식으로 동작한다면 위와 같이 영구적으로 호출되는 것은 아닐 것이라 생각한다.

OnCollisionExit, OnCollisionExit2D
두 오브젝트가 충돌이 끝날 때 호출된다. 






충돌에 관한 여러가지 고찰


1. 충돌의 방향을 어떻게 결정할 수 있을까?


OnTrigger나 OnCollision은 어떤 오브젝트에 충돌됐는지의 여부만 파악할 수 있다. 만약 충돌한 오브젝트가 어떤 방향에서 충돌되었는지 알고 싶다면 어떻게 해야할까?
2D로 예를 들면, 아래 코드를 한번 보도록 하자.
 private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag.Equals("Block"))
        {
            GameObject colBlock = collision.gameObject;
            block GetBlock = colBlock.GetComponent<block>();
            // 블록이 위쪽에 있을 경우
            if (posy == GetBlock.posy - 1 && posx == GetBlock.posx) collideBlock[0] = colBlock;
            // 블록이 아래쪽에 있을 경우
            else if (posy == GetBlock.posy + 1 && posx == GetBlock.posx) collideBlock[1] = colBlock;
            // 블록이 오른쪽에 있을 경우
            else if (posx == GetBlock.posx - 1 && posy == GetBlock.posy) collideBlock[3] = colBlock;
            // 블록이 왼쪽에 있을 경우
            else if (posx == GetBlock.posx + 1 && posy == GetBlock.posy) collideBlock[2] = colBlock;
        }
    }
block은 Block의 위치 정보를 담고 있는 클래스이다. "Block"이란 태그를 가진 오브젝트와 충돌했을 때, 해당 오브젝트가 기준 오브젝트의 x, y좌표에 대해 어디에 위치해 있는지 체크하면서 위, 아래, 왼쪽, 오른쪽을 파악하면 된다. 가령 기준 오브젝트가 (0, 0)일 때, 충돌 오브젝트가 위쪽에 있다면 (0, 1)이 될 것이며, 왼쪽에 있다면 (-1, 0)이 될 것이다. 
3D도 비슷할 것이다. 3D의 경우에는 x, y, z 좌표를 모두 찾아주어야 한다. 다만 3D게임의 경우 캐릭터와 같은 오브젝트는 머리, 팔, 몸, 다리 등 여러가지의 오브젝트의 집합체일텐데 (게임에 따라 다르겠지만) 해당 파츠의 좌표에 따라 각각 다르게 설정해야할 것이기 때문에 좀 더 복잡할 것이다. (애당초 게임에서 어떤 좌표에 충돌이 발생했는지 완벽하게 구해야 하는 경우는 많지 않을 것이라 생각한다.)


2. 하나의 오브젝트에 여러 개의 오브젝트가 동시에 충돌할 때 하나의 충돌 함수만으로 발생한 모든 충돌을 확인할 수 있을까?


OnTrigger나 OnCollision을 하나만 만들어도 여러 개의 충돌에 대해 체크할 수 있다. 
위의 영상을 보면, 오른쪽의 Player 스크립트의 Collide Block은 충돌 방향에 있는 오브젝트를 담는 배열이다. 
위의 코드를 이용해 체크를 하는데, 두 오브젝트에 동시에 충돌이 발생할 때 딜레이 없이 한번에 충돌 판정을 하는 것을 볼 수 있다.


3. OnTriggerStay, OnCollisionStay의 영구 지속에 관해서


세팅의 문제인지 개념의 문제인지 모르지만, Stay 함수들은 영구적으로 호출되지 않는 것처럼 보인다. 아래와 같은 코드를 작성했을 때, 아래와 같이 9977에서 더 이상 연산을 하지 않았다.
public int test = 10000;

    private void OnTriggerStay2D(Collider2D other)
    {
        if (other.gameObject.tag.Equals("testObject"))
        {
            test--;
            Debug.Log(test);
        }
    }



영구적으로 호출되는 함수라면 계속해서 빼야하는 것이 아닌가? "매 프레임마다 호출된다."라는 말에 어떤 함정이 숨어있는 것인가? 그냥 내가 멍청해서 무언가 놓치고 있는 것인가? 이에 대한 해답을 최대한 찾고 있지만 아직 발견하진 못했다. 혹시 누군가 알고 있다면 댓글로 어리석은 중생을 구제해주길 바란다.
일단의 실험 결과로는, OnTriggerStay와 OnCollisionStay는 영구적으로 호출되지는 않는 것 같다.

댓글

  1. rigidbody를 가진 오브젝트가 이동이나 회전을 멈추면 asleep상태로 들어가 스크립트 진행을 멈춘다고 합니다. sleepvelocity의 디폴트는 0.14라고 하니 0으로 바꿔두면 움직이지 않더라도 정상적으로 동작을 확인할 수 있을 것 같습니다.

    답글삭제
    답글
    1. 서원근양학계정2022년 8월 10일 오전 7:06

      감사합니다. Rigidbody 2D에서 Sleeping Mode를 Never Sleep으로 바꿔주었더니 문제가 해결되었습니다.

      삭제

댓글 쓰기