Ray와 Raycast

woollim·2024년 11월 10일
0

Unity개념

목록 보기
6/8

■ Ray

○ Ray의 기본 개념

  • 리적 충돌을 감지하거나 게임 오브젝트와의 상호작용을 구현할 때 자주 사용
  • Ray는 시작점(origin)방향(direction)을 가지고 있는 가상의 선입니다.(무한히 연장되는 직선)
  • Unity에서 Ray는 주로 Raycast와 함께 사용되며, Raycast는 특정 방향으로 발사된 Ray가 충돌하는 오브젝트를 감지합니다.

○ 사용 방법

  • Ray 생성 : Ray 클래스는 시작 위치와 방향을 정의하여 Ray를 만듭니다.
    예를 들어, Ray ray = new Ray(transform.position, transform.forward);는 현재 오브젝트 위치에서 정면 방향으로 Ray를 생성합니다.

  • Raycast : Physics.Raycast 함수를 사용해 Ray가 다른 Collider와 충돌하는지를 검사합니다.
    예를 들어, Physics.Raycast(ray, out RaycastHit hitInfo, maxDistance)와 같이 사용하면 지정한 거리 내에서 Ray와 충돌한 첫 번째 오브젝트에 대한 정보를 얻을 수 있습니다.

  • RaycastHit : Raycast가 충돌한 오브젝트의 정보를 포함합니다. hitInfo.collider를 통해 충돌한 오브젝트의 Collider에 접근할 수 있습니다.

○ Raycast를 통한 충돌 감지

  • Physics.Raycast 함수를 사용하여 특정 지점에서 특정 방향으로 Ray를 발사하고, 충돌한 오브젝트가 있는지 검사합니다.
  • Raycast는 주로 사격 게임에서 총알이 맞은 오브젝트를 감지하거나, RTS 게임에서 유닛을 선택하는 등 다양한 상황에서 유용하게 사용됩니다.

○ Ray 생성 및 사용 예시

using UnityEngine;

public class RayExample : MonoBehaviour
{
    void Update()
    {
        // 카메라에서 마우스 위치로 향하는 Ray 생성
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        // RaycastHit에 충돌 정보 저장
        RaycastHit hitInfo;

        // Raycast를 사용하여 충돌 감지
        if (Physics.Raycast(ray, out hitInfo, 100f)) // 100f는 Ray의 최대 거리
        {
            Debug.Log("충돌한 오브젝트: " + hitInfo.collider.name);
        }
    }
}
  • 이 예제에서는 카메라에서 마우스 위치로 향하는 Ray를 생성하고, Physics.Raycast를 통해 충돌을 감지합니다. hitInfo 변수에 충돌 정보가 저장되며, 충돌한 오브젝트의 이름을 출력합니다.
  • 100f는 Ray의 최대 거리로, 이 범위 안에서만 충돌 감지가 이루어집니다.

○ 레이어 마스크와 Raycast

  • Raycast에서 레이어 마스크를 사용하면 특정 레이어에 속한 오브젝트와의 충돌만 감지하도록 설정할 수 있습니다.
int layerMask = LayerMask.GetMask("Enemy");

if (Physics.Raycast(ray, out hitInfo, 100f, layerMask))
{
    Debug.Log("Enemy 레이어 오브젝트와 충돌!");
}
  • 위 코드에서 layerMask를 사용하여 "Enemy" 레이어에 속한 오브젝트만 감지합니다.

활용예시

  • FPS 게임에서 총알 경로 설정 : Ray를 통해 총알의 궤적을 계산하고, 충돌 지점에 파티클 효과를 생성할 수 있습니다.
  • RTS 게임에서 유닛 선택 : 마우스 클릭으로 Raycast를 발사하여 유닛을 선택하거나 명령을 내릴 수 있습니다.
  • 플랫폼 게임에서 시야 판별 : 캐릭터 시야 안에 특정 오브젝트가 있는지 확인할 수 있습니다.


■ Ray 문법

○ Ray 요약

  • Ray : 직선의 시작점(origin)과 방향(direction)
  • Ray ray = new Ray(transform.position, transform.forward); : 오브젝트
  • Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0)); : 카메라 중심
  • Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); : 마우스

