유니티 오브젝트 관통 - yuniti obeujegteu gwantong



✔ Unity - LayerMask, Ray, Raycast, RaycastAll

마우스 포인트에서 특정 오브젝트 찾기(Raycast 활용)



◆ Ray 란 어떤 지점의 위치와 방향에서 일직선상의 선을 그어봅니다.

유니티 오브젝트 관통 - yuniti obeujegteu gwantong

선이 지나가면서 충돌되는 오브젝트들을 판단하는 것이 Raycast 입니다.

일반적으로 많이 사용되는 Raycast, RaycastAll에 대해서 설명 드립니다.

Raycast : 선이 지나가면서 처음 충돌한 오브젝트를 반환.

RaycastAll : 선이 지나가면서 충돌하는 모든 오브젝트를 반환.

Raycast 사용법 예제 : 현재 마우스 위치에서 충돌되는 오브젝트가 있다면 그 위치로 변경.

//마우스 위치에서 레이
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
        
if (Physics.Raycast(ray, out hit))
{
    if (hit != null)
    {
        transform.postion = hit.point;
    } 
}

RaycastAll 사용법 예제 : 충돌되는 모든 오브젝트를 그중에 특정하나만 뽑아서 위치 변경

//마우스 위치에서 레이
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

//충돌된 모든 오브젝트를 불러오고 원하는 조건을 넣음.
RaycastHit hit = Physics.RaycastAll(ray).OrderBy(h => h.distance)	
                        .Where(h => h.transform.tag != "player").FirstOrDefault();
                       
if (hit.transform != null)
{
	transform.position = hit.point;
}               

RaycastAll은 약간 응용하여 예제를 넣어봤습니다. 

-> 현재 마우스 위치에서 충돌되는 모든 오브젝트를 찾는다.

-> 거리가 가까운순으로 정렬한다.

-> 특정 태그는 제외시킨다.

-> 첫번째 오브젝트에서 충돌된 위치를 찾는다.

◆ Raycast 응용 : Ray의 선상에 있는 오브젝트들 중에서 특정 오브젝트만 추출.

1. 특정 레이어의 오브젝트는 관통하고 다른 레이어 오브젝트를 찾기

현재 마우스 위치에서 내 앞에 있는 오브젝트가 바라보고 있는 오브젝트를 찾고싶은 경우가 있다.

예를 들어 플레이어가 있고 플레이어가 바라보고 있는 오브젝트가 무엇인지 찾아낼때 사용한다.

기법은 Ray를 인식할때 플레이어를 제외하는 방법이다.

먼저 Unity에서 레이어를 추가해주고 설정해주어야한다.

유니티 오브젝트 관통 - yuniti obeujegteu gwantong
<예시화면>

Layers에서 원하는 위치에 레이어 이름을 넣어준다.

유니티 오브젝트 관통 - yuniti obeujegteu gwantong

다시 Player 오브젝트를 클릭하고 Layer에서 추가한 레이어를 선택한다.

유니티 오브젝트 관통 - yuniti obeujegteu gwantong

그리고 스크립트를 생성하여 아래 부분을 넣으면 플레이어 뒤에 있는 물체를 알아낼 수 있다.

//플레이어 레이어만 제외함.
int layerMask = (-1) - (1 << LayerMask.NameToLayer("Player"));

Ray ray = camera.ScreenPointToRay(Input.mousePosition);
if ( Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, layerMask))
{
     Debug.Log(hit.collider.gameObject);
}

2. 특정 레이어의 오브젝트만 찾고 나머지 레이어는 관통하기

아래 코드는 위와는 반대로 특정레이어만 인식하는 방식이다.

//플레이어 레이어만 체크함
int layerMask = 1 << LayerMask.NameToLayer("Player");

Ray ray = camera.ScreenPointToRay(Input.mousePosition);
if ( Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, layerMask))
{
     Debug.Log(hit.collider.gameObject);
}

3. RaycastAll로 일직선상 모든 오브젝트를 찾아 특정 레이어만 뽑아내기

또 한가지 방법은 RaycastAll 사용하여 특정 레이어를 찾는 방법이다.

이 방법은 위에서 설명했던 RaycastAll 부분에서 조건만 변경하면 됩니다.

하지만 RaycastAll은 오브젝트가 많으면 많을수록 속도가 느려지므로 사용하지 않는것을 추천합니다.

