Unity

[Unity] 유니티 레이캐스트(Raycast) 기초 : 이해 및 활용

MayQ 2025. 6. 8. 20:29

Unity Raycast 기초 : 이해 및 활용

유니티(Unity)를 활용한 게임 개발에서 레이캐스트(Raycast)는 매우 중요한 기능 중 하나입니다. 레이캐스트느 가상의 광선을 쏘아 충돌하는 오브젝트를 감지하는 방법으로, 시야 검사, 오브젝트 선택, 충돌 감지 등 다양한 상황에서 필수적으로 사용됩니다. 이 글에서는 레이캐스트의 기본 개념을 이해하고, Unity에서 이를 어떻게 효과적으로 활용할 수 있는지 자세히 알아보겠습니다.

레이캐스트(Raycast)란?

레이캐스트란, 말 그대로 가상의 광선을 쏘아 그 광선이 충돌하는 오브젝트를 감지하는 방법입니다. 이는 Unity의 물리 시스템에서 중요한 기능으로, 게임 내에서 캐릭터가 시야 내에 있는 오브젝트를 인식하거나, 특정 위치를 클릭했을 때 해당 위치의 오브젝트를 선택하는 등 다양한 상황에 사용됩니다. 레이캐스트의 가장 기본적인 형태는 시작점과 방향을 정의하고, 이 광선이 충돌하는 오브젝트와의 충돌 여부를 확인하는 것입니다.

Unity에서는 Physics.Raycast 함수를 사용하여 레이캐스팅을 구현할 수 있습니다. 이 함수는 다양한 오버라이드(Overload) 옵션을 제공하여 상황에 맞게 사용할 수 있습니다. 가장 기본적인 형태의 레이캐스트는 시작점과 방향을 입력받아, 광선이 일정 거리 내에서 충돌하는 오브젝트가 있는지를 검사합니다. 예를 들어, 플레이어 캐릭터의 앞쪽으로 레이캐스트를 발사해, 일정 거리 내에 적 캐릭터가 있는지 확인하는 식입니다.

레이캐스 함수는 다양한 오버라이드가 제공되며, 각 오버라이드는 서로 다른 매개변수를 받습니다. 예를 들어, 특정 레이어의 오브젝트만 감지하고자 할 때는 레이어 마스크(LayerMask)를 사용하여 레이캐스트를 필터링할 수 있습니다. 이렇게 하면 불필요한 충돌 검사를 줄일 수 있어 성능 최적화에 도움이 됩니다.

bool hit = Physics.Raycast(ray, out hit, 100f, layerMask);

위 코드에서는 layerMask 변수를 사용하여 특정 레이어에 속한 오브젝트만 검사합니다. 이 방법은 복잡한 씬에서 성능을 유지하면서 필요한 오브젝트만 정확하게 감지할 때 유용합니다.


Physics.Raycast 오버라이드 함수 간단 설명

Physics.Raycast 반환값

Physics.Raycast 함수는 모두 동일한 반환값을 가집니다:

  • 반환값: bool - 레이캐스트가 충돌한 경우 true를 반환하고, 그렇지 않은 경우 false를 반환합니다.

Physics.Raycast 함수의 매개변수

Physics.Raycast 함수는 다양한 오버로드를 통해 레이캐스트를 수행할 수 있습니다. 아래는 모든 함수에서 사용되는 매개변수들을 정리한 목록입니다.

  1. Ray ray:
    • 레이캐스트를 수행할 시작점과 방향을 포함하는 Ray 구조체.
  2. Vector3 origin:
    • 레이캐스트의 시작점 좌표를 나타내는 Vector3 구조체.
  3. Vector3 direction:
    • 레이캐스트의 방향을 나타내는 Vector3 구조체.
  4. float maxDistance:
    • 레이캐스트의 최대 거리를 설정하는 값. 이 거리를 초과하는 범위에서는 충돌을 감지하지 않습니다.
  5. out RaycastHit hitInfo:
    • 충돌된 물체에 대한 정보를 반환하는 RaycastHit 구조체. 충돌이 발생하면 이 구조체에 정보가 저장됩니다.
  6. int layerMask:
    • 충돌을 감지할 레이어를 지정하는 비트 마스크. 이 값을 통해 특정 레이어에서만 충돌을 감지할 수 있습니다.
  7. QueryTriggerInteraction queryTriggerInteraction:
    • 트리거와의 상호작용 방식을 지정하는 열거형. 트리거와의 충돌을 무시할지, 감지할지를 설정할 수 있습니다.

1. Physics.Raycast(Ray ray)

Physics.Raycast(Ray ray)
  • 설명:
    • Ray 구조체를 입력받아 레이캐스트를 수행하고, 충돌이 발생했는지 여부를 반환합니다. 충돌 정보는 반환되지 않습니다.