○ new Ray

  • Ray ray = new Ray(Vector3 origin, Vector3 direction);
    • 시작점(origin)과 방향(direction) 두 가지 매개변수를 지정
    • 매개변수
      • origin : Ray가 시작하는 위치, Vector3 타입이며, 보통 월드 좌표계를 기준으로 지정
      • direction : Ray가 향하는 방향, Vector3 타입
        방향은 정규화(normalized)되어 있지 않아도 되지만, 방향 벡터는 크기보다는 방향이 중요하기 때문에 일반적으로 정규화된 벡터를 사용하는 것이 좋음
    • 예시
      Vector3 startPoint = new Vector3(0, 1, 0); // 시작 위치
      Vector3 direction = Vector3.up;            // 위쪽 방향
      Ray ray = new Ray(startPoint, direction);
      // Ray는 (0, 1, 0) 지점에서 시작하여 위쪽(Vector3.up, 즉 (0, 1, 0))으로 뻗어나감

○ ViewportPointToRay

  • Unity에서 화면상의 특정 점을 3D 세계 좌표로 변환하는 데 사용되는 함수
  • 함수는 주로 2D 화면 공간에서 3D 월드로의 광선을 쏘거나, 카메라가 바라보는 방향에 따라 특정 점을 추적하는 데 사용
  • 이 함수는 화면의 Viewport 좌표를 입력으로 받아, 해당 좌표에서 시작하는 3D 공간상의 광선(ray)을 반환
    • Viewport 좌표는 화면의 (0, 0)부터 (1, 1) 사이의 정규화된 좌표계를 의미합니다. 예를 들어, (0.5, 0.5)는 화면의 중앙을 의미
  • 매개변수
    • Vector3 viewportPoint : 2D 화면의 Viewport 좌표.
      • 이 값은 (0, 0)부터 (1, 1)까지의 값을 가질 수 있습니다.
      • 예를 들어, 화면의 왼쪽 하단은 (0, 0)이고, 오른쪽 상단은 (1, 1)입니다.
  • 반환값
    • 이 함수는 Ray 객체를 반환.
    • Ray는 시작점과 방향으로 구성되며, 주어진 Viewport 점에서 카메라를 통해 나가는 광선의 정보를 제공
  • 예시
using UnityEngine;
public class Example : MonoBehaviour
{
    void Update()
    {
        // 마우스 위치를 Viewport 좌표로 변환
        Vector3 viewportPoint = Camera.main.ScreenToViewportPoint(Input.mousePosition);
        
        // Viewport 좌표를 기준으로 광선을 생성
        Ray ray = Camera.main.ViewportPointToRay(viewportPoint);
        
        // 광선이 맞는 객체가 있는지 확인
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit))
        {
            Debug.Log("Hit object: " + hit.collider.name);
        }
    }
}
- ScreenToViewportPoint : 마우스의 화면 좌표를 Viewport 좌표로 변환합니다.
- ViewportPointToRay : 변환된 Viewport 좌표에서 카메라를 향해 광선을 쏩니다.
- Physics.Raycast : 생성된 광선이 무엇인가와 충돌하는지 확인합니다.

