흔한 덕후의 잡동사니
Raycast 와 그 사용법에 대하여 본문
이 글은 Unity API에 대한 설명을 기반으로 따로 찾은 내용과 함께 정리한 내용이다. 보다 자세한 내용을 보고 싶다면 글 아래 참조 쪽에 공식 API 사이트 링크를 참고하자.
Raycast란?
Raycast는 3D 공간에서 특정 방향으로 광선(Ray)을 쏘아 충돌을 감지하는 기능이다. 주로 오브젝트 간의 충돌 감지, 시야 확인, 마우스 포인터로 오브젝트 선택 등에 사용된다.
1. Raycast 사용 방법
1.1 좌표, 방향, 거리를 사용하는 Raycast
Raycast는 특정 시작점(origin)에서 특정 방향(direction)으로 광선을 쏘아, 지정된 거리(maxDistance) 내에서 충돌을 감지한다. 이때 Physics.Raycast 함수를 사용한다.
using UnityEngine;
public class RaycastExample : MonoBehaviour
{
void Update()
{
// Ray의 시작점 (예: 현재 오브젝트의 위치)
Vector3 origin = transform.position;
// Ray의 방향 (예: 현재 오브젝트의 앞쪽 방향)
Vector3 direction = transform.forward;
// Ray의 최대 거리
float maxDistance = 10f;
// RaycastHit 구조체: 충돌 정보를 저장할 변수
RaycastHit hit;
// Raycast 실행
if (Physics.Raycast(origin, direction, out hit, maxDistance))
{
// 충돌이 감지된 경우
Debug.Log("Hit: " + hit.collider.name);
Debug.DrawLine(origin, hit.point, Color.red); // Ray를 시각적으로 표시
}
else
{
// 충돌이 없는 경우
Debug.DrawLine(origin, origin + direction * maxDistance, Color.green); // Ray를 시각적으로 표시
}
}
}
+ DrawLine은 Unity Screen 창에서 시작적으로 선을 보여주는 기능이다. 이로서 보다 쉽게 Ray가 뻗어 나가는 위치와 방향을 알 수 있다.
1.2 Ray를 사용하는 Raycast
Ray는 시작점과 방향을 포함하는 구조체로, Ray를 사용하여 Raycast를 수행할 수도 있다.
public class RayExample : MonoBehaviour
{
void Update()
{
// Ray 생성 (시작점과 방향 지정)
Ray ray = new Ray(transform.position, transform.forward);
// Ray의 최대 거리
float maxDistance = 10f;
// RaycastHit 구조체: 충돌 정보를 저장할 변수
RaycastHit hit;
// Raycast 실행
if (Physics.Raycast(ray, out hit, maxDistance))
{
// 충돌이 감지된 경우
Debug.Log("Hit: " + hit.collider.name);
Debug.DrawLine(ray.origin, hit.point, Color.red); // Ray를 시각적으로 표시
}
else
{
// 충돌이 없는 경우
Debug.DrawLine(ray.origin, ray.origin + ray.direction * maxDistance, Color.green); // Ray를 시각적으로 표시
}
}
}
2. Ray의 원리
Ray는 3D 공간에서 시작점(origin)과 방향(direction)을 가지는 직선이다.
Unity에서 Raycast는 이 Ray를 따라 물리적인 충돌을 검사한다.
Raycast는 다음과 같은 과정으로 동작한다.
1. Ray 생성: 시작점과 방향을 지정하여 Ray를 생성
2. 충돌 검사: Ray가 지정된 거리 내에서 Collider와 충돌하는지 검사
3. 충돌 정보 반환: 충돌이 발생하면 RaycastHit 구조체에 충돌 정보(충돌한 오브젝트, 충돌 지점, 충돌 표면의 법선 벡터 등)를 저장. 여기서 함수에 따라 충돌한 물체들에 대한 정보도 얻을 수 있다.
4. 결과 처리: 충돌 정보를 바탕으로 원하는 로직을 수행한다.
위는 사용 방법이고, 세부 원리에 대해선 조금 복잡하다. 아래의 내용을 참고하자.