2. Physics.Raycast(Ray ray, float maxDistance)

Physics.Raycast(Ray ray, float maxDistance)
  • 설명:
    • Ray와 최대 거리를 입력받아 레이캐스트를 수행합니다. 최대 거리 이내에서 충돌이 발생했는지 여부를 반환합니다.

3. Physics.Raycast(Ray ray, out RaycastHit hitInfo)

Physics.Raycast(Ray ray, out RaycastHit hitInfo)
  • 설명:
    • Ray와 충돌 정보를 출력하는 RaycastHit를 입력받아 레이캐스트를 수행합니다. 충돌이 발생하면 hitInfo에 충돌 정보를 저장합니다.

4. Physics.Raycast(Vector3 origin, Vector3 direction)

Physics.Raycast(Vector3 origin, Vector3 direction)
  • 설명:
    • 시작점과 방향을 직접 지정하여 레이캐스트를 수행하고, 충돌 여부를 반환합니다.

5. Physics.Raycast(Ray ray, out RaycastHit hitInfo, int layerMask)

Physics.Raycast(Ray ray, out RaycastHit hitInfo, int layerMask)
  • 설명:
    • Ray, RaycastHit, 그리고 레이어 마스크를 입력받아 레이캐스트를 수행합니다. 특정 레이어에서만 충돌을 감지하며, 충돌이 발생하면 hitInfo에 충돌 정보를 저장합니다.

6. Physics.Raycast(Ray ray, float maxDistance, int layerMask)

Physics.Raycast(Ray ray, float maxDistance, int layerMask)
  • 설명:
    • Ray, 최대 거리, 레이어 마스크를 입력받아 레이캐스트를 수행합니다. 특정 레이어에서만 최대 거리 이내에서 충돌을 감지합니다.

7. Physics.Raycast(Vector3 origin, Vector3 direction, float maxDistance)

Physics.Raycast(Vector3 origin, Vector3 direction, float maxDistance)
  • 설명:
    • 시작점, 방향, 최대 거리를 직접 지정하여 레이캐스트를 수행하고, 충돌 여부를 반환합니다.

8. Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo)

Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo)
  • 설명:
    • 시작점과 방향을 지정하고, 충돌 정보를 출력하는 RaycastHit를 입력받아 레이캐스트를 수행합니다. 충돌이 발생하면 hitInfo에 충돌 정보를 저장합니다.

9. Physics.Raycast(Ray ray, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction)

Physics.Raycast(Ray ray, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction)
  • 설명:
    • Ray, 최대 거리, 레이어 마스크, 트리거 상호작용 방식을 입력받아 레이캐스트를 수행합니다. 트리거와의 상호작용 방식을 지정할 수 있습니다.

10. Physics.Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask)

Physics.Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask)
  • 설명:
    • 시작점, 방향, 최대 거리, 특정 레이어에서만 충돌을 감지하도록 설정할 수 있습니다.

11. Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance)

Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance)
  • 설명:
    • 시작점, 방향, 충돌 정보 저장, 최대 거리를 지정할 수 있는 레이캐스트 함수입니다. 충돌이 발생하면 hitInfo에 충돌 정보를 저장합니다.

12. Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, int layerMask)

Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, int layerMask)
  • 설명:
    • 시작점, 방향, 충돌 정보 저장, 그리고 특정 레이어에서만 충돌을 감지하도록 설정할 수 있는 레이캐스트 함수입니다.

13. Physics.Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction)

Physics.Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction)
  • 설명:
    • 시작점, 방향, 최대 거리, 특정 레이어, 그리고 트리거와의 상호작용 방식을 지정하여 레이캐스트를 수행합니다.

14. Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask)

Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask)
  • 설명:
    • 시작점, 방향, 최대 거리, 특정 레이어에서의 충돌 감지와 충돌 정보를 반환하는 레이캐스트 함수입니다.

15. Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction)

Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction)
  • 설명:
    • 모든 옵션을 포함하여 레이캐스트를 설정할 수 있는 포괄적인 함수입니다. 충돌 정보를 반환하며, 특정 레이어 및 트리거와의 상호작용 방식을 지정할 수 있습니다.

16. Physics.Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction)

Physics.Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction)
  • 설명:
    • Ray, 충돌 정보, 최대 거리, 레이어 마스크, 트리거 상호작용 방식을 입력받아 레이캐스트를 수행합니다. 특정 레이어 및 트리거와의 상호작용 방식을 지정할 수 있습니다.

비교적 자주 사용되는 Raycast 함수

Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo)

  • 이유: 이 함수는 충돌된 오브젝트에 대한 정보를 반환할 수 있어, 게임 개발에서 매우 중요한 역할을 합니다. 주로 상호작용이나 공격 등에서 충돌된 오브젝트의 세부 정보를 확인하기 위해 자주 사용됩니다.

