area = 30
playerCheckToggle = false
Citizen.CreateThread(function ()
while true do
if IsControlJustPressed(0, 303) then
if(playerCheckToggle == false) then
playerCheckToggle = true
elseif(playerCheckToggle == true) then
playerCheckToggle = false
end
end
Wait(1)
end
end)
Citizen.CreateThread(function ()
while true do
while playerCheckToggle do
local playerPed = PlayerPedId(-1)
local playerId = PlayerId(-1)
local playerName = GetPlayerName(playerId)
local playerCoords = GetEntityCoords(playerPed)
local activePlayers = GetActivePlayers()
for i, player in ipairs(activePlayers) do
local otherPed = GetPlayerPed(player)
local x2, y2, z2 = table.unpack(GetEntityCoords(otherPed))
local distance = math.floor(GetDistanceBetweenCoords(playerCoords.x, playerCoords.y, playerCoords.z, x2, y2, z2, true))
if distance <= area then
DrawText3D(x2, y2, z2+1, "ID: " .. GetPlayerServerId(player) .. " | " .. GetPlayerName(player) .. "")
end
end
Wait(1)
end
Wait(1)
end
end)
function DrawText3D(x,y,z, text)
local onScreen,_x,_y= World3dToScreen2d(x,y,z)
if onScreen then
BeginTextCommandDisplayText("STRING")
SetTextScale(0.0, 0.40)
SetTextFont(0)
SetTextColour(255, 255, 255, 255)
SetTextCentre(1)
AddTextComponentString(text)
EndTextCommandDisplayText(_x,_y)
end
end
The following code is untested but sum up everything I could think of :
local area = 30 -- Use local variables
local playerCheckToggle = false
-- Use local functions if you use them only in that file
local function DrawText3D(coords, text) -- coords is a vector3, usually easier to use with natives
SetTextScale(0.0, 0.40)
SetTextFont(0)
SetTextColour(255, 255, 255, 255)
SetTextCentre(1)
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringPlayerName(text) -- New native name
SetDrawOrigin(coords, 0) -- The proper way to draw 3d text
EndTextCommandDisplayText(0.0, 0.0)
ClearDrawOrigin()
end
CreateThread(function() -- Removed the Citizen. but thats not mandatory
while true do
if IsControlJustPressed(0, 303) then
playerCheckToggle = not playerCheckToggle -- Correct way to toggle a boolean
end
if playerCheckToggle then -- Merged the two thread together as they have the same wait time
local playerPed = PlayerPedId() -- This native takes no parameters
local playerId = PlayerId() -- This native takes no parameters
local playerName = GetPlayerName(playerId)
local playerCoords = GetEntityCoords(playerPed)
local activePlayers = GetActivePlayers()
for i = 1, #activePlayers do -- Avoid the use of ipairs and pairs as much as possible
local player = activePlayers[i]
local otherPed = GetPlayerPed(player)
local pos = GetEntityCoords(otherPed) -- Don't unpack the data and use the vector3
local distance = #(playerCoords - pos) -- Use the distance operator between vector3, no need to floor the value
if distance <= area then
DrawText3D(vector3(pos.xy, pos.z + 1), "ID: " .. GetPlayerServerId(player) .. " | " .. GetPlayerName(player) .. "")
-- A bit strange vector3 manipulation, see the doc : https://docs.fivem.net/docs/scripting-reference/runtimes/lua/functions/vector3/
end
end
end
Wait(0) -- Wait(1) is the same as Wait(0)
end
end)
It is still not perfect however, you could use key bindings to avoid creating a thread for instance.
In the spirit of optimization, you could create a thread that checks every 250 or 500 ms for players within a slightly larger radius store them in an array. Then use a similar thread to what @Elio suggested and iterate the array of players within that radius. This would prevent you from calculating the distance between you and every player every frame, which could substantially cut down on CPU time if there are a large number of players in the server. Instead, you will be calculating the distance between you and every player within a reasonable distance from you that may need their names displayed.
Here is a quick untested mock up of what that may look like. Here, I am checking every 500ms for players within area * 2 of your player. Anyone further than that away will likely not be approaching you faster than 500ms.
local area = 30 -- Use local variables
local playerCheckToggle = false
local activePlayersNear = {}
-- Use local functions if you use them only in that file
local function DrawText3D(coords, text) -- coords is a vector3, usually easier to use with natives
SetTextScale(0.0, 0.40)
SetTextFont(0)
SetTextColour(255, 255, 255, 255)
SetTextCentre(1)
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringPlayerName(text) -- New native name
SetDrawOrigin(coords, 0) -- The proper way to draw 3d text
EndTextCommandDisplayText(0.0, 0.0)
ClearDrawOrigin()
end
CreateThread(function() -- Removed the Citizen. but thats not mandatory
while true do
if IsControlJustPressed(0, 303) then
playerCheckToggle = not playerCheckToggle -- Correct way to toggle a boolean
end
if playerCheckToggle then -- Merged the two thread together as they have the same wait time
for i = 1, #activePlayersNear do -- Avoid the use of ipairs and pairs as much as possible
local player = activePlayersNear[i]
local otherPed = GetPlayerPed(player)
local pos = GetEntityCoords(otherPed) -- Don't unpack the data and use the vector3
local distance = #(playerCoords - pos) -- Use the distance operator between vector3, no need to floor the value
if distance <= area then
DrawText3D(vector3(pos.xy, pos.z + 1), "ID: " .. GetPlayerServerId(player) .. " | " .. GetPlayerName(player) .. "")
-- A bit strange vector3 manipulation, see the doc : https://docs.fivem.net/docs/scripting-reference/runtimes/lua/functions/vector3/
end
end
end
Wait(0) -- Wait(1) is the same as Wait(0)
end
end)
CreateThread(function()
while true do
if playerCheckToggle then
activePlayersNear = {}
local playerPed = PlayerPedId() -- This native takes no parameters
local playerId = PlayerId() -- This native takes no parameters
local playerName = GetPlayerName(playerId)
local playerCoords = GetEntityCoords(playerPed)
local activePlayers = GetActivePlayers()
for i = 1, #activePlayers do -- Avoid the use of ipairs and pairs as much as possible
local player = activePlayers[i]
local otherPed = GetPlayerPed(player)
local pos = GetEntityCoords(otherPed) -- Don't unpack the data and use the vector3
local distance = #(playerCoords - pos) -- Use the distance operator between vector3, no need to floor the value
if distance <= area * 2 then
table.insert(activePlayersNear, activePlayers[i])
end
end
end
Wait(500)
end
end)
This could absolutely be further cleaned up, this is just a quick mock up of how you may reduce CPU time on a resource such as this.
I would suggest to remove the whole loop waiting for pressing a key, RegisterKeyMapping is better and you will save performance.
local playerCheckToggle = false
RegisterKeyMapping('+showplayers', 'your key description', 'keyboard', YourKey)
RegisterCommand('+showplayers', function() -- when holding key
if playerCheckToggle == true then
return
end
playerCheckToggle = true
CreateThread(function()
while playerCheckToggle do
--rest of the code from above
end
end)
end,false)
RegisterCommand('-showplayers', function() --when key is released
playerCheckToggle = false
end,false)
Since I want to press the key once and display the names and press the key again to hide the names, I came up with this:
local area = 30
local playerCheckToggle = false
local function DrawText3D(coords, text)
SetTextScale(0.0, 0.40)
SetTextFont(0)
SetTextColour(255, 255, 255, 255)
SetTextCentre(1)
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringPlayerName(text)
SetDrawOrigin(coords, 0)
EndTextCommandDisplayText(0.0, 0.0)
ClearDrawOrigin()
end
RegisterKeyMapping('showplayers', 'Toggle Near Players Names', 'keyboard', 'U')
RegisterCommand('showplayers', function()
if playerCheckToggle then
playerCheckToggle = not playerCheckToggle
return
end
playerCheckToggle = true
CreateThread(function()
while playerCheckToggle do
local playerPed = PlayerPedId()
local playerId = PlayerId()
local playerName = GetPlayerName(playerId)
local playerCoords = GetEntityCoords(playerPed)
local activePlayers = GetActivePlayers()
for i = 1, #activePlayers do
local player = activePlayers[i]
local otherPed = GetPlayerPed(player)
local pos = GetEntityCoords(otherPed)
local distance = #(playerCoords - pos)
if distance <= area then
DrawText3D(vector3(pos.xy, pos.z + 1), "ID: " .. GetPlayerServerId(player) .. " | " .. GetPlayerName(player) .. "")
end
end
Wait(0)
end
end)
end, false)
local area = 30
local playerCheckToggle = false
local function DrawText3D(coords, text)
SetTextScale(0.0, 0.40)
SetTextFont(0)
SetTextColour(255, 255, 255, 255)
SetTextCentre(1)
BeginTextCommandDisplayText("STRING")
AddTextComponentSubstringPlayerName(text)
SetDrawOrigin(coords, 0)
EndTextCommandDisplayText(0.0, 0.0)
ClearDrawOrigin()
end
RegisterKeyMapping('showplayers', 'Toggle Near Players Names', 'keyboard', 'U')
RegisterCommand('showplayers', function()
if playerCheckToggle then
playerCheckToggle = not playerCheckToggle
return
end
playerCheckToggle = true
CreateThread(function()
while playerCheckToggle do
local sleep = 1000
local playerPed = PlayerPedId()
local playerId = PlayerId()
local playerName = GetPlayerName(playerId)
local playerCoords = GetEntityCoords(playerPed)
local activePlayers = GetActivePlayers()
for i = 1, #activePlayers do
local player = activePlayers[i]
local otherPed = GetPlayerPed(player)
local pos = GetEntityCoords(otherPed)
local distance = #(playerCoords - pos)
if distance <= area then
sleep = 5
DrawText3D(vector3(pos.xy, pos.z + 1), "ID: " .. GetPlayerServerId(player) .. " | " .. GetPlayerName(player) .. "")
end
end
Citizen.Wait(sleep)
end
end)
end, false)
If you face flickering draw text then keep reducing sleep under distance check unless it stops flickering
Bad idea because it depends on player FPS and will flicker for players with higher FPS, and you probably want to make the script work properly for others players too, right? Citizen.Wait(1) in loops is practical and changing between 5 and 1ms will not make any noticeable change.
Thank you to all of you for helping. I know a lot of people often fear coming here and posting these sort of things as they are afraid of being bashed.
This is the type of community support we like to see, so thank you all.