How to Raycast in Roblox (Raycasting Guide)

Quick answer: Call workspace:Raycast(origin, direction, params) with a Vector3 origin and direction to cast an invisible ray and get a RaycastHit describing the first thing it hits. Use a RaycastParams object to exclude your character or other parts you want the ray to pass through.

Raycasting in Roblox is done by calling workspace:Raycast(origin, direction), where origin is a Vector3 point where the ray starts and direction is a Vector3 that says which way the ray travels and how far it goes. If the ray hits something, the function returns a RaycastHit with details about what was hit; if it hits nothing, it returns nil.

What is a raycast actually doing?

A raycast fires an invisible line into the 3D world and reports the first object that line touches. Games use it constantly: guns checking whether a shot connects, characters detecting the ground beneath them, lasers, security cameras, hover vehicles, and click-to-move systems all run on raycasts. Unlike the Touched event, a raycast does not require anything to physically move into anything else — you can fire one from anywhere at any moment and get an answer immediately, which makes it ideal for instant-hit weapons and ground checks.

How do I call workspace:Raycast?

The modern API is a single method on Workspace:

local origin = Vector3.new(0, 10, 0)
local direction = Vector3.new(0, -20, 0) -- straight down, 20 studs

local result = workspace:Raycast(origin, direction)
if result then
    print("Hit", result.Instance.Name, "at", result.Position)
else
    print("Nothing hit")
end

Two details trip up almost everyone learning this:

  1. Direction includes distance. A direction of (0, -20, 0) casts 20 studs downward. The length of the direction vector is the maximum range of the ray.
  2. Nil means miss. There is no error when the ray hits nothing — you simply get nil, so always check before reading properties off the result.

The old Ray.new() constructor still exists (workspace:Raycast(Ray.new(origin, direction))), but it is legacy style. New code should always pass origin and direction directly, because that form lets you supply a third argument: the RaycastParams.

How do I aim a ray from a part or the camera?

In practice you rarely hardcode directions. You build them from a CFrame's LookVector multiplied by the range in studs:

local gun = workspace.LaserGun.Tip -- some attachment point on your weapon
local RANGE = 200 -- studs

local direction = gun.CFrame.LookVector * RANGE
local result = workspace:Raycast(gun.Position, direction)

LookVector is a unit vector (length 1), so multiplying it by 200 produces a vector pointing where the part faces, exactly 200 studs long. For tools held by a player, you often want to aim from the camera through the mouse instead:

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera

local origin = camera.CFrame.Position
local direction = (mouse.Hit.Position - origin).Unit * 300

If you want the shot triggered by a click or key press rather than running every frame, combine this with input handling as described in how to detect key presses in Roblox.

How do I ignore my own character with RaycastParams?

Without configuration, a ray fired from inside a character immediately hits that character. Fix it with a RaycastParams:

local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { character }

local result = workspace:Raycast(gun.Position, direction, params)

You can also set params.IgnoreWater = true if terrain water should be transparent to the ray, and reuse the same params object for every shot instead of rebuilding it.

What does a RaycastHit contain?

When the ray hits something, you get back an object with four properties you will use constantly:

if result then
    local hitPart = result.Instance  -- the BasePart or Terrain that was hit
    local hitPos  = result.Position  -- Vector3 point of impact
    local normal  = result.Normal    -- unit Vector3 facing away from the surface
    local dist    = result.Distance  -- studs travelled from origin to hit
end

How do I build a simple laser gun?

Putting it together: a LocalScript listens for activation, casts from the muzzle toward the mouse, excludes the shooter, damages a Humanoid if one was hit, and briefly draws a visible beam so players can see the shot:

local tool = script.Parent
local player = tool.Parent.Parent -- adjust to your rig hierarchy

