Raycast | 정확한 물리처리에 사용

해당 게시물은 유니티에서 레이캐스트를 어떤 식으로 활용할 수 있는지 정리한 게시물입니다.

레이캐스트는 정확한 물리처리를 요구할 때 사용합니다. 2D 플랫포머 게임에서 바닥을 체크할 때, FPS 게임에서의 총알 데미지, 등등 활용되는 방식은 여러가지 입니다. 지금부터 어떻게 사용되는지 간단히 알아보겠습니다.

레이캐스트 간단 설명


어떤 위치에서 광선을 발사하여 그 광선에 닿는 물체가 있는지 검사하는 방식. RaycastHit 을 사용하여 여러가지 값도 쉽게 가져올 수 있어서 많이 사용하는 방법입니다.

유니티 스크립팅 API


https://docs.unity3d.com/kr/530/ScriptReference/Physics.Raycast.html

Physics.Raycast(vector3 시작점,vector3 방향,float 최대거리,layerMask 레이어마스크);

보통 레이캐스트는 위에서 처럼 사용됩니다. 하지만 유니티 스크립팅을 보면 훨씬 다양하게 사용할 수 있으니 한 번은 읽어보는 게 좋습니다.

눈앞의 사물을 감지하고 옮길 수 있는 기능을 만들어보자


지금부터 설명할 방법은 1인칭 시점 3D 캐릭터 기준으로 설명합니다. 1인칭 시점 캐릭터는 메인 카메라를 가지고 있어 직접 움직이면서 게임 세상을 볼 수 있는 특징을 가지고 있습니다.

캐릭터가 가지고 있는 메인 카메라를 활용하여 직접 움직이면서 Raycast 로 물건을 옮기는 기능을 만들어 보겠습니다.

1. Raycast 를 통해 광선을 쏘고 게임 오브젝트를 감지하자


// 카메라를 받을 변수
private Camera playerCam;
// 광선거리
public float distance = 100f;

void Start()
{
    // 스타트 함수를 통해 카메라를 받습니다
    playerCam = Camera.main;
}

void Update()
{
    // 본문 설명 참조
    Vector3 rayOrigin = playerCam.
            ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0));
    Vector3 rayDir = playerCam.transform.forward;
    
    // Scene 창에서만 보이는 선을 발사합니다
    // 레이케스트 광선 범위 확인용입니다
    Debug.DrawRay(rayOrigin, rayDir*distance, Color.red);
    
    // 마우스 왼쪽 버튼을 통해 레이캐스트 발동
    if(Input.GetMouseButtonDown(0))
    {
        if(Physics.Raycast(rayOrigin, rayDir, distance))
        {
            Debug.Log("광선에 맞았다!!");
        }
    }
}

스크립트를 만들어서 코드를 작성합시다. 위 코드는 메인 카메라를 통해 광선을 발사하여 게임 오브젝트를 감지하는 코드입니다.

위 코드에서 눈여겨 봐야하는 부분은 playerCam.ViewportToWorldPoint 입니다.

Raycast는 광선이 발사되는 원점 Vector3 좌표를 요구합니다. 위 코드는 메인 카메라가 Raycast를 발사되게 작성되었고 광선이 발사되는 위치는 메인 카메라의 좌표가 됩니다.

ViewportToWorldPoint 는 유니티 카메라의 내장된 함수인데 카메라의 위치를 찍어줍니다. Vector3(0.5f, 0.5f, 0) 은 화면 정중앙을 의미합니다.

위 스크립트를 가지고 있는 1인칭 시점의 캐릭터는 정상적으로 Raycast를 통해 오브젝트를 인식(물리처리)할 것입니다. 하지만 문제가 있습니다. 원하는 오브젝트만 물리처리 하는 것이 아니라 모든 오브젝트를 물리처리하죠.

2. 레이어를 활용하여 원하는 오브젝트만 Raycast 에 처리되게 하자


유니티의 게임 오브젝트들은 인스펙터 창에서 Layer(레이어) 설정을 할 수 있습니다.