Physics.Raycast(Ray ray, out RaycastHit hitInfo)

  • 이유: Ray 구조체를 사용해 레이캐스트를 수행하며, 충돌된 오브젝트에 대한 정보를 얻을 수 있습니다. 특히, 카메라에서의 클릭 처리나 특정 방향으로의 레이캐스트에 자주 사용됩니다.

Physics.Raycast(Vector3 origin, Vector3 direction)

  • 이유: 이 함수는 단순히 충돌 여부만을 확인할 때 사용되며, 간단한 충돌 감지에 매우 유용합니다. 복잡한 추가 정보가 필요하지 않을 때 자주 활용됩니다.

Physics.Raycast(Ray ray)

  • 이유: Ray 구조체를 사용해 간단히 충돌 여부를 확인할 수 있으며, 간단한 충돌 감지 용도로 많이 사용됩니다. 스크린 레이캐스트나 특정 방향으로의 간단한 광선 쏘기에 자주 사용됩니다.

Physics.Raycast(Vector3 origin, Vector3 direction, float maxDistance)

  • 이유: 이 함수는 충돌을 감지할 수 있는 최대 거리를 제한할 수 있어, 일정 거리 내에서만 충돌을 감지해야 하는 상황에서 유용합니다. 거리 제한이 중요한 게임 플레이에서 자주 사용됩니다.

레이캐스트 작동 주요 조건

1. 게임 오브젝트에 콜라이더 (Collider) 필요

Raycasting은 게임 오브젝트를 감지할 때 콜라이더가 반드시 필요합니다. 콜라이더가 없는 경우, 레이(Ray)가 물체와의 충돌을 감지하지 못합니다. 이 점은 흔히 간과되는 부분이므로, 타겟 오브젝트에 콜라이더가 올바르게 설정되어 있는지 반드시 확인해야 합니다.

2. 올바른 레이어 마스크 (Layer Mask) 사용

레이캐스트에서 사용하는 레이어 마스크가 타겟 오브젝트의 레이어와 일치해야 합니다. 만약 레이어 마스크가 맞지 않으면, 레이가 해당 오브젝트를 감지하지 못할 수 있습니다. Unity에서는 레이어 마스크를 통해 원하는 오브젝트만 필터링할 수 있지만, 타겟 오브젝트의 레이어가 반드시 마스크에 포함되어야 한다는 점을 기억해야 합니다​.

3. 방향과 거리 파라미터

레이의 방향과 길이(거리)는 레이가 타겟 오브젝트와 교차하기 위해 매우 중요합니다. 레이의 방향은 발사 원점과 타겟의 위치에 따라 정확하게 계산되어야 하며, 최대 거리 파라미터는 레이의 예상 범위에 맞게 설정되어야 합니다. 잘못된 값이 설정되면 레이가 타겟을 초과하거나 도달하지 못할 수 있습니다​.


레이캐스트 간단 예시

예제 스크립트 구현

아래는 마우스 클릭 시 Ray를 발사하여 충돌한 물체의 이름을 출력하는 Unity 스크립트입니다.

using UnityEngine;

public class RaycastExample : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        // 마우스 왼쪽 버튼이 클릭되었는지 확인합니다.
        if (Input.GetMouseButtonDown(0))
        {
            // 카메라에서 마우스 포인터 위치를 기준으로 Ray를 발사합니다.
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            // Ray가 충돌한 물체의 정보를 담을 RaycastHit 구조체를 선언합니다.
            RaycastHit hit;

            // Ray를 발사하고 물체와의 충돌 여부를 확인합니다.
            if (Physics.Raycast(ray, out hit, 100f))
            {
                // Ray가 충돌한 물체의 이름을 디버그 콘솔에 출력합니다.
                Debug.Log("Hit Object: " + hit.collider.name);
            }
        }
    }
}

스크립트 설명