tool.Activated:Connect(function()
    local mouse = player:GetMouse()
    local origin = tool.Handle.Position
    local direction = (mouse.Hit.Position - origin).Unit * 300

    local params = RaycastParams.new()
    params.FilterType = Enum.RaycastFilterType.Exclude
    params.FilterDescendantsInstances = { tool, player.Character }

    local result = workspace:Raycast(origin, direction, params)

    -- draw the beam: thin neon part stretched between origin and endpoint
    local endpoint = result and result.Position or (origin + direction)
    local beam = Instance.new("Part")
    beam.Anchored = true
    beam.CanCollide = false
    beam.Material = Enum.Material.Neon
    beam.Size = Vector3.new(0.2, 0.2, (endpoint - origin).Magnitude)
    beam.CFrame = CFrame.lookAt(origin, endpoint) * CFrame.new(0, 0, -beam.Size.Z / 2)
    beam.Parent = workspace
    game:GetService("Debris"):AddItem(beam, 0.1)

    if result then
        local humanoid = result.Instance.Parent:FindFirstChildOfClass("Humanoid")
        if humanoid then
            humanoid:TakeDamage(15)
        end
    end
end)

The visualization part is optional but invaluable while developing — you instantly see whether your origin, direction, and filter are behaving. Note the beam uses CFrame.lookAt plus a half-length offset so the part spans exactly from origin to endpoint.

How do I detect multiple objects along one path?

workspace:Raycast stops at the first hit only. To collect several hits along the same line — say, penetrating walls or counting every target in the way — cast again starting just past each hit until you miss:

local origin = gun.Position
local remaining = 500

while remaining > 0 do
    local result = workspace:Raycast(origin, Vector3.new(direction.Unit.X * remaining,
        direction.Unit.Y * remaining, direction.Unit.Z * remaining), params)
    if not result then break end
    table.insert(hits, result.Instance)
    local traveled = result.Distance + 0.1 -- step slightly past the surface
    origin = origin + direction.Unit * traveled
    remaining -= traveled
end

Keep penetration logic simple at first; most games never need more than the single first hit.

Why does my raycast return nil when something is clearly there?

The usual suspects: the ray starts inside an excluded instance, the direction vector accidentally has near-zero length, FilterType is set to Include but you forgot to add the parts you actually want to test, or the target is non-collidable geometry the ray skips by default. Debug by temporarily removing the params argument and visualizing the beam — if the drawn line passes through the object, the problem is your filter, not your math.

Raycasting pairs naturally with the other event-driven patterns on this site: trigger shots with input detection, and use Touched events when physical overlap matters more than line-of-sight.

Learn Luau by doing

Reading about raycasts only gets you so far — the fastest way to internalize origins, directions, and filters is to build small challenges that force you to use them. On Luablox lessons you work through interactive Luau exercises in order, the /theory reference explains the underlying language concepts, and /problems gives you hands-on tasks to apply them. Try a mini-project like a laser pointer that changes color based on what material it hits.

Frequently asked questions

What is the difference between Ray.new and workspace:Raycast?

Ray.new() just constructs a Ray data object; workspace:Raycast(origin, direction) actually performs the cast and returns a RaycastHit or nil. Modern code passes origin and direction directly to workspace:Raycast so a RaycastParams filter can be supplied as a third argument.

How do I make a raycast ignore my own character?

Create a RaycastParams with RaycastParams.new(), set FilterType to Enum.RaycastFilterType.Exclude, put the character model in FilterDescendantsInstances, and pass the params as the third argument to workspace:Raycast.

What units does the raycast direction use?

Direction is a plain Vector3 measured in studs. The magnitude of the vector is the maximum distance the ray travels, so Vector3.new(0, -50, 0) casts 50 studs straight down.

Why does workspace:Raycast return nil?

nil simply means the ray reached its full length without hitting anything — it is not an error. Always guard the result with an if statement before accessing Instance, Position, Normal, or Distance.

Can a raycast detect multiple objects?

Not in one call — workspace:Raycast stops at the first hit. To find multiple objects along the same line, loop: record each hit, restart the ray slightly past the last Position, and repeat until the ray misses.

Learn core Roblox scripting and Luau programming concepts, including: how to raycast roblox, raycast roblox, workspace raycast, raycastparams roblox, roblox laser gun script.

Related pages