○ ScreenPointToRay

  • Ray ray = Camera.main.ScreenPointToRay(screenPosition);
    • 특정 화면 좌표(스크린 포인트)로부터 발사된 Ray를 생성하는 메서드
    • 주로 마우스 클릭 지점이나 터치 위치에서 Ray를 발사해 3D 공간의 특정 지점이나 오브젝트와 상호작용할 때 사용
    • 매개변수
      • screenPosition : Vector3 타입으로, 화면의 좌표를 의미.
        보통 (x, y, 0)의 형태로, x와 y는 화면상의 픽셀 위치를 나타냄. 0은 기본적으로 화면의 z 좌표
        화면 좌표는 화면의 왼쪽 아래가 (0, 0)이고,
        오른쪽 위가 (Screen.width, Screen.height)로 설정
        예를 들어, Input.mousePosition을 사용하여 현재 마우스 위치를 screenPosition으로 전달할 수 있습니다.
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    • 반환값
      • ScreenPointToRay는 Ray를 반환.
      • 이 Ray는 화면에서 지정된 지점에서 카메라가 바라보는 방향으로 생성된 Ray
    • 예시
    using UnityEngine;
    public class RaycastExample : MonoBehaviour
    {
    	void Update()
      {
          if (Input.GetMouseButtonDown(0)) // 마우스 왼쪽 버튼 클릭 시
          {
              // 마우스 위치로부터 Ray 생성
              Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
              RaycastHit hitInfo;
              
              // Raycast를 사용해 충돌 감지
              if (Physics.Raycast(ray, out hitInfo))
              {
              	Debug.Log("충돌한 오브젝트: " + hitInfo.collider.name);
              }
          }
       }
    }

○ Physics.Raycast

  • bool hit = Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction);

  • 매개변수

    • origin (필수) : Ray의 시작 위치 (Vector3). 예를 들어, 캐릭터나 카메라의 위치에서 시작할 수 있습니다.
    • direction (필수) : Ray가 향하는 방향 (Vector3). 예를 들어, 앞쪽을 향하는 transform.forward 벡터 등을 사용할 수 있습니다.
    • hitInfo (선택) : 충돌된 오브젝트에 대한 정보를 담는 RaycastHit 구조체입니다. out 키워드를 사용하여 값을 전달받으며, 충돌 지점의 좌표, 충돌한 오브젝트의 콜라이더 등의 정보를 포함합니다.
    • maxDistance (선택) : Ray의 최대 거리 (float). Ray가 이 거리를 넘어가면 충돌하지 않은 것으로 간주됩니다.
    • layerMask (선택) : 특정 레이어에 속한 오브젝트만 충돌 대상으로 지정하는 레이어 마스크(int). 레이어 마스크를 사용하면 Ray가 특정 레이어에 속한 오브젝트에만 반응하도록 할 수 있습니다.
    • queryTriggerInteraction (선택) : 트리거 콜라이더와의 충돌 여부를 설정하는 QueryTriggerInteraction 열거형입니다. 기본값은 QueryTriggerInteraction.UseGlobal로, 물리 설정에 따라 트리거와 충돌할지 결정됩니다.
      Ignore 또는 Collide로 설정하여 트리거 충돌 여부를 지정할 수 있습니다.
  • 반환값

    • bool 타입으로, 충돌이 발생하면 true, 발생하지 않으면 false를 반환합니다.
  • 예시

    1. 기본적인 Raycast 사용
      origin과 direction을 지정하여 Ray를 발사하고, 충돌한 오브젝트가 있는지 검사하는 간단한 예제입니다.

      void Update()
      {
        Vector3 origin = transform.position;       // Ray 시작 위치
        Vector3 direction = transform.forward;     // Ray 방향
        if (Physics.Raycast(origin, direction, out RaycastHit hitInfo))
        {
            Debug.Log("충돌한 오브젝트: " + hitInfo.collider.name);
        }
      }
      
    2. 거리를 지정하여 Raycast 사용
      maxDistance를 설정하여 지정된 거리 내에서만 충돌을 감지할 수 있습니다

      float maxDistance = 10f;
      if (Physics.Raycast(origin, direction, out RaycastHit hitInfo, maxDistance))
      {
      	Debug.Log("충돌한 오브젝트: " + hitInfo.collider.name);
      }
      
    3. 레이어 마스크를 사용하여 특정 레이어만 감지
      layerMask를 사용하면 특정 레이어에만 Ray를 감지하도록 할 수 있습니다.

      int layerMask = LayerMask.GetMask("Enemy");
      if (Physics.Raycast(origin, direction, out RaycastHit hitInfo, maxDistance, layerMask))
      {
      	Debug.Log("Enemy 레이어에 속한 오브젝트와 충돌!");
      }
      
    4. 트리거 콜라이더와의 충돌 설정
      QueryTriggerInteraction을 사용하여 트리거 콜라이더와의 충돌 여부를 결정할 수 있습니다.

      if (Physics.Raycast(origin, direction, out RaycastHit hitInfo, maxDistance, layerMask, QueryTriggerInteraction.Collide))
      {
        Debug.Log("트리거 콜라이더와도 충돌 감지");
      }

