[How-To] Get the closest entity to your character (LUA)

First of all there are many methods to get the closest ped/entity/whatever to your character, however they have some issues:

  • They only are able to find things within a small radius.
  • They use broken native methods such as GetClosestPed which do not get the correct result.

Here i will show you a way to get the closest entity to you, without any of those issues ocurring (hopefully).

For an improved version of the script i reccomend checking this out.

function GetClosestEntity(entitytype, alive, targets, los, ignoreanimals, coords)

    -- Usage: GetClosestEntity("CPed", "alive", "any", "inlos", false, {x, y, z})
    -- entitytype accepts the following arguments: CPed, CObject, CVehicle, CPickup
    -- alive accepts the following arguments: "any", "alive", "dead" Vehicle Only: "alivedriver", "deaddriver"
    -- targets accepts the following arguments: "any", "player", "npc" or "empty" (Vehicle only!)
    -- los accepts the following arguments: "any", "notinlos", "inlos"
    -- coords accepts the following arguments: {x,y,z} or you can omit it and it will use the player's coords
    -- ignoreanimals is just a simple boolean

    -- Tested params: entitytype,alive,los,ignoreanimals,coords

    local entitytypeParam = entitytype or "CPed"

    -- Get only alive or dead entities according to the alive param
    local aliveParam = alive or "alive"

    -- Get entities that are only players or npcs or both.
    local targetsParam = targets or "any"

    -- Get only entities in or out of your line of sight according to the los param
    local losParam = los or "inlos"

    -- Ignore entities that are animals according to the ignoreanimals param
    local ignoreanimalsParam = ignoreanimals or false

    -- Get specified coords or coords of current player if empty
    local coordsparam = coords or GetEntityCoords(PlayerPedId())


    local closestEntity = -1
    local closestDist = -1
    local aliveCheck = -1
    local losCheck = -1

    -- Get Game Pool according to type:
    local entityPool = GetGamePool(entitytypeParam)

    -- Loop through the game pool until closest entity is found.
    for i = 1, #entityPool do
        entity = entityPool[i]

        -- Handles the alive param
        aliveCheck = (aliveParam == "alive" and not IsEntityDead(entity)) or (aliveParam == "dead" and IsEntityDead(entity)) or (aliveParam == "alivedriver" and entitytypeParam == "CVehicle" and not IsEntityDead(GetPedInVehicleSeat(entity, -1)) and not IsVehicleSeatFree(entity, -1)) or (aliveParam == "deaddriver" and entitytypeParam == "CVehicle" and IsEntityDead(GetPedInVehicleSeat(entity, -1)) and not IsVehicleSeatFree(entity, -1)) or (aliveParam == "any")

        -- Handles the targets param
        targetsCheck = (targetsParam == "player" and not entitytypeParam == "CVehicle" and IsPedAPlayer(entity)) or (targetsParam == "npc" and not entitytypeParam == "CVehicle" and not IsPedAPlayer(entity)) or (targetsParam == "npc" and entitytypeParam == "CVehicle" and not IsPedAPlayer(GetPedInVehicleSeat(entity, -1)) and not IsVehicleSeatFree(entity, -1)) or (targetsParam == "player" and entitytypeParam == "CVehicle" and IsPedAPlayer(GetPedInVehicleSeat(entity, -1))) or (targetsParam == "empty" and entitytypeParam == "CVehicle" and IsVehicleSeatFree(entity, -1) and GetVehicleNumberOfPassengers(entity) == 0 and GetPedInVehicleSeat(entity,-1) == 0) or (targetsParam == "any")
        -- Handles the los param
        losCheck = (losParam == "inlos" and HasEntityClearLosToEntity(PlayerPedId(), entity, 17)) or (losParam == "notinlos" and not HasEntityClearLosToEntity(PlayerPedId(), entity, 17)) or (losParam == "any")

        -- Handles the ignoreanimals param
        ignoreanimalsCheck = (ignoreanimalsParam and IsEntityAPed(entity) and GetPedType(entity) ~= 28) or (not ignoreanimalsParam)

        if entity ~= PlayerPedId() and aliveCheck and losCheck and targetsCheck and ignoreanimalsCheck then
            local distance = #(coordsparam - GetEntityCoords(entity))
            if closestDist == -1 or distance < closestDist then
                closestEntity = entity
                closestDist = distance
            end
        end

    end
    return closestEntity, closestDist