Ray의 시작점과 방향
Ray의 시작점은 카메라의 위치(cameraPos)다.
Ray의 방향은 카메라의 근평면(Near Plane)에서의 위치(nearPlanePos)를 구한 후, (nearPlanePos - cameraPos).normalized로 계산한다.
이는 카메라에서 근평면까지의 방향 벡터를 정규화한 것이다.
+ Ray의 방향은 카메라의 위치와 근평면의 위치를 사용해 계산되지만, 실제로는 카메라의 시야 방향(View Direction)과 시야각(View Angle)도 고려해야 한다. 이를 FOV(Field of View)라 하며, 카메라가 한 번에 관측할 수 있는 영역을 말한다. 카메라로 볼 수 있는 대상의 범위인 것이다.
카메라의 좌표계와 평면의 깊이
카메라의 위치를 로컬 좌표계 기준으로 (0, 0, 0)이라고 가정할 때, 근평면(Near Plane)과 원평면(Far Plane)은 z값으로 깊이를 결정한다.
근평면의 z값(near)은 0보다 커야 하며, 원평면의 z값(far)은 근평면보다 커야 한다.
즉, 0 < near < far 관계가 성립해야 한다.
카메라의 시야 범위
카메라의 위치와 근평면 사이(0 < z < near)에 오브젝트가 배치되면, 해당 오브젝트는 카메라에 포착되지 않아 시야에 보이지 않는다.
마찬가지로 원평면보다 더 뒤쪽(z > far)에 있는 오브젝트도 카메라에 포착되지 않는다.
이렇게 카메라의 시야 범위는 근평면과 원평면 사이(near ≤ z ≤ far)로 제한된다.
절두체 컬링(Frustum Culling)
카메라의 시야 범위를 활용한 대표적인 최적화 기법이 절두체 컬링(Frustum Culling)이다. 이는 카메라의 시야 범위(절두체, Frustum) 내에 있는 오브젝트만 렌더링하고, 범위 밖의 오브젝트는 렌더링하지 않는 방법이다.
+ 절두체 컬링은 단순히 near와 far 범위뿐만 아니라, 카메라의 시야각(View Angle)과 화면의 종횡비(Aspect Ratio)도 고려한다.
+ 절두체 컬링은 Unity에서 자동으로 수행되지만, 개발자가 직접 제어할 수도 있다. 다음은 절두체 컬링을 시각화한 예제이다.
public class FrustumCullingExample : MonoBehaviour
{
void Update()
{
Camera mainCamera = Camera.main;
// 절두체의 평면들을 가져옴
Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(mainCamera);
// 테스트할 오브젝트
GameObject testObject = GameObject.Find("TestObject");
if (testObject != null)
{
// 오브젝트의 경계 상자(Bounds) 계산
Bounds bounds = testObject.GetComponent<Renderer>().bounds;
// 절두체 내부에 있는지 확인
if (GeometryUtility.TestPlanesAABB(frustumPlanes, bounds))
{
Debug.Log("TestObject is inside the frustum.");
}
else
{
Debug.Log("TestObject is outside the frustum.");
}
}
}
}
절두체(Frustum)의 정의
절두체는 카메라의 근평면, 원평면, 그리고 시야각(View Angle)로 정의되는 3D 공간의 영역이다.
이 영역은 카메라의 시야를 형성하며, 절두체 내부에 있는 오브젝트만 렌더링 대상이 된다.
절두체 컬링의 동작
Unity는 매 프레임마다 각 오브젝트의 위치와 크기를 계산해 절두체 내부에 있는지 확인한다.
절두체 내부에 있는 오브젝트만 렌더링하고, 외부에 있는 오브젝트는 렌더링하지 않는다.
이를 통해 불필요한 렌더링을 줄이고 성능을 최적화한다.
절두체 컬링의 활용
절두체 컬링은 특히 대규모 3D 게임에서 필수적인 최적화 기법이다.
예를 들어, 오픈 월드 게임에서는 카메라 시야 밖의 오브젝트를 렌더링하지 않음으로써 GPU 부하를 크게 줄일 수 있다.
이 밖에 수학적인 이유(행렬 연산에 의한 local - world - view - screen 변환 과정)가 있는데, 이는 나중에 기회가 되면 정리하려 한다.
3. Raycast 사용 방법
Raycast는 Unity에서 다양한 목적으로 사용되며, 대표적으로 다음과 같은 세 가지 방식이 있다.
3.1.1 LayerMask를 사용한 충돌 최적화
Raycast는 특정 레이어(Layer)에 속한 오브젝트만 충돌 검사를 하도록 설정할 수 있다. 이를 통해 불필요한 충돌 검사를 줄이고 성능을 최적화할 수 있다.
LayerMask에 대한 세부 설명은 다음과 같다.
Unity에서 Layer는 int형 변수로 표현되며, 32개의 비트로 구성되어 있다. 이 중 0부터 7번까지의 레이어는 Unity에서 기본적으로 제공하는 Built-In 레이어이며, 8부터 31번까지의 레이어는 사용자가 직접 정의할 수 있다.


