U
17 / 22 · 10 min
FAQ

Raycasts: asking the world what is in front of you

the short answer

How do I detect what the player is aiming at or standing on?

Physics.Raycast fires an invisible line and reports the first collider it meets, with the point, the distance and the surface normal. Always pass a LayerMask and a maximum distance — without them a ray tests every layer out to infinity, and one fired from a third-person camera hits the player standing in front of it first.

A raycast is a question: if I fire an invisible line from here, in this direction, what does it hit first? Shooting, ground checks, what is under the player's finger, what the camera is looking at, whether an enemy can see you — all of it is one function with four arguments.

animated diagram

One invisible line, and the first collider it meets hands back the point, the surface normal and the distance.

The shape of it

Every raycast you will ever write looks like this
using UnityEngine;

public class Shooter : MonoBehaviour
{
    public float range = 50f;
    public LayerMask hittable;      // set this in the Inspector

    void Fire()
    {
        // origin, direction, out result, how far, what counts
        if (Physics.Raycast(transform.position, transform.forward,
                            out RaycastHit hit, range, hittable))
        {
            Debug.Log("hit " + hit.collider.name + " at " + hit.distance + "m");
        }
    }
}

What the hit tells you

  • hit.point — the exact spot in world space. This is where a bullet hole, a spark or a decal goes.

  • hit.normal — the direction the surface faces. Align an effect to it and the spark lies flat on the wall instead of sticking out of it.

  • hit.distance — how far the ray travelled. Useful for damage falloff, and for drawing a tracer the right length.

  • hit.collider — what was hit. Call GetComponent on it to find the health script, or CompareTag to sort friend from wall.

Placing an effect so it sits on the surface
// Quaternion.LookRotation(hit.normal) makes the effect's forward axis point
// straight out of the wall.
Instantiate(impactPrefab, hit.point, Quaternion.LookRotation(hit.normal));

Layer masks, properly

A LayerMask is not a layer number. It is 32 bits, one per layer, and the bit is set when that layer counts. Declaring the field as LayerMask gives you the tick-box dropdown in the Inspector and saves you from ever doing the arithmetic.

The bug, and the two right ways
// WRONG: layer 8 as a number means bit 3, which is layer 3.
Physics.Raycast(origin, dir, out hit, range, 8);

// Right, in code:
int mask = 1 << LayerMask.NameToLayer("Enemy");

// Right, and better: let the Inspector do it.
public LayerMask hittable;

// Everything EXCEPT the player's own layer:
int allButPlayer = ~(1 << LayerMask.NameToLayer("Player"));

From the camera, or from a finger

ScreenPointToRay — the same line for mouse and touch
void Update()
{
    // Mouse. On a phone, Input.mousePosition follows the first touch too,
    // so this works on both without changing anything.
    if (!Input.GetMouseButtonDown(0)) return;

    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

    if (Physics.Raycast(ray, out RaycastHit hit, 100f, selectable))
    {
        hit.collider.GetComponent<Selectable>()?.Select();
    }
}

Seeing the ray

A raycast is invisible, which makes debugging it guesswork unless you draw it. Two lines turn a mystery into something you can watch in the Scene view while the game runs.

Draw what you cast
// Green to where it hit, red for the full length when it missed.
if (Physics.Raycast(origin, dir, out RaycastHit hit, range, mask))
    Debug.DrawLine(origin, hit.point, Color.green, 1f);
else
    Debug.DrawRay(origin, dir * range, Color.red, 1f);

When a line is too thin

  • SphereCast sweeps a ball along the ray. This is what a ground check wants, and what a thick projectile wants, because a line slips through gaps that a body would not.

  • RaycastAll returns everything along the line, unsorted. Use it to shoot through several enemies — but sort by distance yourself, because the order is not guaranteed.

  • RaycastNonAlloc fills an array you own instead of allocating a new one. On a phone, an Update that calls RaycastAll every frame produces garbage the collector eventually has to stop the game to clear.

  • Triggers are ignored or included depending on Physics settings. Pass QueryTriggerInteraction explicitly rather than relying on a project-wide default somebody may change.

the lesson that builds this

Projectiles, damage and VFX

This post is a standalone recipe. In the course, the same thing is built as part of the one project that runs through all five tabs — Character & Combat, lesson 05.