○ RaycastHit

RaycastHit 구조체는 Ray가 충돌한 위치에 대한 상세 정보를 제공합니다. 주요 속성으로는 다음이 있습니다

  • collider (Collider) : 충돌한 오브젝트의 콜라이더.
    • 이를 통해 충돌한 오브젝트의 정보를 가져올 수 있습니다.
    • hitInfo.collider.name을 사용해 충돌한 오브젝트의 이름을 출력
  • point (Vector3) : Ray가 충돌한 월드 좌표.
    • hitInfo.point을 사용해 충돌 위치를 확인하고 그 위치에 파티클을 생성하거나 다른 오브젝트를 이동시킬 수 있음
  • normal (Vector3) : 충돌한 표면의 법선 벡터.
    • 충돌한 표면이 바라보는 방향을 나타내며, 물리적 반응을 계산하거나 시각적 효과를 줄 때 유용
    • hitInfo.normal을 사용해 해당 위치에서 반사 방향을 계산할 수 있음
  • distance (float) : Ray의 시작점에서 충돌 지점까지의 거리.
    • hitInfo.distance는 시작점에서 충돌 지점까지의 거리를 나타내어, 거리 조건에 따라 추가 로직을 실행하는 데 사용할 수 있음
  • transform (Transform) : 충돌한 오브젝트의 Transform을 반환.
    • 이를 통해 충돌한 오브젝트의 위치, 회전, 스케일 등에 접근할 수 있음
    • hitInfo.transform.position으로 충돌한 오브젝트의 위치에 접근할 수 있음
  • rigidbody (Rigidbody) : 충돌한 오브젝트에 연결된 Rigidbody 컴포넌트를 반환.
    • 충돌한 오브젝트가 물리적 속성을 가지고 있다면, 이를 통해 속도나 회전 등 물리적 제어가 가능
    • hitInfo.rigidbody가 null이 아닌 경우, AddForce를 사용해 물리적 힘을 가할 수 있음
  • textureCoord (Vector2) : 충돌 지점의 텍스처 좌표(UV 좌표)를 반환
    • 이 좌표는 오브젝트 표면의 텍스처상 충돌한 지점을 나타내며, 충돌 지점에 특정한 시각 효과를 줄 때 유용
    • hitInfo.textureCoord를 사용해 텍스처 위의 특정 좌표를 기준으로 마커를 표시할 수 있음
  • lightmapCoord (Vector2) : 충돌 지점의 라이트맵 UV 좌표를 반환
    • 라이트맵은 오브젝트의 조명을 저장하는 맵으로, 충돌 지점에서의 조명 정보를 활용할 때 유용
  • 예시
using UnityEngine;

public class RaycastExample : MonoBehaviour
{
    public ParticleSystem hitEffect;

    void Update()
    {
        if (Input.GetMouseButtonDown(0)) // 마우스 왼쪽 버튼 클릭 시
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out RaycastHit hitInfo, 100f))
            {
                // 충돌 지점에 파티클 생성
                Instantiate(hitEffect, hitInfo.point, Quaternion.LookRotation(hitInfo.normal));
                
                // 충돌 정보 출력
                Debug.Log("충돌한 오브젝트 이름: " + hitInfo.collider.name);
                Debug.Log("충돌 지점까지의 거리: " + hitInfo.distance);
            }
        }
    }
}
  • RaycastHit의 point와 normal을 사용해 충돌 지점에 파티클을 생성합니다.
  • collider.name을 통해 충돌한 오브젝트의 이름을 출력하고, distance를 통해 Ray의 시작점에서 충돌 지점까지의 거리를 출력합니다.


■ 헛갈리기 쉬운점

○ ScreenPointToRay와 ViewportPointToRay 차이