감사합니다.

리지드바디(Rigidbody)


Unity Docs
  • https://docs.unity3d.com/kr/2019.4/Manual/class-Rigidbody.html

‘강체’로 번역할 수 있다.

유니티 엔진에서 제공하는 기본 컴포넌트이다.

리지드바디가 존재하는 게임 오브젝트는 물리 엔진의 영향을 받는다.

현실감 있는 물리 시뮬레이션이 가능해지며,

힘(Force)속도(Velocity)의 영향을 받는다.

3D에서는 Rigidbody, 2D에서는 Rigidbody2D 컴포넌트를 사용한다.

프로젝트 종류에 따라 잘 구분해서 사용해야 한다.

콜라이더(Collider)


Unity Docs
  • https://docs.unity3d.com/kr/2019.4/Manual/Physics3DReference.html

‘충돌체’로 번역할 수 있다.

유니티 엔진에서 기본적으로 제공하는 콜라이더는

박스(Box), 구(Sphere), 캡슐(Capsule), 메시(Mesh) 등이 있다.

콜라이더마다 고유의 모양과 영역이 있으며, 서로의 영역이 겹칠 경우 충돌이 발생한다.

Is Trigger 설정에 체크하지 않을 경우, 충돌 시 접촉에 의한 물리 시뮬레이션이 발생하며

Is Trigger 설정에 체크할 경우, 충돌 시 부딪히지 않고 ‘겹침 상태’만 검사하게 된다.

2D에서는 BoxCollider2D처럼 이름 뒤에 2D가 붙는 컴포넌트를 사용해야 한다.

정적 콜라이더와 동적 콜라이더


리지드바디를 갖고 있지 않는 게임 오브젝트의 콜라이더를 정적 콜라이더(Static Collider),

리지드바디를 갖고 있는 게임 오브젝트의 콜라이더를 동적 콜라이더(Dynamic Collider)라고 지칭한다.

정적 콜라이더는 게임 내에서 위치, 회전을 변경시키면 안된다.

물리 시뮬레이션에 성능, 정확도 면에서 악영향을 줄 수 있다.

동적 콜라이더는 자유롭게 위치, 회전을 변경해도 된다.

대신 트랜스폼을 직접 조작하면 안되고, 리지드바디를 통해 변경해야 한다.

충돌(Collision)


Unity Docs
  • OnCollisionEnter
  • OnCollisionStay
  • OnCollisionExit

  • OnCollisionEnter2D
  • OnCollisionStay2D
  • OnCollisionExit2D

콜라이더 컴포넌트를 가진 두 게임오브젝트가 부딪힐 경우 발생한다.

두 게임 오브젝트 중 적어도 하나는 반드시 리지드바디를 갖고 있어야 하고,

두 콜라이더는 모두 Is Trigger가 체크되지 않아야 한다.

동적 콜라이더는 충돌 발생 시 부딪혀 튕겨나가게 된다.

리지드바디를 가진 게임 오브젝트에 MonoBehaviour를 상속받는 클래스를 만들어서 컴포넌트로 넣고,

해당 클래스 내에 void OnCollisionEnter(Collision) 메소드를 작성하면

충돌이 발생하는 순간 OnCollisionEnter(Collision) 메소드가 호출된다.

마찬가지로 충돌이 유지되는 동안에는 OnCollisionStay(Collision),

충돌이 끝나는 순간에는 OnCollisionExit(Collision) 메소드가 호출된다.

2D의 경우, OnCollisionEnter2D(Collision2D)처럼

이름 뒤에 2D가 붙은 메소드를 작성해야만 한다.

트리거(Trigger) 충돌


Unity Docs
  • OnTriggerEnter
  • OnTriggerStay
  • OnTriggerExit

  • OnTriggerEnter2D
  • OnTriggerStay2D
  • OnTriggerExit2D

두 게임 오브젝트가 모두 콜라이더 컴포넌트를 갖고 있고

두 콜라이더 중 하나 이상의 콜라이더에 Is Trigger가 체크되어 있으며

두 게임 오브젝트 중 하나 이상의 게임 오브젝트에 리지드바디 컴포넌트가 존재할 경우 발생한다.

트리거 충돌이 발생해도 게임오브젝트는 튕겨나가지 않고, 그저 관통한다.