이 스크립트는 Unity에서 마우스 클릭 시 Ray를 발사하여, Ray가 충돌한 물체의 이름을 출력하는 간단한 예제를 보여줍니다. 각 부분의 역할은 다음과 같습니다.

  • 마우스 클릭 확인:
    Input.GetMouseButtonDown(0) 은 마우스 왼쪽 버튼이 눌렸을 때만 true를 반환합니다. 이 조건문은 사용자가 마우스를 클릭할 때마다 Raycast가 실행되도록 합니다.
  • Ray 생성:
    Camera.main.ScreenPointToRay(Input.mousePosition) 는 카메라에서 마우스 포인터가 있는 화면 위치를 기준으로 Ray를 생성합니다. 이 Ray는 카메라의 시점에서 마우스 포인터가 가리키는 방향으로 발사되며, 클릭한 위치에서부터 앞으로 나아가는 가상의 레이저 빔을 의미합니다.
  • RaycastHit 구조체:
    RaycastHit hit; 은 Ray가 충돌한 물체에 대한 정보를 저장합니다. 이 구조체에는 충돌 지점, 충돌된 물체의 콜라이더, 충돌된 지점의 법선 벡터 등 다양한 정보가 포함될 수 있습니다.
  • Ray 발사 및 충돌 체크:
    Physics.Raycast(ray, out hit, 100f) 는 Ray를 발사하고, 물체와의 충돌 여부를 확인합니다. 100f는 Ray가 최대 100 유닛 거리까지 발사된다는 의미입니다. 이 함수는 Ray가 어떤 물체와 충돌하면 true를 반환하고, hit 변수에 충돌 정보를 저장합니다.
  • 충돌한 물체 정보 출력:
    Debug.Log("Hit Object: " + hit.collider.name); 은 Ray가 충돌한 물체의 이름을 Unity 콘솔에 출력합니다. 이는 개발자가 어떤 물체와 충돌했는지 시각적으로 확인할 수 있도록 돕습니다.

Unity Editor 적용 방법

  1. Unity에서 새 C# 스크립트를 생성하고 이름을 "RaycastExample"로 지정합니다.

  1. 위의 코드를 복사하여 스크립트 파일에 붙여넣습니다.

  1. Hierarchy 창에서 우클릭 후 'Create Empty' 버튼을 눌러 빈 오브젝트를 생성하고 이름을 RaycastController 로 생성한 후, RaycastExample 스크립트를 RaycastController 오브젝트에 부착합니다.

  1. Hierarchy 창에서 우클릭 후 3D Object에서 큐브, 스페어, 캡슐 오브젝트를 생성하여 Game Scene에 적당히 배치합니다.

  1. 플레이 모드에서 마우스 왼쪽 버튼을 클릭하여 Ray가 충돌하는 물체의 이름이 Unity 콘솔에 출력되는지 확인합니다.


레이캐스트 응용 방법

레이캐스팅은 단순히 오브젝트를 감지하는 데 그치지 않고, 게임 내 다양한 상황에서 응용될 수 있습니다. 예를 들어, NPC의 시야를 구현할 때 레이캐스팅을 사용하면, NPC가 일정 범위 내에서 플레이어 캐릭터를 인식하고 행동을 취하도록 할 수 있습니다. 또한, 총기 발사 시 레이캐스팅을 사용하여 탄환이 적 캐릭터에 맞았는지를 판별할 수 있습니다.

잡한 환경에서는 여러 개의 레이캐스팅을 동시에 실행하거나, 특정 조건에 따라 레이캐스팅의 결과를 필터링하는 등의 고급 기술이 필요합니다. 예를 들어, 레이캐스팅을 사용하여 플레이어가 클릭한 지점의 오브젝트를 감지하는 것 외에도, UI 요소와의 상호작용을 처리할 수도 있습니다. Unity의 Graphic Raycaster 컴포넌트는 UI 상에서의 클릭 이벤트를 감지하는 데 사용되며, 이 역시 레이캐스팅의 원리를 바탕으로 합니다.

 

Map Objects 만들기

  • Ground - Plane Object
  • Walls - Cube Object

Player Object 및 Components, Script

  • Capsule Object 생성
  • Rigidbody Component 부착
    • Constraints - Freeze Rotation에서 X, Z 체크
  • PlayerMovement Script 부착

using UnityEngine;  // Unity 엔진의 기본 네임스페이스를 사용

public class RandomMovementWithChase : MonoBehaviour
{
    public float moveSpeed = 5f;  // 오브젝트의 이동 속도
    public float changeDirectionTime = 2f;  // 랜덤하게 방향을 변경하는 시간 간격
    public float rotationSpeed = 5f;  // 오브젝트가 회전하는 속도
    public float positionCheckInterval = 0.1f;  // 위치를 확인하는 시간 간격 (초 단위)
    public float positionThreshold = 0.01f;  // 오브젝트가 이동했는지 판단하는 최소 거리
    public float detectionRange = 10f;  // Raycast로 적을 탐지할 범위
    public int numberOfRays = 8;  // 다양한 방향으로 발사할 레이의 개수

    private float directionTimer = 0f;  // 방향 변경을 위한 타이머
    private float positionTimer = 0f;  // 위치 변경 확인을 위한 타이머
    private Vector3 moveDirection;  // 오브젝트의 현재 이동 방향
    private Vector3 lastPosition;  // 오브젝트의 마지막 위치
    private bool isChasing = false;  // 오브젝트가 적을 추적 중인지 여부를 나타내는 플래그
    private Transform targetEnemy;  // 추적 중인 적의 Transform

    void Start()
    {
        ChooseRandomDirection();  // 게임 시작 시 랜덤하게 방향을 선택
        lastPosition = transform.position;  // 오브젝트의 초기 위치를 기록
    }