end

Example Usage:

 local ent, dist = GetClosestEntity("CPed", "alive", "any", "inlos", false)

    print(ent,dist)

    if IsEntityAVehicle(ent) then
        print("Entity is a veh")
    elseif IsEntityAPed(ent) then
        print("Entity is a ped")
    elseif IsEntityAnObject(ent) then
        print("Entity is a object")
    end

    if IsEntityDead(ent) then
        print("Entity is dead")
    else
        print("Entity is alive")
    end

This code might be a bit crap and unoptimized but im always happy for some constructive criticism if you find a way to improve it!

Thanks to Cozy on the cfx.re discord and @DrAceMisanthrope for helping me out with this!

5 Likes

In your code you are getting all peds, then looping through them all, checking the distance and inserting into a new table. Then you are sorting said table (which essentially loops it again). Then you are looping AGAIN to check if they are an NPC and alive, before returning the first true result.

I believe it could be faster by just getting all peds, then looping through them all, checking if they are an NPC and alive, and if so, checking their distance. You store the distance value along the way and at the end of ONE loop, you have the closest ped and the distance to said ped.

function GetClosestAliveNPC(coords)
	local pedPool = GetGamePool("CPed")
	local coords = coords or GetEntityCoords(PlayerPedId())
	local closestPed = -1
	local closestDist = -1
	for _, ped in pairs(pedPool) do
		if not IsPedAPlayer(ped) and not IsPedDeadOrDying(ped) then 
			local distance = #(coords - GetEntityCoords(ped))
			if closestDist == -1 or distance < closestDist then
				closestPed = ped
				closestDist = distance
			end
		end
	end
	return closestPed, closestDist
end

Usage:

local ped, dist = GetClosestAliveNPC() -- You can pass coords, or leave blank for player coords
print(ped, dist)
TaskJump(ped)

Would love to see the results of which runs faster.

“You store the distance value along the way and at the end of ONE loop, you have the closest ped and the distance to said ped.”
Was trying to figure out how to do that but i could not plan it out in my head, your code looks much nicer, ill test it when i have the time.

Dont know if its best to make different functions for different use cases or merge it all into one.

Something like this:

function GetClosestPed(los,alive,player,coords)
    -- los (gets only peds in your line of sight) , alive (self explanatory) , player (include players except your player ped)
	local pedPool = GetGamePool("CPed")
	local coords = coords or GetEntityCoords(PlayerPedId())
	local closestPed = -1
	local closestDist = -1
	for _, ped in pairs(pedPool) do
        -- do different logic depending on the function params.
		-- if not IsPedAPlayer(ped) and not IsPedDeadOrDying(ped) then 
		-- 	local distance = #(coords - GetEntityCoords(ped))
		-- 	if closestDist == -1 or distance < closestDist then
		-- 		closestPed = ped
		-- 		closestDist = distance
		-- 	end
		-- end
	end
	return closestPed, closestDist
end

I dont really know how to implement that without making some ugly if else chain

Firstly, there is a native called GetClosestPed which is why you should name it differently.
Here is one option, using your format;

function GetClosestPedAdvanced(los, alive, allowPlayers, coords)
	local playerPed = PlayerPedId()
	local pedPool = GetGamePool("CPed")
	local coords = coords or GetEntityCoords(playerPed)
	local closestPed = -1
	local closestDist = -1
	for _, ped in pairs(pedPool) do
		local isPlayer = IsPedAPlayer(ped)
		local los = (los == true or los == 1) and HasEntityClearLosToEntity(playerPed, ped, 17) or true
		local aliveCheck = (alive == true or alive == 1) and not IsPedDeadOrDying(ped) or true
		if aliveCheck and ped ~= playerPed and (allowPlayers or not isPlayer) then
			local distance = #(coords - GetEntityCoords(ped))
			if closestDist == -1 or distance < closestDist then
				closestPed = ped
				closestDist = distance
			end
		end
	end
	return closestPed, closestDist
