Raycasting in FiveM, explained properly

Raycasting in FiveM, explained properly — why there’s no Raycast() and how shapetests actually work

Almost every interaction system on your server is secretly asking one question: what is the player looking at? Opening a door, aiming at a ped to check ID, placing a prop where the crosshair lands, the “press E” that only shows when you face the right thing — all of it is a raycast.

And the first time you go looking for a Raycast() function in FiveM, you won’t find one. You find StartShapeTestLosProbe, GetShapeTestResult, and a flags argument nobody explains. So here’s the mental model that makes it all click.

It’s asynchronous

In most engines you call raycast(a, b) and get an answer on the same line. FiveM does not work like that. You start a shape test one frame and read the result the next. The start native hands you a handle (a ticket), and you cash it in with a second native:

local function castRay(from, to, flags, ignoreEntity)
    local handle = StartShapeTestLosProbe(
        from.x, from.y, from.z,
        to.x,   to.y,   to.z,
        flags, ignoreEntity, 7
    )

    local retval, hit, coords, normal, entity
    repeat
        Wait(0) -- let a frame pass so the engine can finish the test
        retval, hit, coords, normal, entity = GetShapeTestResult(handle)
    until retval ~= 1 -- 1 = still computing, loop until it's ready (2)

    return hit, coords, normal, entity
end

That retval is a status code: 0 = handle gone, 1 = not ready yet, 2 = ready. This one detail is behind almost every “my raycast returns nothing” post — people read the result on the same line they started it, before the engine has answered.

(There’s also a synchronous StartExpensiveSynchronousShapeTestLosProbe — ready on the next call, no loop — fine for one-off casts, blocks the frame so don’t spam it.)

“What am I looking at?” — the camera ray

There’s no native for this in one call; you build the ray from the camera’s position + rotation:

local function rotationToDirection(rot)
    local z, x = math.rad(rot.z), math.rad(rot.x)
    local n = math.abs(math.cos(x))
    return vector3(-math.sin(z) * n, math.cos(z) * n, math.sin(x))
end

local cam  = GetGameplayCamCoord()
local dir  = rotationToDirection(GetGameplayCamRot(2))
local dest = cam + (dir * 50.0) -- 50 units forward

local hit, coords, normal, entity = castRay(cam, dest, -1, PlayerPedId())
if hit and entity ~= 0 then
    print(("Hit entity %d (type %d)"):format(entity, GetEntityType(entity)))
end

That’s the skeleton behind every qb-target / ox_target you’ve used. GetEntityType(entity) returns 1 ped, 2 vehicle, 3 object — how a targeting script knows what to show.

The flags are a bitmask (this is the other big one)

flags decides what your ray is allowed to hit. Leave a category out and the ray passes straight through it:

  • 1 map/world · 2 vehicles · 4 peds · 16 objects · -1 everything

Combine them by adding: map + vehicles = 1 + 2 = 3. Most interaction casts just use -1. Being selective is also cheaper if you cast often.

Heads up: FiveM exposes these as a plain integer and doesn’t officially document each bit — the mappings above are community-tested and reliable for everyday use, but for less-common bits, verify in your own build. When in doubt, cast with -1 and filter by GetEntityType on the result.

Three points worth knowing

  • Client-side only. There’s no server raycast — the server doesn’t have the streamed collision. Client casts, server validates.
  • entityHit == 0 means you hit the world, not “nothing.” Use the hit boolean for whether, entityHit for what.
  • Don’t fire one every frame unless you need to. Cast on keypress, or throttle — not a bare Wait(0) loop forever.

I wrote the full breakdown — the async design decision explained, the surface-normal trick for placing props flush to walls, and diagrams for the ray anatomy + the frame-by-frame timeline — over on our blog:

How Raycasting Actually Works in FiveM (Shapetests Explained)

We build performance-first, server-authoritative scripts for QBCore / ESX / QBOX over at OsmFX Mods — including the kind of targeting and placement systems this post is about. If raycasts, targeting, or interaction bugs are giving you grief, reply here, always happy to dig in.


Disclaimer:
P.S. I used AI (primarily Cowork) to help with research, the content, visuals, and polishing the writing, but I reviewed and edited everything myself. I’m new to writing technical blogs, so if you notice anything that could be improved, whether it’s the content or the writing style, I’d genuinely appreciate constructive feedback.

3 Likes

Sad year to live in when everything is written by bots.

1 Like

Grow up, AI is here and it isn’t going away.

Was it written by AI, sure… is English his first language? Maybe not, and what is presented is factually correct - it isn’t wrong, there’s no AI hallucination.

AI is cool as a tool for being useful and helping taking the small weight off of person for menial tasks, if done right anyway.

You 100% write every line of code you do? Congratulations bud, you’ve fallen behind. Why write the same 20 lines of code ie doing a loop over objects when you can get AI to do it, you use oxlib and use raycast? Congrats bud, you’re using someone elses tooling instead of handwriting it yourself every time, you’re lazy brother.

5 Likes

I’ve added a disclaimer at the end of the post. Replies like yours mislead people into thinking that the article is of no substantial value to them.

There’s something cynical about the way certain people behave with regard to AI usage — and I’ve observed it elsewhere too. If it’s AI, they just start to denigrate it. It’s mostly people who either think they know too much or actually possess little to no knowledge. Even when they themselves have made a negligible contribution to the community (pretty evident from their profile’s topics section), they make sure they earn their share of attention by letting everyone know: “Hey, I went to this website that I don’t know how it works, I checked this article that I don’t know what it’s about, and I’m sharing a screenshot. The only thing I understand is that — it’s AI.”

I’m pretty sure that, even now, you’d probably be judging whether this message was written with AI or by a human. Good luck. I’ve even added a few — (dashes) so you don’t have to put in much effort figuring it out.


For the readers: I’ve made every effort to fact-check, properly reference, and thoroughly proofread everything before posting, with additional scrutiny knowing technically proficient people would read it. Even if you only read this forum post and not the full article, I’m confident you’ll learn something accurate and worthwhile.

1 Like

Either you or the AI definitely missed the remaining flags and doesn’t even talk about the option flags (that are set to 7 in your examples).

Would also be helpful to talk about uses for the other types of shape tests and why it can be really useful to use StartExpensiveSynchronousShapeTestLosProbe instead as there are many cases where an immediate result is neede (e.g. a custom camera should not go through walls)

2 Likes

I’m coming from another platform and I’m curious, how do people validate if a gun bullet is legit if raycast is client only? On another platform I would raycast on the client and if it says “hit person B” then the server would also raycast but directly to person B position to determine if it’s true or not. Can that be done on FiveM ? If not, what developers do other than client scripts ? I’m thinking of server side stuff

Since FiveM doesn’t have server-side raycasts, the best you can do is validate hits in weaponDamageEvent (docs). This server event fires whenever a player damages someone, and you can cancel it if the hit doesn’t look legitimate.

In that event, you can check things like whether the player is using the correct weapon, whether the damage amount is reasonable, whether the distance makes sense, and whether they’re firing at a believable rate.

The important limitation is that this isn’t true server-side hit validation. The server is still trusting information sent by the shooter’s client, so it’s only checking whether the reported hit seems reasonable, not whether the shot actually happened.


There’s also a downside to cancelling the event (refer to post), probably after validating and classifying a shot as unreasonable: The shooter’s game has already predicted that the hit was successful, so if the server rejects it, only the shooter sees the victim die. Everyone else still sees the victim alive. That means you need another system to resync the shooter’s game, otherwise they’ll be temporarily out of sync with everyone else.

1 Like