    void Update()
    {
        if (!isChasing)  // 오브젝트가 적을 추적 중이 아닐 때만 랜덤 이동 및 위치 확인
        {
            directionTimer += Time.deltaTime;  // 경과한 시간을 방향 변경 타이머에 더함
            positionTimer += Time.deltaTime;  // 경과한 시간을 위치 확인 타이머에 더함

            if (directionTimer >= changeDirectionTime)  // 방향 변경 시간이 지났을 경우
            {
                ChooseRandomDirection();  // 새로운 랜덤 방향 선택
                directionTimer = 0f;  // 타이머 리셋
            }

            if (positionTimer >= positionCheckInterval)  // 위치 확인 간격이 지났을 경우
            {
                CheckPosition();  // 오브젝트의 위치가 이동했는지 확인
                positionTimer = 0f;  // 타이머 리셋
            }

            DetectEnemy();  // 적이 있는지 레이캐스트로 탐지
        }

        MoveAndRotate();  // 오브젝트를 이동시키고, 이동 방향으로 회전시킴
    }

    void ChooseRandomDirection()
    {
        int randomDirection = Random.Range(0, 4);  // 0~3 중 랜덤한 정수를 선택하여 방향을 결정

        switch (randomDirection)  // 선택된 정수에 따라 방향을 설정
        {
            case 0:
                moveDirection = Vector3.forward;  // 앞으로 이동
                break;
            case 1:
                moveDirection = Vector3.back;  // 뒤로 이동
                break;
            case 2:
                moveDirection = Vector3.left;  // 왼쪽으로 이동
                break;
            case 3:
                moveDirection = Vector3.right;  // 오른쪽으로 이동
                break;
        }
    }

    void MoveAndRotate()
    {
        if (isChasing && targetEnemy != null)  // 적을 추적 중이고, 적이 유효한 경우
        {
            moveDirection = (targetEnemy.position - transform.position).normalized;  // 적을 향한 방향을 계산하여 이동 방향으로 설정
        }

        transform.Translate(moveDirection * moveSpeed * Time.deltaTime, Space.World);  // 현재 이동 방향으로 오브젝트를 이동시킴

        Quaternion targetRotation = Quaternion.LookRotation(moveDirection);  // 현재 이동 방향을 향한 회전을 계산
        transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);  // 오브젝트를 부드럽게 회전시킴
    }

    void CheckPosition()
    {
        float distanceMoved = Vector3.Distance(transform.position, lastPosition);  // 현재 위치와 마지막 위치 사이의 거리를 계산

        if (distanceMoved < positionThreshold)  // 이동한 거리가 임계값보다 작을 경우
        {
            ChooseRandomDirection();  // 새로운 랜덤 방향을 선택
        }

        lastPosition = transform.position;  // 현재 위치를 마지막 위치로 업데이트
    }

    void DetectEnemy()
    {
        float angleStep = 360f / numberOfRays;  // 레이 사이의 각도를 계산

        for (int i = 0; i < numberOfRays; i++)  // 지정된 수의 레이를 발사
        {
            float angle = i * angleStep;  // 각 레이의 각도를 계산
            Vector3 direction = Quaternion.Euler(0, angle, 0) * transform.forward;  // 현재 오브젝트의 앞 방향을 기준으로 회전된 방향을 계산
            Ray ray = new Ray(transform.position, direction);  // 레이를 생성
            RaycastHit hit;  // 레이캐스트 히트 정보를 저장할 변수

            Debug.DrawRay(transform.position, direction * detectionRange, Color.red);  // 레이를 시각적으로 표시 (디버깅용)

            if (Physics.Raycast(ray, out hit, detectionRange))  // 레이캐스트를 수행하여 무언가를 맞췄는지 확인
            {
                if (hit.collider.gameObject.name.Contains("Enemy"))  // 맞춘 오브젝트의 이름에 "Enemy"가 포함되어 있는지 확인
                {
                    Debug.Log("Enemy detected: " + hit.collider.gameObject.name);  // 적이 탐지되면 콘솔에 메시지를 출력
                    isChasing = true;  // 적을 추적 중임을 표시
                    targetEnemy = hit.transform;  // 적의 Transform을 저장
                    moveDirection = (targetEnemy.position - transform.position).normalized;  // 적을 향한 방향으로 이동 방향 설정
                    break;  // 첫 번째 적을 찾으면 루프를 종료하여 더 이상 레이캐스트를 하지 않음
                }
            }
        }
    }

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.name.Contains("Enemy"))  // 충돌한 오브젝트의 이름에 "Enemy"가 포함되어 있는지 확인
        {
            Destroy(collision.gameObject);  // 적 오브젝트를 제거
            isChasing = false;  // 추적 상태를 해제
            ChooseRandomDirection();  // 적을 제거한 후 새로운 랜덤 방향을 선택하여 다시 랜덤 이동 시작
        }
    }
}
  1. 레이캐스트를 사용한 적 탐지:
    • DetectEnemy 메서드는 여러 방향으로 레이캐스트를 발사하여 오브젝트 주변의 적을 탐지합니다.
    • 설정된 개수(numberOfRays)만큼 다양한 각도로 레이를 발사하여 넓은 범위를 탐색합니다.
    • 각 레이는 detectionRange 범위 내에서 "Enemy"라는 이름을 포함한 오브젝트와 충돌할 경우, 해당 오브젝트를 감지합니다.
    • 적이 감지되면 콘솔에 탐지된 적의 이름을 출력하고, 오브젝트가 그 적을 추적하도록 설정합니다.
  2. 레이캐스트 결과에 따른 동작:
    • 적이 레이캐스트에 의해 감지되면, 랜덤 이동이 중지되고 오브젝트는 감지된 적을 향해 이동 방향을 조정합니다.
    • 오브젝트는 레이캐스트로 탐지된 적을 향해 부드럽게 회전하며 추적을 시작합니다.
    • 적과의 충돌이 발생하면, 적 오브젝트를 제거하고 추적 상태를 해제한 후, 다시 랜덤 이동 모드로 전환됩니다.