end

-- USAGE:
GetClosestPedAdvanced(
	los,			-- [[ bool ]]		accepted: false, true, 0, 1, nil
	alive,			-- [[ bool ]]		accepted: false, true, 0, 1, nil
	allowPlayers,	-- [[ bool ]]		accepted: false, true, 0, 1, nil
	coords			-- [[ vector3 ]]	accepted: vector3, nil
)

-- EXAMPLES:
GetClosestPedAdvanced(1, 1, 1)
GetClosestPedAdvanced(true, false, false)
GetClosestPedAdvanced(0, true, 1, vector3(111.0, 222.0, 100.0))

But with some slight tweaking, you can change the “allowPlayers” variable to “targets” and receive a string of “players”, “npcs” or “all” to choose what ped you want to target.

function GetClosestPedAdvanced(los, alive, targets, coords)
	local playerPed = PlayerPedId()
	local pedPool = GetGamePool("CPed")
	local coords = coords or GetEntityCoords(playerPed)
	local targets = targets or "all"
	local closestPed = -1
	local closestDist = -1
	for _, ped in pairs(pedPool) do
		local isPlayer = IsPedAPlayer(ped)
		local los = (los == true or los == 1) and HasEntityClearLosToEntity(playerPed, ped, 17) or true
		local aliveCheck = (alive == true or alive == 1) and not IsPedDeadOrDying(ped) or true
		if aliveCheck and ped ~= playerPed and ((isPlayer and (targets == "all" or targets == "players")) or (not isPlayer and targets == "npcs")) then
			local distance = #(coords - GetEntityCoords(ped))
			if closestDist == -1 or distance < closestDist then
				closestPed = ped
				closestDist = distance
			end
		end
	end
	return closestPed, closestDist
end

-- USAGE:
GetClosestPedAdvanced(
	los,		-- [[ bool ]]		accepted: false, true, 0, 1, nil
	alive,		-- [[ bool ]]		accepted: false, true, 0, 1, nil
	targets,	-- [[ string ]]		accepted: "players", "npcs", "all", nil
	coords		-- [[ vector3 ]]	accepted: vector3, nil
)

-- EXAMPLES:
GetClosestPedAdvanced(1, 1, "players")
GetClosestPedAdvanced(true, false, "all", coords)
GetClosestPedAdvanced(0, true, "npcs", vector3(111.0, 222.0, 100.0))

I also made it like many FiveM natives, where boolean values support 1/0 as well as true/false and the fields are optional, so nil values will still work and it will use defaults.

None of this has been tested, as I am just writing this on the work PC between customers, so feel free to give it a shot and get back to me.

I also wrote this a while back. It could probably be expanded to include more options like the other one, but it’s an example of how you can have 1 function for many uses.

function GetClosestEntity(type, coords)
	local closestEntity = -1
	local closestDistance = -1
	if type then
		local entities = {}
		if type == "ped" or type == 1 then
			entities = GetGamePool("CPed")
		elseif type == "vehicle" or type == 2 then
			entities = GetGamePool("CVehicle")
		elseif type == "object" or type == 3 then
			entities = GetGamePool("CObject")
		end
		if coords then
			coords = type(coords) ~= "vector3" and vector3(coords.x, coords.y, coords.z) or coords
		else
			coords = GetEntityCoords(PlayerPedId())
		end
		for _, entity in ipairs(entities) do
			local distance = #(coords - GetEntityCoords(entity))
			if distance < closestDistance or closestDistance == -1 then
				closestEntity = entity
				closestDistance = distance
			end
		end
	end
	return closestEntity, closestDistance
end

-- USAGE:
GetClosestEntity(
	type,		-- [[ string or int ]]	accepted: "ped", "vehicle", "object", 1, 2, 3
	coords		-- [[ vector3 ]]		accepted: vector3, nil
)

-- EXAMPLES:
closestPed, closestDistance = GetClosestEntity("ped")
closestPed, closestDistance = GetClosestEntity(1, coords)

