Can't reliably get the Ground Z position

I’m having issues getting the correct ground position for some coordinates. This is my output:
image

from this code:

    local val, zGround = GetGroundZFor_3dCoord(jobVector.x, jobVector.y, jobVector.z, true)
    currentJobLocation = vector3(jobVector.x, jobVector.y, zGround)
print('jobVector: ' .. json.encode(jobVector))
print('z: ' .. jobVector.z)
print('zGround: ' .. zGround)
1 Like

Edited: See two posts down.

The loop was some trash code from literally the other post below mine, I recalled seeing it there a long while ago.

Collision probably isn’t in scope.

This post may help you out

1 Like

That’s nonsense, and I don’t know why people are somehow spreading that as a ‘solution’.

There’s two likely things that may be going on:

  1. The collision data for that area isn’t loaded. Obvious, if not nearby. Also, not fixed by looping upwards. Would be fixed by, worst case, RequestAdditionalCollisionAtCoord - FiveM Natives @ Cfx.re Docs and similar.
  2. You’re checking for coords below ground already, so the closest is of course 0.

This whole ‘step upwards’ thing is just silly, slow, and misguided, and even in cases where you would need to get the lowest ground (say, an underpass) it’s best to start at 1000 downwards, then take every hit, - 0.5 or so, until you don’t hit anything anymore, and then get the result from the last hit.

Same for the post linked above, the thing solving it there is that the code teleports (without freezing the ped, good job!) to the target coords and then does a few random Wait calls (with 5 being worrying too → anyone with over 200 FPS will be screwed there) and usually getting lucky with the collision mesh for the area having loaded by that time. That still doesn’t make the part where you ‘loop upwards’ fix it, it rather shows it was a case of 1. from the list above.

1 Like

All the positions I copied using the qb-core admin menu with the player standing at the locations. I’ve ended up subtracting 0.99 from all the z positions and they all appear fine, so none of them are below the terrain.

Given I can walk on all the locations where the values end up as 0, could the collision data still not be loaded? … although given I’m not in the region, it’s a waypoint being calculated for an area that may not be loaded… hmm.

So is GetGroundZFor_3dCoord considered an expensive operation? I’m thinking that if I put it in my
loop when I’m displaying the marker on the ground with the Wait(0), that maybe calcluating it everything is a waste. Could leave a groundZ variable as nil and if nill going around the loop once it’s being displayed, calculate once then ignore every other iteration.

Make me cringe frankly. I recall doing this in Win32 programming to find windows with th specific “class” name. Eww. Just because someone thinks of something and it works doesn’t mean it should be done.

Thanks d-bubble, that definitely got me thinking about the problem at hand. Becuase my waypoints are potentionally a long way away from the player the mesh isn’t loaded in, so of course me calculating it before I arrive can only result in 0. So I decided to do the call when I’m displaying the marker once I’m in range and only calculate when the marker comes into range and reset it to nil when I’m no longer in range, then when I new point is generated it can repeat fo rthe new co-ordinates. No looping in order to get there the way people hav been suggesting. Just takes a little “architecture”. This is my new marker drawing thread:

Citizen.CreateThread(function()
    local waitTime = 500
    local zGround = nil

    while true do
        Citizen.Wait(waitTime)

        if jobBlip and currentJobLocation then
            local playerPos = GetEntityCoords(PlayerPedId())
            local distance = #(playerPos - currentJobLocation)
            if distance <= 15 then
                waitTime = 0

                if not zGround then
                    local val, ground = GetGroundZFor_3dCoord(currentJobLocation.x, currentJobLocation.y, currentJobLocation.z, true)
                    zGround = ground
                end

                DrawMarker(
                    27,
                    currentJobLocation.x, currentJobLocation.y, zGround, -- x, y, z
                    0, 0, 0, -- Direction vector x, y, z
                    0, 0, 0, -- Rotation x, y, z
                    5.0, 5.0, 5.0, -- Scale
                    10, 10, 255, 255, -- Red, Green, Blue, Alph
                    false, -- Slowly bob up and down?
                    false, -- Constantly face camera?
                    2, -- P19. Other values don't seem to do anything
                    false, -- Rotate
                    nil, -- textureDict [[ string ]]
                    nil, -- textureName [[ string ]]
                    false -- Draw on intesecting entities?
                )

                if distance <= 2.5 then
                    if not listen then
                        StartListeningForInput()
                    end
                else
                    listen = false
                end

            else
                zGround = nil
                waitTime = 500
            end

        end

    end
end)

That’s a lot easier to get direct ground positions then: peds in GTA have a script height of ~2 units so if you ended up with centered coordinates of the ped just ‘- 1’.