Raycast 에 물리처리되게 하고싶은 게임 오브젝트들을 적당한 레이어를 선택해 주시면 됩니다. 그리고 당연히 스크립트를 수정해야 합니다.

private Camera playerCam;

public float distance = 100f;
// 레이어 선택을 위한 변수
public LayerMask whatIsTarget;

void Start()
{
    playerCam = Camera.main;
}

void Update()
{
    Vector3 rayOrigin = playerCam.
            ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0));
    Vector3 rayDir = playerCam.transform.forward;
    
    Debug.DrawRay(rayOrigin, rayDir*distance, Color.red);
    
    if(Input.GetMouseButtonDown(0))
    {
        // LayerMask 변수인 whatIsTarget 을 추가하자
        if(Physics.Raycast(rayOrigin, rayDir, distance, whatIsTarget))
        {
            Debug.Log("광선에 맞았다!!");
        }
    }
}

LayerMask 타입의 변수는 현재 생성되있는 레이어를 인스펙터에서 선택할 수 있습니다. 물론 퍼블릭으로 선언해야 인스펙터에서 선택할 수 있겠죠.

저는 Raycast에 물리처리 되게 하고싶은 게임 오브젝트의 Layer를 위 사진과 같은 레이어로 설정했었습니다. 해당 레이어를 인스펙터에서 LayerMask 변수에 대입시키면 됩니다.

코드에서 레이캐스트 함수에 레이어마스크 파라미터를 추가했기 때문에 레이캐스트는 설정했었던 레이어에 포함돼있는 오브젝트만 물리처리하게 됩니다.

3. RaycastHit 을 통해 광선에 닿은 게임 오브젝트의 정보를 가져와 게임 오브젝트를 옮기자


private Camera playerCam;

public float distance = 100f;

public LayerMask whatIsTarget;

// 이동시킬 게임 오브젝트 트랜스폼 변수
private Transform moveTarget;

// 해당 오브젝트의 거리
private float targetDistance;

void Start()
{
    playerCam = Camera.main;
}

void Update()
{
    Vector3 rayOrigin = playerCam.
            ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0));
    Vector3 rayDir = playerCam.transform.forward;
    
    Debug.DrawRay(rayOrigin, rayDir*distance, Color.red);
    
    // 레이캐스트힛 변수 선언
    RaycastHit hit;
    
    if(Input.GetMouseButtonDown(0))
    {
        
        if(Physics.Raycast(rayOrigin, rayDir, out hit, distance, whatIsTarget))
        {
            // out 키워드는 입력으로 들어온 값이 내부에서
            // 어떠한 값이 생겨서 빠져나간다는 의미
            GameObject hitTarget = hit.collider.gameObject;
            
            Debug.Log("광선에 맞았다!!");
            
            // 레이캐스트힛 으로 받아온 오브젝트 대입
            moveTarget = hitTarget.transform;
            // 게임 오브젝트와의 거리
            targetDistance = hit.distance;
        }
    }
    
    // 마우스 버튼 때면 moveTarget 변수 비우기
    if(Input.GetMouseButtonUp(0))
    {
        moveTarget = null;
    }
    
    // moveTarget 에 값이 있을 시 오브젝트 이동
    if(moveTarget != null)
    {
        moveTarget.position = rayOrigin + rayDir * targetDistance;
    }
}

레이캐스트힛 으로 선언한 변수가 Update 함수안에 있습니다. Raycast 의 파라미터를 통해 RaycastHit 변수에 값을 대입시켰습니다.

RaycastHit 변수는 많은 정보를 담고 있습니다. Raycast 에 충돌처리된 오브젝트의 정보를 RaycastHit 으로 받아올 수 있는데 게임 오브젝트를 이동시키는 핵심이 RaycastHit 으로 받아온 게임 오브젝트의 값을 활용하는 것입니다.

그리고 Raycast 파라미터 중에 out 키워드가 있는데, out 키워드를 통해 RaycastHit 변수에 값이 대입되는 것입니다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다