closestVehicle, closestDistance = GetClosestEntity("vehicle", coords)
closestVehicle, closestDistance = GetClosestEntity(2)

closestObject, closestDistance = GetClosestEntity("object", vector3(101.0, 102.0, 103.0))
closestObject, closestDistance = GetClosestEntity(3, coords)

Tried the bottom function with the string parameter for targets, the alive check does not work, as in it just accounts for dead peds no matter what the parameter is.

I dont think the los parameter works either, which is weird.

Players seems to grab your current players ped as well (ped id: -1)

But…

ped ~= playerPed`

This should prevent your ped from ever being returned as a result… However if it finds no other peds, it will return -1, since it is initiated as -1;

local closestPed = -1

You could always change that line to;

local closestPed = nil

Normally with these types of things, you check if the return value ~= -1 though, so that’s why I did it like that in the first place. Also aliveCheck should definitely work… The code is correct as far as I can tell. Let me think on it for a bit.

I did notice I did the targets wrong though. I put;

if aliveCheck and ped ~= playerPed and ((isPlayer and (targets == "all" or targets == "players")) or (not isPlayer and targets == "npcs")) then

But I think it should be;

if aliveCheck and ped ~= playerPed and (targets == "all" or (targets == "players" and isPlayer) or (targets == "npcs" and not isPlayer)) then

And I completely forgot to implement “los” into the function. It gets assigned but never used :joy: My bad!

Try testing this, please;

function GetClosestPedAdvanced(los, alive, targets, coords)
	local playerPed = PlayerPedId()
	local pedPool = GetGamePool("CPed")
	local coords = coords or GetEntityCoords(playerPed)
	local targets = targets or "all"
	local closestPed = nil
	local closestDist = -1
	for _, ped in pairs(pedPool) do
		local isPlayer = IsPedAPlayer(ped)
		local los = (los == true or los == 1) and HasEntityClearLosToEntity(playerPed, ped, 17) or true
		local aliveCheck = (alive == true or alive == 1) and (not IsPedDeadOrDying(ped)) or true
		if aliveCheck and los and ped ~= playerPed and (targets == "all" or (targets == "players" and isPlayer) or (targets == "npcs" and not isPlayer)) then
			local distance = #(coords - GetEntityCoords(ped))
			if closestDist == -1 or distance < closestDist then
				closestPed = ped
				closestDist = distance
			end
		end
	end
	return closestPed, closestDist
end

local closePed, distance = GetClosestPedAdvanced(1, 1, "all")
print(closePed, distance)

It will have to wait until I get home from work, sorry.
That will be in about 3.5 hours.
YouTube is blocked here on the work PCs. :joy:

ah, ill be away for the day as its getting late here

Hi, i’ve tried to use this with meta_target but somehow i cant find the function to get the npc hash is theres any functions i can use?

Did a rewrite following some of the good practices that @DrAceMisanthrope used in his code!

Hopefully there should not be any bugs.

The entity model hash is returned with GetEntityModel(npc), where npc is the ped entity.

So uh, @DrAceMisanthrope , what do you think of the code now?

Well done.

It’s good, but there was room for improvement again, in my opinion. Some of the changes will make it faster, some are just better practice when writing code and others are my personal preference. But the changes include;

  • Not getting PlayerPedId() multiple times
  • Not creating new vars when you can check/reassign the parameters
  • Not assigning the game pool to a var and looping, but instead looping from it directly
  • Not using global vars in a loop that aren’t needed that scope, which can alter expected results
  • Not looping with an integer index and assigning it to a new variable but instead looping in pairs
  • Cleaned up a lot of the check variables with less repetition of natives

It now looks like this. Feel free to give it a test;

function GetClosestEntity(entitytype, alive, targets, los, ignoreanimals, coords)

	-- Usage: GetClosestEntity("CPed", "alive", "any", "inlos", false, vector3(x, y, z))
	-- entitytype: "CPed", "CObject", "CVehicle", "CPickup"
	-- alive: "any", "alive", "dead" or (Vehicle only: "alivedriver", "deaddriver")
	-- targets: "any", "player", "npc" or (Vehicle only: "empty")
	-- los: "any", "notinlos", "inlos"
	-- ignoreanimals: true, false
	-- coords: vector3, {x, y, z} or nil for the player's coords

	local playerPed, closestEntity, closestDist, aliveCheck, losCheck = PlayerPedId(), -1, -1, false, false

	-- Get peds, objects, vehicles or pickups, according to the param
	entitytype = entitytype or "CPed"

	-- Get only alive or dead entities, according to the param
	alive = alive or "alive"

	-- Get entities that are only players or npcs or both, according to the param
	targets = targets or "any"

	-- Get only entities in or out of your line of sight, according to the param
	los = los or "inlos"

	-- Ignore entities that are animals according to the ignoreanimals param
	ignoreanimals = ignoreanimals or false

	-- Get specified coords or coords of current player if empty
	coords = coords or GetEntityCoords(playerPed)

	-- Loop through the game pool until closest entity is found.
	for index, entity in pairs(GetGamePool(entitytype)) do

		local driver = entitytype == "CVehicle" and GetPedInVehicleSeat(entity, -1) or -1

		-- Handles the alive param
		local aliveCheck =
			(alive == "any") or
			(alive == "alive" and not IsEntityDead(entity)) or
			(alive == "dead" and IsEntityDead(entity)) or
			(alive == "alivedriver" and driver > 0 and not IsEntityDead(driver)) or
			(alive == "deaddriver" and driver > 0 and IsEntityDead(driver))

		-- Handles the targets param
		local targetsCheck =
			(targets == "any") or
			(targets == "player" and IsPedAPlayer(driver == -1 and entity or driver)) or
			(targets == "npc" and driver == -1 and not IsPedAPlayer(entity)) or
			(targets == "npc" and driver > 0 and not IsPedAPlayer(driver)) or
			(targets == "empty" and driver == 0 and GetVehicleNumberOfPassengers(entity) == 0)

		-- Handles the los param
		local losCheck =
			(los == "any") or
			(los == "inlos" and HasEntityClearLosToEntity(playerPed, entity, 17)) or
			(los == "notinlos" and not HasEntityClearLosToEntity(playerPed, entity, 17))

		-- Handles the ignoreanimals param
		local ignoreanimalsCheck = (not ignoreanimals or (IsEntityAPed(entity) and GetPedType(entity) ~= 28))

		if entity ~= playerPed and aliveCheck and losCheck and targetsCheck and ignoreanimalsCheck then
			local distance = #(coords - GetEntityCoords(entity))
			if closestDist == -1 or distance < closestDist then
				closestEntity = entity
				closestDist = distance
			end
		end

	end

	return closestEntity, closestDist
end

local ent, dist = GetClosestEntity("CPed", "alive", "any", "inlos", false)
print(ent, dist)
if IsEntityAVehicle(ent) then
	print("Entity is a veh")
elseif IsEntityAPed(ent) then
	print("Entity is a ped")
elseif IsEntityAnObject(ent) then
	print("Entity is a object")
end
if IsEntityDead(ent) then
	print("Entity is dead")
else
	print("Entity is alive")
end

Tested the function throughly and it works!, only thing i have not been able to test is the “player” target parameter as i dont really have anyone to test that with.

Glad to hear! And thanks for crediting my help. It’s a pretty powerful function now, so good teamwork.

Create a shortcut of the FiveM.exe, right click and open the shortcut’s properties, then under “Target”, add -cl2 at the end. So it should be something like;

D:\Rockstar Games\FiveM\FiveM.exe -cl2

When you then launch this shortcut, it will create a new instance of the game. A 2nd client, if you will. So you can ALT+TAB between windows and test with yourself. I’ve not tried this yet but I found out about it and committed it to memory, cause I may need to one day when I can’t drag my girlfriend in to test with me :joy:

not work
:C

It does work.
Make sure you are doing -cl2 which is an L, not a 1 or something.
This is how anyone that connects with multiple clients, does it.
Unless they have a separate VM or PC to use.

Here is mine;
Target: C:\Users\Ace\AppData\Local\FiveM\FiveM.exe -cl2