게임 오브젝트에 Collider 컴포넌트를 추가하지 않고 한번만 충돌체를 찾아내는 Physics의 Cast 계열 함수들의 사용법


유니티 엔진에서 충돌체(Collider)를 찾아내는 방법은 여러 가지가 있다. 일반적으로는 게임 오브젝트에 캡슐(Capsule), 박스(Box), 구(Sphere) 형태의 콜라이터 컴포넌트를 달아서 OnTrigger나 OnCollision 계열의 이벤트를 사용해서 충돌체를 찾아내게 된다. 하지만 위 방법의 경우에는 콜라이더 컴포넌트를 지속적으로 유지해야하고 OnTrigger나 OnCollision 계열의 이벤트는 매 프레임 실행되기 때문에 필요한 순간에 한번만 충돌체를 찾아내려는 경우에는 성능상 부적절할 수 있다.


이렇게 필요한 순간에 단 한번 충돌체를 찾아내는 함수는 Physics라는 클래스가 static으로 가지고 있고 여기에 해당하는 함수들은 Cast라는 이름이 붙어있다. 이 Cast 함수에는 크게 4가지의 충돌체를 찾아내는 모양이 있는데 Ray, Box, Sphere, Capsule이 그것이다.


Ray

가장 일반적으로 사용되는 형식으로 특정 지점에서 시작하여 특정한 방향으로 향하는 직선 형태의 Cast이다. 이 직선에 닿은 Collider를 찾아낸다. 이 Ray Cast는 주로 사용자가 클릭한 지점이나 오브젝트를 3D 공간 상에서 찾아내기 위해서 주로 사용된다.


Box

설정한 중심점을 시작으로 하여 지정한 가로, 세로, 높이에 해당하는 직육면체 형태의 Cast이다. 이 직육면체의 면에 닿거나 직육면체의 안에 있는 Collider를 찾아낸다.


Sphere

설정한 중심점을 기준으로 지정한 반지름 내의 구 형태의 Cast이다. 이 구의 표면에 닿거나 구 안에 있는 Collider를 찾아낸다.


Capsule

설정한 두 점을 있는 선을 중심으로 지정한 반지름만큼의 캡슐 형태의 Cast이다. 이 캡슐의 표면에 닿거나 캡슐 안에 있는 Collider를 찾아낸다.


앞서서 살펴본 내용이 충돌체를 찾아내는 모양에 따른 분류였다면 지금 이야기하는 것은 찾아낼 충돌체의 개수에 따른 분류이다. 이 분류에는 Cast, CastAll, CastNonAlloc이 있다.


Cast

Cast는 찾아낸 충돌체 하나만을 반환한다. Ray Cast를 예로 들자면 제일 처음 선에 충돌한 물체만을 반환하는 형식이다. 그 결과는 RayCastHit이라는 구조체로 반환된다.


CastAll

CastAll은 찾아낸 충돌체 전부를 반환한다. 찾아낸 결과는 찾아낸 오브젝트의 개수와 딱맞는 RayCastHit 배열로 반환된다.


CastNonAlloc

CastNonAlloc은 약간 독특한 방식인데 반환이 return을 통해서 이루어지는 것이 아니라, 매개변수를 통해서 이루어진다. 사용자가 RayCastHit의 배열을 만들어서 함수의 매개변수에 넣어주면, 함수가 그 배열에 찾아낸 오브젝트를 채워서 돌려주는 방식이다. 그렇기 때문에 찾아낸 오브젝트의 수가 배열의 수보다 적을 수도 많을 수도 있기 때문에 항상 주의해서 사용해야 한다.


각 형태와 방식에 따른 Cast의 사용법은 다음과 같다.




Cast 사용법

// Raycast

Vector3 startVect = Vector3.zero;
Vector3 direction = Vector3.forward;
float distance = 10f;

Ray ray = new Ray(startVect, direction);
RayCastHit hit = Physics.Raycast(ray, distance);



// RaycastAll

Vector3 startVect = Vector3.zero;
Vector3 direction = Vector3.forward;
float distance = 10f;

Ray ray = new Ray(startVect, direction);
RaycastHit[] hits = Physics.RaycastAll(ray, distance);

foreach(var hit in hits)
{
    Debug.Log(hit.collider.name);
}



// RaycastNonAlloc

Vector3 startVect = Vector3.zero;
Vector3 direction = Vector3.forward;
float distance = 10f;
Ray ray = new Ray(startVect, direction);
RaycastHit[] hits = new RaycastHit[10];     // 여기서 할당한 배열 수 이상은 가지고 오지 못한다.
int hitCount = Physics.RaycastNonAlloc(ray, hits, distance);

// foreach를 사용할 경우, 찾아낸 숫자가 배열 길이보다 작으면 에러가 발생한다.
for (int i = 0; i < hitCount; i++)
{
    Debug.Log(hits[i].collider.name);
}



// BoxCast

Vector3 boxCenter = Vector3.zero;
Vector3 boxHalfSize = new Vector3(1f, 1f, 1f);  // 캐스트할 박스 크기의 절반 크기. 이렇게 하면 가로 2 세로 2 높이 2의 크기의 공간을 캐스트한다.
Vector3 direction = Vector3.up;


bool isCollide = Physics.BoxCast(boxCenter, boxHalfSize, direction);    // 일반적으로 BoxCast는 충돌 여부만 반환한다.


RaycastHit hit;

Physics.BoxCast(boxCenter, boxHalfSize, direction, out hit);    // 충돌 결과에 대한 내용을 가져오려면 RaycastHit 구조체를 out 매개변수로 넣어주어야 한다.

