Raycasting
Raycasting은 방향으로 보이지 않는 광선을 쏴 Collider를 찾습니다. 사격·지면 검사·마우스 피킹·시야의 기초이며 LayerMask·maxDistance·SphereCast까지 다룹니다. 성능과 정확도를 함께 잡는 Unity 레이캐스트의 실전 가이드입니다.
상상해 보세요...
Raycast는 어둠 속의 손전등과 같습니다. 한 방향으로 비추면 빔이 무언가에 닿을 때까지 갑니다. 그다음 무엇을 맞췄는지, 얼마나 멀리, 어디, 그리고 면이 어느 쪽인지 보고합니다. 슈터에서는 실제 물리 총알 대신 — 더 싸고 빠르고 정밀합니다.
개념 자세히
Physics.Raycast()는 origin·direction을 받아 Collider에 맞으면
true를 반환합니다. 상세는 RaycastHit에: 히트 위치(
.point), 면 방향(.normal), 거리(
.distance), 오브젝트(.collider).
Unity는 여러 변형을 제공합니다: Physics.RaycastAll()은 경로상 모든
Collider(최근접만이 아님), Physics.SphereCast()는 구를 쓸어(캐릭터
“스냅”이 부드러움), Physics.BoxCast()는 상자를 쓸어갑니다. Unity 2022+부터
Physics.RaycastCommand로 Job System에서 병렬 raycast도 가능합니다.
성능: Raycast는 공짜가 아닙니다 — 호출마다
broadphase BVH를 걷습니다. 항상 LayerMask와 maxDistance를
넘기세요. 매 프레임 루프는 피하세요 — AI는 매 프레임 대신 0.2초마다 4방향 raycast.
흔한 용도: shooting(hitscan, Rigidbody 총알 없음),
ground check(점프 전 지면 확인),
mouse picking(
Camera.ScreenPointToRay()로 클릭을 3D ray로),
line of sight(AI와 플레이어 사이 장애물).
Raycast 변형
직선 레이. 반환값은 가장 가까운 Collider. 거의 모든 용도에 사용.
Physics.Raycast()
구형 스윕 — 캐릭터, 대형 무기, 넓은 범위 감지에 적합.
Physics.SphereCast()
경로상의 모든 Collider를 반환. 철갑탄, 유리를 통과하는 빛에 사용.
Physics.RaycastAll()
실습 단계
origin과 direction 정하기
FPS는 종종 Camera.main.transform.position과 .forward, 지면 검사는 Vector3.down.
RaycastHit 선언
RaycastHit hit; — Physics.Raycast()에 out hit로 넘깁니다. GC를 피하려면 Update 밖에 둡니다.
LayerMask 설정
Inspector에서 할당하거나 LayerMask.GetMask("Enemy", "Ground")로 필요한 것만 검사합니다.
maxDistance 제한
항상 최대 거리를 넘기세요 — 무한 ray는 씬 전체를 검사합니다. 예: 총 사거리 50f, 지면 검사 1.2f.
Scene view에 Debug Ray
Debug.DrawRay(origin, direction * maxDist, Color.green) — 플레이 중만 보이고 빌드에는 영향 없음.
인터랙티브 시뮬레이터
캔버스 위에서 마우스를 움직여 ray를 조준하세요. 장애물을 드래그할 수 있습니다.
아직 히트 없음
코드 예제
기본Hitscan 사격 — Rigidbody 총알 없이 발사.
using UnityEngine;
public class HitscanGun : MonoBehaviour
{
public float range = 50f;
public int damage = 25;
public LayerMask shootMask;
public ParticleSystem muzzleFlash;
Camera cam;
RaycastHit hit; // Declare outside Update to avoid GC
void Start() => cam = Camera.main;
void Update()
{
if (Input.GetButtonDown("Fire1")) Fire();
}
void Fire()
{
muzzleFlash.Play();
// Ray from camera center, forward
if (Physics.Raycast(cam.transform.position,
cam.transform.forward,
out hit, range, shootMask))
{
Debug.Log("Hit: " + hit.collider.name +
" at " + hit.distance.ToString("F1") + "m");
hit.collider.GetComponent<Health>()
?.TakeDamage(damage);
}
}
}코드 예제
고급정확한 지면 검사 + 마우스→월드 피킹 — 가장 흔한 두 용도.
using UnityEngine;
public class RaycastUtils : MonoBehaviour
{
[Header("Ground Check")]
public float groundCheckDist = 1.2f;
public LayerMask groundLayer;
bool isGrounded;
[Header("Mouse Picking")]
public LayerMask clickable;
Camera cam;
RaycastHit hit;
void Awake() => cam = Camera.main;
void Update()
{
// Ground check every frame — SphereCast is smoother than Raycast
isGrounded = Physics.SphereCast(
transform.position, 0.4f,
Vector3.down, out hit,
groundCheckDist, groundLayer
);
// Click → world position
if (Input.GetMouseButtonDown(0))
{
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 100f, clickable))
{
Debug.Log("Clicked: " + hit.collider.name);
// Move the character to hit.point
MoveTo(hit.point);
}
}
}
void OnDrawGizmosSelected()
{
Gizmos.color = isGrounded ? Color.green : Color.red;
Gizmos.DrawLine(transform.position,
transform.position + Vector3.down * groundCheckDist);
}
}📌 빠른 정리
- ▸항상 LayerMask + maxDistance 전달
- ▸RaycastHit는 Update 밖에 한 번 선언(GC 방지)
- ▸마우스→3D는 Camera.ScreenPointToRay()
- ▸Scene view에서 Debug.DrawRay()
- ▸캐릭터 이동은 SphereCast가 Raycast보다 부드러움
⚠️ 흔한 실수
❌ ray가 자기 자신을 맞춤(self-hit)
origin이 사수 Collider 안 — 항상 true
✅ origin을 forward로 +0.1f 오프셋, 또는 자기 Layer 무시
❌ MaxDistance 없이 매 프레임 Raycast
무한 ray가 씬 전체를 검사해 FPS 하락
✅ 용도에 맞는 maxDistance를 항상 전달