이 스크립트는 레이캐스트를 활용하여 오브젝트 주변의 적을 탐지하고, 탐지된 적을 추적하는 기능을 구현합니다. 레이캐스트를 통해 적이 감지되면 오브젝트는 자동으로 그 방향으로 이동하여 적을 제거하려고 합니다.

Enemy Object 및 Components, Script

  • Capsule Object 생성
  • Capsule Collider - Radius : 0.6으로 변경
  • Rigidbody Component 부착
    • Constraints - Freeze Rotation에서 X, Z 체크
  • RandomMovement Script 부착

using UnityEngine;  // Unity 엔진의 핵심 클래스를 포함하는 네임스페이스

public class RandomMovement : MonoBehaviour
{
    public float moveSpeed = 3f;  // 오브젝트의 이동 속도
    public float changeDirectionTime = 2f;  // 오브젝트가 새로운 방향으로 변경되는 시간 간격
    public float rotationSpeed = 5f;  // 오브젝트가 회전하는 속도
    public float positionCheckInterval = 0.1f;  // 오브젝트의 위치를 확인하는 시간 간격 (초 단위)
    public float positionThreshold = 0.01f;  // 위치 변화가 얼마나 작아야 방향을 변경할지 결정하는 임계값

    private float directionTimer = 0f;  // 방향 변경 타이머를 초기화
    private float positionTimer = 0f;  // 위치 확인 타이머를 초기화
    private Vector3 moveDirection;  // 오브젝트가 이동하는 방향을 저장할 변수
    private Vector3 lastPosition;  // 마지막으로 기록된 오브젝트의 위치를 저장할 변수

    void Start()
    {
        ChooseRandomDirection();  // 처음 시작할 때 임의의 방향을 선택
        lastPosition = transform.position;  // 현재 오브젝트의 위치를 기록
    }

    void Update()
    {
        directionTimer += Time.deltaTime;  // 매 프레임마다 방향 변경 타이머 증가
        positionTimer += Time.deltaTime;  // 매 프레임마다 위치 확인 타이머 증가

        if (directionTimer >= changeDirectionTime)  // 만약 방향 변경 시간이 지났다면
        {
            ChooseRandomDirection();  // 새로운 임의의 방향을 선택
            directionTimer = 0f;  // 방향 변경 타이머를 초기화
        }

        if (positionTimer >= positionCheckInterval)  // 만약 위치 확인 시간이 지났다면
        {
            CheckPosition();  // 현재 위치와 마지막 위치를 비교하여 방향을 변경할지 결정
            positionTimer = 0f;  // 위치 확인 타이머를 초기화
        }

        MoveAndRotate();  // 오브젝트를 이동하고 회전시킴
    }

    void ChooseRandomDirection()
    {
        int randomDirection = Random.Range(0, 4);  // 0부터 3 사이의 임의의 정수를 생성 (방향 선택)

        switch (randomDirection)
        {
            case 0:
                moveDirection = Vector3.forward;  // 0인 경우 앞으로 이동
                break;
            case 1:
                moveDirection = Vector3.back;  // 1인 경우 뒤로 이동
                break;
            case 2:
                moveDirection = Vector3.left;  // 2인 경우 왼쪽으로 이동
                break;
            case 3:
                moveDirection = Vector3.right;  // 3인 경우 오른쪽으로 이동
                break;
        }
    }