LayerMask를 설정하지 않으면 Raycast는 모든 레이어에 대해 충돌 검사를 수행한다. 불필요한 충돌 검사를 줄이고 성능을 최적화하기 위해선 LayerMask는 필수이다.
3.1.2 LayerMask를 사용한 레이어 설정 방법
LayerMask를 사용하기 위한 방법은 보통 2가지로 대표된다.
3.1.2.1 비트 연산을 사용한 LayerMask 설정
각 레이어는 비트로 표현되며, 비트 연산을 통해 원하는 레이어를 선택하거나 제외할 수 있다.
예를 들어, 레이어 8과 레이어 9를 감지하려면 다음과 같이 설정한다.
int layerMask = (1 << 8) | (1 << 9);
3.1.2.2 LayerMask 클래스를 사용한 레이어 설정
비트 연산이 복잡하다면, LayerMask 클래스를 사용하여 레이어 이름으로 레이어를 설정할 수 있다.
LayerMask.GetMask 메서드를 사용하는 것으로, 레이어 이름으로 LayerMask를 생성할 수 있다.
int layerMask = LayerMask.GetMask("LayerName1", "LayerName2");
+ LayerMask를 무조건 사용해야하는 가장 큰 이유중 하나는 Mesh Collider이다. Mesh는 폴리곤(3차원 공간에 존재하는 위치 벡터 3개로 이루어진 삼각형이다.)으로 이루어져 있으며, 모든 폴리곤에 대해 Raycast를 수행하기에 어마어마한 연산이 소모된다. 이와 관련되어 Unreal에선 최적화를 위해 Mesh를 넣을 시 해당 Mesh의 폴리곤을 뭉개 PhysicsAsset(충돌 처리용 Mesh)를 따로 만들어서 처리한다. 이런 이유로 가능하면 최적화를 위해 충돌용 Collider를 따로 만들어 사용하도록 하자.
+ 삼각형의 내부로 직선이 통과하는지 여부를 계산하는 공식을 가지고 Raycast를 수행한다.
3.2 카메라와 캐릭터 사이의 장애물 처리
3D 게임에서 카메라가 캐릭터를 따라다닐 때, 카메라와 캐릭터 사이에 장애물이 있으면 시야가 가려질 수 있다. 이때 Raycast를 사용해 카메라와 캐릭터 사이에 장애물이 있는지 검사하고, 장애물이 있다면 카메라를 장애물 앞쪽으로 이동시켜 캐릭터가 보이도록 할 수 있다.
이 방식은 MMORPG(예: 월드 오브 워크래프트)와 같은 게임에서 자주 사용된다.
3.3 2D 화면에서 마우스 포인터로 3D 오브젝트 선택
2D 화면(ex) 마우스 위치)에서 3D 오브젝트를 선택할 때 Raycast를 사용할 수 있다. 이는 마우스 클릭으로 오브젝트를 선택하거나 상호작용하는 기능을 구현할 때 유용하다.
public class MouseRaycastExample : MonoBehaviour
{
void Update()
{
// 마우스 포인터의 위치에서 Ray 생성
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// RaycastHit 구조체: 충돌 정보를 저장할 변수
RaycastHit hit;
// Raycast 실행
if (Physics.Raycast(ray, out hit))
{
// 충돌이 감지된 경우
Debug.Log("Hit: " + hit.collider.name);
// 마우스 클릭 시 오브젝트 선택
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Selected: " + hit.collider.name);
}
}
}
}
4. 결론
Raycast는 Unity에서 매우 유용한 기능으로, 다양한 상황에서 활용할 수 있다. 직접 좌표와 방향을 정의하여 Raycast를 수행하거나, Ray를 직접 생성하여 사용하면 된다. 사용할 때 충돌 정보(Collider)가 있어야 충돌 처리가 가능하니 만약 충돌하고자하는 물체가 있다면 Collider 컴포넌트를 추가해주자.
참조
Raycast API : https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Physics.Raycast.html
Ray API : https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Ray.html
'GameEngine > Unity' 카테고리의 다른 글
Unity UI 레이아웃과 이미지 활용하기 (0) | 2025.03.06 |
---|---|
Collider 컴포넌트의 Trigger 옵션에 대하여 (0) | 2025.02.10 |
IL2CPP, Mono, AOT, JIT 개념 정리 (0) | 2025.02.06 |
C#의 object, Unity의 Object의 차이 (0) | 2025.02.05 |
객체의 회전에 대하여 (0) | 2025.02.03 |