둘 다 Unity에서 화면상의 점을 3D 공간의 광선(Ray)으로 변환할 때 사용되는 함수이지만, 화면 좌표계의 기준이 다르다는 점에서 차이가 있습니다.

  1. ScreenPointToRay

    • 좌표계 : Screen 좌표계를 사용합니다.
    • 좌표 범위 : Screen 좌표는 화면 픽셀 단위의 좌표입니다. (0, 0)은 화면의 왼쪽 하단, (Screen.width, Screen.height)는 화면의 오른쪽 상단을 나타냅니다.
    • 용도 : 일반적으로 마우스 위치나 터치 입력을 기반으로 3D 공간에서 광선을 쏘고자 할 때 사용됩니다.
    • 예시
      Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    • 사용 사례 : 마우스 클릭 위치를 3D 공간으로 변환하는 경우.
  2. ViewportPointToRay

    • 좌표계 : Viewport 좌표계를 사용합니다.
    • 좌표 범위 : Viewport 좌표는 정규화된 값으로, (0, 0)은 화면의 왼쪽 하단, (1, 1)은 화면의 오른쪽 상단을 나타냅니다.
    • 용도 : 화면의 특정 비율 위치를 기준으로 3D 공간에서 광선을 쏘고자 할 때 사용됩니다. 좌표가 정규화되어 있어 비율에 기반한 위치를 처리할 때 유리합니다.
    • 예시
      Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
      // 화면의 중앙에서 시작하는 광선
    • 사용 사례 : 화면의 중앙이나 특정 비율 위치에서 광선을 쏘고자 할 때.
  3. 요약

    • ScreenPointToRay는 픽셀 단위 좌표를 사용하여, 주로 마우스나 터치 위치와 같은 절대적인 화면 위치에 적합합니다.
    • ViewportPointToRay는 정규화된 비율 좌표를 사용하여, 주로 화면 내의 비율적 위치에 따른 광선을 쏠 때 유용합니다.
  4. Screen과 Viewport

    • 모두 화면의 좌표를 나타내지만, 기준이 서로 다릅니다.

    • Screen (스크린 좌표)

      • 정의 : Screen 좌표는 화면의 픽셀 단위 좌표입니다.
      • 범위 : (0, 0)부터 (Screen.width, Screen.height)까지의 값으로 나타납니다.
        -> (0, 0)은 화면의 왼쪽 하단입니다.
        -> (Screen.width, Screen.height)는 화면의 오른쪽 상단입니다.
      • 용도 : 마우스 위치나 터치 입력 등 화면의 절대적인 위치를 나타낼 때 사용됩니다. 각 기기의 해상도에 따라 값이 달라질 수 있습니다.
      • 예시 : 해상도가 1920x1080인 화면에서는 (960, 540)이 중앙이 됩니다.
    • Viewport (뷰포트 좌표)

      • 정의 : Viewport 좌표는 화면의 비율을 기반으로 한 정규화 좌표입니다.
      • 범위 : (0, 0)부터 (1, 1)까지의 값으로 나타납니다.
        -> (0, 0)은 화면의 왼쪽 하단입니다.
        -> (1, 1)은 화면의 오른쪽 상단입니다.
        -> (0.5, 0.5)는 화면의 중앙을 의미합니다.
      • 용도 : 화면 비율에 따른 상대적인 위치를 다룰 때 사용됩니다. 이 값은 해상도에 영향을 받지 않으므로 해상도와 관계없이 비율에 기반한 좌표를 사용할 수 있습니다.
      • 예시 : 화면 중앙은 항상 (0.5, 0.5)로 표현됩니다.
    • 차이점 요약

      • Screen 좌표는 픽셀 단위의 절대 위치로, 해상도에 따라 달라질 수 있습니다.
      • Viewport 좌표는 정규화된 비율 좌표로, 해상도에 영향을 받지 않습니다.
    • 활용 예

      • Screen 좌표는 마우스 입력, UI 터치 위치 등에 적합합니다.
      • Viewport 좌표는 화면 비율을 기준으로 특정 지점을 나타내거나, 다양한 해상도에서 동일한 비율의 위치를 추적할 때 유용합니다.

0개의 댓글