    void MoveAndRotate()
    {
        // 현재 위치에서 이동 방향으로 오브젝트를 이동
        transform.Translate(moveDirection * moveSpeed * Time.deltaTime, Space.World);

        // 오브젝트를 이동하는 방향으로 회전시키기 위해 목표 회전을 설정
        Quaternion targetRotation = Quaternion.LookRotation(moveDirection);
        // 오브젝트를 현재 회전에서 목표 회전으로 부드럽게 회전
        transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
    }

    void CheckPosition()
    {
        float distanceMoved = Vector3.Distance(transform.position, lastPosition);  // 현재 위치와 마지막 위치 사이의 거리 계산

        if (distanceMoved < positionThreshold)  // 만약 이동한 거리가 임계값보다 작다면
        {
            ChooseRandomDirection();  // 새로운 임의의 방향을 선택
        }

        lastPosition = transform.position;  // 마지막 위치를 현재 위치로 업데이트
    }
}

주요 포인트:

  • ChooseRandomDirection() 메서드는 임의의 방향을 선택하여 오브젝트의 이동 방향을 설정합니다.
  • MoveAndRotate() 메서드는 설정된 방향으로 오브젝트를 이동시키고, 해당 방향으로 부드럽게 회전시킵니다.
  • CheckPosition() 메서드는 오브젝트의 위치 변화를 모니터링하고, 변화가 거의 없을 경우 새로운 방향을 선택하도록 합니다.

Enemy 캐릭터 복사 후 여러 개 임의로 배치하기

이제 Play button을 누르면 player 캐릭터의 8방향 raycast에 적 오브젝트가 탐지되면 플레이어 캐릭터가 적 캐릭터를 따라가 섬멸하는 것을 볼 수 있습니다.


레이캐스트 활용 예시

  1. 충돌 감지:
    • Raycast는 게임에서 충돌이나 상호작용을 감지하는 데 매우 유용합니다. 예를 들어, 1인칭 슈팅 게임에서 총알이 목표물에 맞았는지 확인하기 위해 Raycast를 사용합니다. 총구에서 발사된 방향으로 광선을 쏘아 목표물이나 다른 물체와 교차하는지 계산하고, 교차할 경우 피해를 주거나 탄환의 충격 효과를 표시하는 등의 반응을 유발합니다​.
  2. 시야 및 가시성 체크:
    • Raycast는 또한 객체가 플레이어나 인공지능(AI)에 의해 보이는지 여부를 확인하는 데 사용됩니다. 예를 들어, 잠입 게임이나 AI 행동 프로그래밍에서 플레이어 캐릭터가 적에게 감지되는지 여부를 정확하게 계산하는 데 중요합니다​.
  3. 환경과의 상호작용:
    • 오픈 월드 게임이나 상호작용 가능한 환경에서, Raycast는 플레이어가 상호작용할 수 있는 객체를 감지하는 데 사용됩니다. 예를 들어, 플레이어가 문을 바라볼 때 Raycast를 통해 문이 범위 내에 있는지 확인하고, 그렇다면 문을 열 수 있도록 합니다. 이는 종종 상호작용할 수 있는 객체를 강조하는 효과와 결합됩니다​.

레이캐스트 디버깅

Raycasting을 디버깅할 때, Debug.DrawRay와 OnDrawGizmosSelected 함수는 매우 유용한 도구입니다. 이들은 플레이 모드 중이거나 에디터에서 시각적으로 레이캐스팅의 경로를 확인할 수 있도록 도와줍니다.

Debug.DrawRay를 사용한 디버깅

Debug.DrawRay 함수는 레이캐스트의 경로를 시각적으로 확인할 수 있게 해줍니다. 이 함수는 레이의 시작 위치와 방향을 받아 해당 방향으로 선을 그립니다. 예를 들어, 아래 코드는 손의 위치에서 전방으로 레이를 쏘고, 충돌이 발생하면 빨간색 선을, 충돌이 없으면 파란색 선을 그립니다.

private void FixedUpdate()
{
    int layerMask = 1 << 8;

    RaycastHit hit;
    if (Physics.Raycast(hand.transform.position, hand.transform.forward, out hit, Mathf.Infinity, layerMask))
    {
        Debug.Log("Raycast hit");
        Debug.DrawRay(hand.transform.position, hand.transform.forward * hit.distance, Color.red, 5f);
    }
    else
    {
        Debug.DrawRay(hand.transform.position, hand.transform.forward * 10, Color.blue);
    }
}

이 코드는 특히 디버깅 중 레이캐스팅이 정확하게 이루어지고 있는지, 예상된 충돌이 발생하는지를 확인하는 데 매우 유용합니다.

OnDrawGizmosSelected를 사용한 디버깅

OnDrawGizmosSelected 함수는 플레이 모드에 들어가지 않고도 에디터에서 레이캐스팅의 경로를 시각화할 수 있게 해줍니다. 이 함수는 특정 객체가 선택되었을 때만 실행되며, Gizmos를 사용해 시각적으로 디버깅 정보를 제공합니다. 예를 들어, 아래 코드는 객체의 전방에 5 단위 길이의 빨간색 레이를 그립니다.