Debug.Log(hit.collider.name);



// BoxCastAll

Vector3 boxCenter = Vector3.zero;
Vector3 boxHalfSize = new Vector3(1f, 1f, 1f);  // 캐스트할 박스 크기의 절반 크기. 이렇게 하면 가로 2 세로 2 높이 2의 크기의 공간을 캐스트한다.
Vector3 direction = Vector3.up;
RaycastHit[] hits = Physics.BoxCastAll(boxCenter, boxHalfSize, direction);    // BoxCastAll은 찾아낸 충돌체를 배열로 반환한다.

foreach (var hit in hits)
{
    Debug.Log(hit.collider.gameObject.name);
}



// BoxCastNonAlloc

Vector3 boxCenter = Vector3.zero;
Vector3 boxHalfSize = new Vector3(1f, 1f, 1f);  // 캐스트할 박스 크기의 절반 크기. 이렇게 하면 가로 2 세로 2 높이 2의 크기의 공간을 캐스트한다.
Vector3 direction = Vector3.up;
RaycastHit[] hits = new RaycastHit[10];
int hitCount = Physics.BoxCastNonAlloc(boxCenter, boxHalfSize, direction, hits);

for (int i = 0; i < hitCount; i++)
{
    Debug.Log(hits[i].collider.name);
}



// SphereCast

Vector3 origin = Vector3.zero;
Vector3 direction = Vector3.up;
Ray ray = new Ray(origin, direction);
float radius = 5f;
bool isCollide = Physics.SphereCast(ray, radius);

RaycastHit hit;
Physics.SphereCast(ray, radius, out hit);

Debug.Log(hit.collider.name);



// SphereCastAll

Vector3 origin = Vector3.zero;
Vector3 direction = Vector3.up;
Ray ray = new Ray(origin, direction);
float radius = 5f;
RaycastHit[] hits = Physics.SphereCastAll(ray, radius);

foreach (var hit in hits)
{
    Debug.Log(hit.collider.gameObject.name);
}



// SphereCastNonAlloc

Vector3 origin = Vector3.zero;
Vector3 direction = Vector3.up;
Ray ray = new Ray(origin, direction);
float radius = 5f;
RaycastHit[] hits = new RaycastHit[10];
int hitCount = Physics.SphereCastNonAlloc(ray, radius, hits);

for (int i = 0; i < hitCount; i++)
{
    Debug.Log(hits[i].collider.name);
}



// CapsuleCast

Vector3 v1 = new Vector3(0f, 0f, 0f);    // 캡슐 시작 부분의 구에 대한 중심점
Vector2 v2 = new Vector3(0f, 3f, 0f);    // 캡슐 끝 부분의 구에 대한 중심점
Vector3 direction = Vector3.up;
float radius = 5f;
bool isCollide = Physics.CapsuleCast(v1, v2, radius, direction);

RaycastHit hit;
Physics.CapsuleCast(v1, v2, radius, direction, out hit);

Debug.Log(hit.collider.name);



// CapsuleCastAll

Vector3 v1 = new Vector3(0f, 0f, 0f);
Vector2 v2 = new Vector3(0f, 3f, 0f);
Vector3 direction = Vector3.up;
float radius = 5f;
RaycastHit[] hits = Physics.CapsuleCastAll(v1, v2, radius, direction);

foreach (var hit in hits)
{
    Debug.Log(hit.collider.gameObject.name);
}



// CapsuleCastNonAlloc

Vector3 v1 = new Vector3(0f, 0f, 0f);
Vector2 v2 = new Vector3(0f, 3f, 0f);
Vector3 direction = Vector3.up;
float radius = 5f;
RaycastHit[] hits = new RaycastHit[10];
int hitCount = Physics.CapsuleCastNonAlloc(v1, v2, radius, direction, hits);

for (int i = 0; i < hitCount; i++)
{
    Debug.Log(hits[i].collider.name);
}


추가적인 이야기로는 캡슐 캐스트를 사용할 때, v1과 v2의 정의에 대해서 헷갈리는 경우가 발생할 수 있는데 그것은 캡슡의 상단과 하단에 가상의 구가 존재한다고 생각했을 때, 그 구의 중심 위치라고 생각하면 된다.

반응형
  1. 료용 2020.02.21 01:19 신고

    Vector3 startVect = Vector3.zero;
    Vector3 direction = Vector3.forward;
    float distance = 10f;

    Ray ray = new Ray(startVect, direction);
    RaycastHit hit = Physics.Raycast(ray, distance);
    이거 이렇게쓰니까
    RaycastHit hit = Physics.Raycast(ray, distance);
    이부분에서 암시적으로 bool형식을 unityEngine.RaycastHit 형식으로 변환할수없다고뜨는데 뭐가문제인거죠?


    • wergia 2020.02.23 00:07 신고

      꽤나 구버전 코드라 그런지 RayCast가 약간 바뀐것 같습니다.
      최신 버전 유니티에서는
      Physics.RayCast 함수가 충돌을 감지했는지에 대한 bool 값을 반환하고 감지 결과는 out 매개변수로 받도록 변경되었습니다.
      따라서 아래 코드와 같이 작성하시면 될것 같습니다.
      Vector3 startVect = Vector3.zero;
      Vector3 direction = Vector3.forward;
      float distance = 10f;

      Ray ray = new Ray(startVect, direction);
      RaycastHit hit;
      if(Physics.Raycast(ray, out hit, distance))
      {

      }

+ Recent posts