트리거가 아닌 충돌과 마찬가지로,

트리거 충돌 발생 시 리지드바디를 가진 게임오브젝트에 메시지가 전달된다.

충돌 시작 시 OnTriggerEnter(Collider),

충돌이 유지되는 동안 OnTriggerStay(Collider),

충돌 종료 시 OnTriggerExit(Collider) 메소드가 호출된다.

MonoBehaviour를 상속받는 클래스에 해당 메소드들을 작성하여

리지드바디를 가진 게임오브젝트에 컴포넌트로 넣어주면

트리거 충돌 발생 시 각각의 충돌 단계에 해당하는 메소드가 호출된다.

2D의 경우, OnTriggerEnter2D(Collider2D)처럼

이름 뒤에 2D가 붙은 메소드를 작성해야만 한다.

트랜스폼과 리지드바디


트랜스폼이 존재하는 기본적인 월드를 게임 월드(Game World)라고 할 때,

물리 엔진에 의해 리지드바디가 제어되는 월드를 물리 월드(Physics World)라고 할 수 있다.

리지드바디가 존재하지 않는 게임 오브젝트의 트랜스폼은 게임 월드에만 존재하고,

리지드바디가 존재하는 게임 오브젝트의 트랜스폼은 게임 월드, 물리 월드 양측에 존재한다.

쉽게 말해,

물리 월드의 트랜스폼은 리지드바디의 내부에 감춰진 트랜스폼이라고 이해하면 된다.

Rigidbody.position, Rigidbody.rotation을 통해 참조할 수 있다.

리지드바디를 가진 게임 오브젝트의 경우,

게임 월드의 트랜스폼은 물리 월드의 트랜스폼에 영향을 받는다.

반대로 물리 월드의 트랜스폼이 게임 월드의 트랜스폼에 영향을 받기도 하지만,

통제의 우선권은 물리 월드에 있다.

따라서 리지드바디가 존재하는 게임 오브젝트는 트랜스폼을 직접 조작하면 안되고

반드시 리지드바디를 조작하여 이동, 회전시켜야 한다.

그리고 물리 엔진의 갱신은 FixedUpdate()가 끝날 때마다 이루어진다.

따라서 리지드바디를 가진 게임 오브젝트는 Update()가 아니라

FixedUpdate()를 통해 조작해야만 한다.

키네마틱(Kinematic) 리지드바디


리지드바디에는 Is Kinematic 설정이 있다.

키네마틱으로 설정될 경우,

해당 게임오브젝트는 리지드바디가 존재함에도 불구하고 물리 시뮬레이션이 발생하지 않는다.

다시 말해, 다른 리지드바디에 부딪혀도 절대 튕겨나가지 않는다.

그리고 물리 엔진의 영향을 아예 받지 않아서,

리지드바디를 통해 힘을 줘도(AddForce()) 움직이지 않는다.

하지만 콜라이더를 갖고 있다면 다른 동적 콜라이더에는 충돌을 발생시킨다.

키네마틱 리지드바디 특징

  • 리지드바디의 힘, 중력, 속도 등의 영향을 받지 않는다.
  • 리지드바디의 position, rotation을 조작하여 이동, 회전할 수 있다.
  • 트랜스폼을 통해 직접 position, rotation을 조작해도 된다.

  • 다른 정적 콜라이더와 부딪힐 때 트리거 충돌 메시지를 수신한다.
  • 다른 동적 콜라이더와 부딪힐 때 충돌, 트리거 충돌 메시지를 수신한다.
  • 다른 동적 콜라이더와 부딪힐 때, 해당 동적 콜라이더 입장에서는 정적 콜라이더로 취급된다.

키네마틱 리지드바디의 사용처

키네마틱 리지드바디는 ‘움직여도 되는 정적 콜라이더’라고 생각하면 된다.

다른 물체로부터 물리적으로 영향을 받을 필요는 없지만,

반대로 플레이어 캐릭터와 같은 강체에 물리적으로 영향을 줄 수 있는 환경 요소에 사용할 수 있다.

예를 들면 여닫이문, 움직이는 발판 등이 있다.

References


  • https://docs.unity3d.com/kr/2019.3/Manual/CollidersOverview.html
  • https://docs.unity3d.com/kr/530/ScriptReference/MonoBehaviour.html