Raycasting
Raycasting casts an invisible ray in a direction to find Colliders — the foundation of shooting, ground checks, mouse picking, and line-of-sight gameplay.
Imagine...
A Raycast is a flashlight in the dark. You shine it one way — the beam travels until it hits something. Then it reports what it hit, how far, where, and which way the surface faces. In a shooter, a Raycast stands in for a real physics bullet — cheaper, faster, more precise.
The concept in detail
Physics.Raycast() takes an origin, a direction, and returns true if
the ray hits a Collider. Details land in a RaycastHit: hit position (
.point), surface direction (.normal), distance (
.distance), and the object (.collider).
Unity ships several variants: Physics.RaycastAll() returns every Collider along
the path (not just the nearest), Physics.SphereCast() sweeps a sphere (smoother
character “snapping”), and Physics.BoxCast() sweeps a box. From Unity 2022+,
Physics.RaycastCommand can run raycasts in parallel on the Job System.
Performance: Raycasts are not free — each call walks
the broadphase BVH. Always pass a LayerMask and a maxDistance. Avoid
looping them every frame — an AI can raycast 4 directions every 0.2s instead of every frame.
Common uses: shooting (hitscan, no Rigidbody bullet),
ground check (is the character standing before a
jump), mouse picking (
Camera.ScreenPointToRay() turns a click into a 3D ray),
line of sight (does anything block the AI from the
player).
Raycast variants
A straight ray; returns only the nearest Collider. Use it for almost everything.
Physics.Raycast()
A spherical sweep — better for characters, large weapons, and wide-area detection.
Physics.SphereCast()
Returns all Colliders along the path. Use for armor-piercing bullets, lights through glass.
Physics.RaycastAll()
Hands-on steps
Pick origin and direction
Often Camera.main.transform.position and .forward for FPS, or Vector3.down for a ground check.
Declare a RaycastHit
RaycastHit hit; — then pass out hit into Physics.Raycast(). Keep it outside Update to avoid GC.
Set a LayerMask
Assign it in the Inspector, or LayerMask.GetMask("Enemy", "Ground") so you only test what you need.
Cap maxDistance
Always pass a max range — an infinite ray tests the whole scene. Example: gun range 50f, ground check 1.2f.
Draw a Debug Ray in Scene view
Debug.DrawRay(origin, direction * maxDist, Color.green) — visible while playing, no effect in builds.
Interactive simulator
Move the mouse over the canvas to aim the ray. Drag obstacles around.
No hit yet
Code example
BasicHitscan shooting — fire without a Rigidbody bullet.
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);
}
}
}Code example
AdvancedAccurate ground check plus mouse-to-world picking — the two most common use cases.
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);
}
}📌 Quick recap
- ▸Always pass a LayerMask + maxDistance
- ▸Declare RaycastHit once outside Update (avoid GC)
- ▸Camera.ScreenPointToRay() for mouse-to-3D
- ▸Debug.DrawRay() to see the ray in Scene view
- ▸SphereCast is smoother than Raycast for character movement
⚠️ Common mistakes
❌ The ray hits yourself (self-hit)
Origin sits inside the shooter’s Collider — always returns true
✅ Offset the origin (+0.1f along forward), or ignore your own layer
❌ Raycast every frame with no MaxDistance
An infinite ray tests the whole scene and tanks FPS
✅ Always pass a maxDistance that matches the use case