Best way of creating markers?

Hi everyone!

So as I browse trough different resources here on the forum, I see alot of different ways to create inzone/markers or whatever you wana call them. What I mean by inzone/markers is a zone where if the player get’s close enough, you can click “E” or something happends in that zone.

My question is, what would be the most efficient / ms friendly way of creating these for either one or multiple locations?

This is often done by adding sleeps to your threads created in your script which will result in the thread running less frequently unless a condition is met for example if the player is x units away from the target.

Example:

local target = {x = 111.1, y = 222.2, z = 222.2}

Citizen.CreateThread(function()
    while true do
        local sleep = 2000 -- Default set sleep to run every 2 seconds

        local ped = GetPlayerPed(-1)
        local pos = GetEntityCoords(ped)
        local dist = GetDistanceBetweenCoords(pos, target.x, target.y, target.z, true)

        if dist < 30.0 then
            sleep = 0 -- If distance is 30 units away run thread continuously
            DrawMarker() -- This is where you would draw your marker and define the marker args
            if dist < 1.0 then
                -- If distance is 1 unit or less away the player can interact
                -- Add drawtext or displayhud help text etc
                if IsControlJustPressed(0, 38) then -- E
                    -- Do something
                end
            end
        end

        Citizen.Wait(sleep) -- Set the wait to the defined sleep cycle
    end
end)
1 Like

@HighHowdy and how would you apply this to multiple targets/zones?
I want to be able to do this as efficient as possible without these markers loops killing the ms of the resource.

Also I don’t really understand why you need to run locals within the loop. I do know you need to since the locals wont get recognized otherwise. Just feel like everything would run more smooth without them.
What if I create a global var for the:

local ped = GetPlayerPed(-1)
local pos = GetEntityCoords(ped)
local dist = GetDistanceBetweenCoords(pos, target.x, target.y, target.z, true)

Then maybe the loop will recognize them.

On smartphone right now so keeping it short.
I had the problem of markers killing ms and at some point not showing up since I had to many.

Check my resource pd5m. In HUD/markers_cl.lua the upmost loop should be what you need. If the code is not self-explanatory (dunno, can’t check right now) we can go over it if you want.

As far as locals go:
local variables will only be known by code of at most the same level:


if true then
  local variable = 'Hi'
  print("1", variable)
  if true then
    print("2", variable)
  end
end
print("3", variable)

This should make 1 and 2 print but not 3 (or give an error…).
Also note that GetPlayerPed(-1) will need to be rerun when the player dies and respawns. I made that mistake meaning that my code didn’t work anymore after first death.

where local target is just replace it with a table and store the locations, loop through the table and input coordinates from there rather than one specific value

Example

local areas = { -- table storing all locations you wish to show the markers
    {x = 111.1, y = 222.2, z = 333.3},
    {x = 111.1, y = 222.2, z = 333.3},
    {x = 111.1, y = 222.2, z = 333.3},
    {x = 111.1, y = 222.2, z = 333.3},
    {x = 111.1, y = 222.2, z = 333.3},
}

Citizen.CreateThread(function()
    while true do
        local sleep = 2000 -- Default set sleep to run every 2 seconds

        local ped = GetPlayerPed(-1)
        local pos = GetEntityCoords(ped)

        for k,v in pairs(areas) do -- Loop through the table "areas" to check coordinates
            local dist = GetDistanceBetweenCoords(pos, v.x, v.y, v.z, true)

            if dist < 30.0 then
                sleep = 0 -- If distance is 30 units away run thread continuously
                DrawMarker() -- This is where you would draw your marker and define the marker args
                if dist < 1.0 then
                    -- If distance is 1 unit or less away the player can interact
                    -- Add drawtext or displayhud help text etc
                    if IsControlJustPressed(0, 38) then -- E
                        -- Do something
                    end
                end
            end
        end

        Citizen.Wait(sleep) -- Set the wait to the defined sleep cycle
    end
end)

Edit: You need the variables in the thread to check for player location/distance to target, otherwise the conditions will never return true.

1 Like

@HighHowdy thankyou!
I will try this and also see if improvements can be made!
I’ll get back to you if I find something usefull!

@HighHowdy what if you want to give each area a specific name? for example if walking in to the first area, you will see “press E for cars” while are two will show “press E for bikes”?
What im seeing here is a bigger and bigger loop… Maybe this is just the way it has to be when looping over markers.

hmmm I will be trying that to! Thanks!

Replying to this one. As HighHowdy put in a pretty much functioning loop there is no need to look into my resource. The code he mentioned is exactly what you need.

As for specifying different areas, what you are asking is all configuration, which means the following:
Specify the config you want to be different inside the table with the locations. It could look like the example below.

local areas = {
  {x = 111.1, y = 222.2, z = 333.3, markertype = 36, vred = 255, vgreen = 0, vblue = 0, text = "Press ~INPUT_CONTEXT~ for vehicles", flag = "vehicles"},
  {x = 444.4, y = 555.5, z = 666.6, markertype = 37, vred = 0, vgreen = 255, vblue = 0, text = "Press ~INPUT_CONTEXT~ for bikes", flag = "bikes"},
}

Notice ~INPUT_CONTEXT~ instead of E as this will show the selected key instead of a hardcoded button. It’s really helpful if you changed your control scheme.

Now you create the thread as is and access the different information just as you would the coordinates:

for k, v in pairs(areas) do
  local dist = GetDistanceBetweenCoords(pos, v.x, v.y, v.z, true)
  if dist < 30.0 then
    sleep = 0
    DrawMarker(v.markertype, v.x, v.y, v.z, [direction, rotation and scale values, try to experiment here; you can save these in the config as well], v.vred, v.vgreen, v.vblue, ...)
    
    if dist < 1.0 then
      
      BeginTextCommandDisplayHelp("STRING") -- this displays a helpwindow in the top left corner
      AddTextComponentString(v.text)
      EndTextCommandDisplayHelp(0,0,0, 500)

      if IsControlJustPressed(0, 38) then
        TriggerEvent('OpenGarage', v.flag)
      end
    end
  end
end

One info for the last line (TriggerEvent...): if you want to open a different garage depending on where you are, use a flag to influence the behavior or use different events that trigger depending on the flag.
So that event OpenGarage could look like this:

AddEventHandler('OpenGarage', function(flag)
  if flag == 'vehicles' then
    -- do something here
  elseif flag == 'bikes' then
    -- do something else here
  elseif flag == 'whatever' then

  end
end)

I’ll post later what I did, thanks silverman!

I would prefer to use the areas in a config instead, just for structure.
I kind of know how to use these inputs, but I wanted to be able to get this done in the most efficient way possible. I will keep looking and trying and see what fits best.