void OnDrawGizmosSelected()
{
    Gizmos.color = Color.red;
    Vector3 direction = transform.TransformDirection(Vector3.forward) * 5;
    Gizmos.DrawRay(transform.position, direction);
}

이 방법은 플레이 모드에 들어가지 않고도 레이캐스트의 정확한 방향과 거리를 확인할 수 있어, 더 빠르고 편리하게 디버깅할 수 있습니다.

이러한 도구들을 사용하면 레이캐스팅이 예상대로 작동하는지, 그리고 필요한 수정이 어디에 필요한지를 빠르게 파악할 수 있습니다​.


고급 Raycasting: RaycastAll

때로는 레이의 경로를 따라 첫 번째로 닿은 객체만이 아닌 모든 객체를 감지해야 할 때가 있습니다. Unity에서는 이를 위해 Physics.RaycastAll을 제공합니다

RaycastHit[] hits = Physics.RaycastAll(origin, direction, maxDistance);
foreach (RaycastHit hit in hits)
{
    Debug.Log("Hit: " + hit.collider.name);
    // 예제: 모든 닿은 객체에 힘을 가할 수 있습니다
    hit.collider.GetComponent<Rigidbody>().AddForce(-hit.normal * 10f);
}

이 스크립트는 레이가 교차하는 모든 객체를 확인하고, 그 객체들의 이름을 로그에 출력합니다. 또한 각 객체에 힘을 가하는 등의 효과를 적용할 수 있습니다.


레이캐스트 사용 시 주의 사항

레이캐스팅은 자주 사용되면 성능에 영향을 미칠 수 있으므로, 성능 최적화에도 신경 써야 합니다. 불필요한 레이캐스팅 호출을 줄이고, 레이어 마스크나 거리를 적절하게 설정하여 최소한의 계산으로 원하는 결과를 얻도록 설계하는 것이 중요합니다.

성능 최적화 방법

  1. 레이캐스팅 호출 최소화:
    • 레이캐스트 호출을 지나치게 자주 하면 성능에 부정적인 영향을 줄 수 있습니다. 특히 복잡한 씬이나 많은 객체가 있는 경우, 여러 번의 레이캐스팅이 메인 스레드를 차단하여 게임 성능을 저하시킬 수 있습니다. 이러한 문제를 피하기 위해 레이캐스트 호출 빈도를 줄이거나, 비동기 방식으로 처리하는 것이 권장됩니다​.
  2. 레이어 마스크 사용:
    • 레이캐스팅 시 특정 레이어에만 적용되도록 레이어 마스크를 사용하는 것은 불필요한 충돌 계산을 피하고 성능을 최적화하는 중요한 방법입니다. 이를 통해 불필요한 객체와의 충돌 검사를 방지할 수 있습니다​.
  3. 거리를 적절하게 설정:
    • 레이캐스트의 최대 거리를 설정하여, 필요 이상의 영역을 검사하지 않도록 하는 것이 성능 최적화에 중요합니다. 최대 거리를 설정하지 않으면 무한한 거리를 검사하게 되어 성능이 저하될 수 있습니다​.
  4. 비동기 레이캐스팅 사용:
    • 복잡한 씬이나 많은 객체와의 상호작용이 필요한 경우, 레이캐스트 명령을 비동기로 실행하여 메인 스레드의 블로킹을 피하는 것이 좋습니다. 이를 통해 성능 저하를 방지할 수 있습니다​.
  5. 물리 시뮬레이션 최적화:
    • 물리 엔진의 고정 시간 스텝(Fixed Timestep)을 조정하여 물리 계산 빈도를 줄이는 것도 성능 최적화의 중요한 부분입니다. 특히 저성능 장치에서는 이러한 최적화가 필수적입니다​.

이와 같은 최적화 방법들을 통해 레이캐스팅이 성능에 미치는 영향을 최소화하고, 더 원활한 게임 플레이를 유지할 수 있습니다.


결론

결론적으로, 레이캐스팅은 Unity에서 매우 강력하고 유용한 기능입니다. 기본적인 오브젝트 감지부터 복잡한 게임 로직 구현까지 폭넓게 활용될 수 있으며, 이를 잘 이해하고 응용하면 더욱 흥미롭고 복잡한 게임을 개발할 수 있습니다. 디버깅과 최적화 방법을 통해 레이캐스팅의 성능을 높이고, 게임의 전체적인 퀄리티를 향상시킬 수 있습니다. 이 글에서 설명한 기본 개념과 다양한 예제를 통해, 여러분도 자신만의 게임 개발 프로젝트에서 레이캐스팅을 활용해 보길 바랍니다.