How to get the Closest Target?

I have a script that gets a random target but I want it to get the closest target. What should I do to achieve that?

local currentTarget = nil
local npcs = workspace:WaitForChild("Npcs")

local function castRay(target)
	if target.PrimaryPart ~= nil then
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {npcs}
		raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
		raycastParams.IgnoreWater = true

		local rayOrigin = character.PrimaryPart.Position
		local rayDestination = target.PrimaryPart.Position
		local rayDirection = (rayDestination - rayOrigin)
		local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		if raycastResult then
			local distance = math.floor(raycastResult.Distance)
			if distance <= range then
				currentTarget = target
				return true, raycastResult.Position, distance
			end
		end
	end
	currentTarget = nil
	return false, nil, nil
end

local function findTarget()
	local checkRay, position, distance = nil, nil, nil

	for i,v in ipairs(npcs:GetChildren()) do
		checkRay, position, distance = castRay(currentTarget or v)

		if currentTarget ~= nil then
			local savedTarget = currentTarget

			local targetRoot = currentTarget.PrimaryPart
			local human = currentTarget:FindFirstChildWhichIsA("Humanoid")

			if human and human.Health > 0 then
				if human and human.Health <= 0 then
					currentTarget = nil
				end

				if distance > range then
					currentTarget = nil
				end

				if player and targetRoot ~= nil and checkRay == true and distance <= attackRange then
					local characterRoot = character.PrimaryPart
					characterRoot.CFrame = CFrame.lookAt(characterRoot.Position, Vector3.new(position.X, characterRoot.Position.Y, position.Z))
				end
			end
		end
	end
end

To get the closest player you need to follow these steps:

local players = game.Players:GetChildren() -- this will make a table (list) of all the players in the game
local initialPos = humanoidRootPart.Position -- This would be the initial position in which you are comparing distances
local nearest,distance = players[1],9999999999999 -- This is some default values so we save time
for i,v in pairs(players) do --Repeat the same function for each player in the list
    local magnitude = (v.Character.HumanoidRootPart.Position - initialPos).Magnitude -- This will get the distance between the current players character and the intial position we are comparing it with
    if magnitude < distance then -- checking if the current players distance is smaller than the current best
        nearest = v -- Setting the new better values if the player IS closer
        distance = magnitude
    end
end
print(nearest) -- nearest will be the nearest player, you can then get his character with: nearest.Character

If you dont want players, just change the list at the start with the list of npcs that you want to compare. Then remove all the player.Character since you already have the character in the list

1 Like

So something like this?

local currentTarget = nil
local npcs = workspace:WaitForChild("Npcs")

local function castRay(target)
	if target.PrimaryPart ~= nil then
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {npcs}
		raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
		raycastParams.IgnoreWater = true

		local rayOrigin = character.PrimaryPart.Position
		local rayDestination = target.PrimaryPart.Position
		local rayDirection = (rayDestination - rayOrigin)
		local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		if raycastResult then
			local distance = math.floor(raycastResult.Distance)
			if distance <= range then
				currentTarget = target
				return true, raycastResult.Position, distance
			end
		end
	end
	currentTarget = nil
	return false, nil, nil
end

local function findTarget()
	local checkRay, position, distance = nil, nil, nil
	local nearestDistance = math.huge

	for i,v in ipairs(npcs:GetChildren()) do
		local targetRoot = v.PrimaryPart
		local human = v:FindFirstChildWhichIsA("Humanoid")

		if targetRoot ~= nil and human and human.Health > 0 then
			checkRay, position, distance = castRay(currentTarget or v)

			if checkRay == true then
				local dist = (character.HumanoidRootPart.Position - targetRoot.Position).Magnitude

				if distance < nearestDistance then
					currentTarget = v
					nearestDistance = distance
				end

				if currentTarget ~= nil then
					local savedTarget = currentTarget

					local targetRoot = currentTarget.PrimaryPart
					local human = currentTarget:FindFirstChildWhichIsA("Humanoid")

					if distance > range then
						currentTarget = nil
					end
				end
			end
		else
			currentTarget = nil
		end
	end
end

Yes but your npc variable isnt a list.
Is it a folder? If so do
local npcfolder = workspace:WaitForChild(“Npcs”)
local npcs = npcfolder:GetChildren()

Not a list, the npcs are in a folder in workspace.

Alright so copy the code here instead of your old npcs variable

Seems to not be working that good.

local currentTarget = nil

local function castRay(target)
	if target.PrimaryPart ~= nil then
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {npcs}
		raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
		raycastParams.IgnoreWater = true

		local rayOrigin = character.PrimaryPart.Position
		local rayDestination = target.PrimaryPart.Position
		local rayDirection = (rayDestination - rayOrigin)
		local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		if raycastResult then
			local distance = math.floor(raycastResult.Distance)
			if distance <= range then
				currentTarget = target
				return true, raycastResult.Position, distance
			end
		end
	end
	currentTarget = nil
	return false, nil, nil
end

local function findTarget()
	local checkRay, position, distance = nil, nil, nil
	local nearestDistance = math.huge

	for i,v in ipairs(npcs:GetChildren()) do
		local targetRoot = v.PrimaryPart
		local human = v:FindFirstChildWhichIsA("Humanoid")

		if targetRoot ~= nil and human and human.Health > 0 then
			checkRay, position, distance = castRay(currentTarget or v)

			if checkRay == true then
				local dist = (character.HumanoidRootPart.Position - targetRoot.Position).Magnitude

				if distance < nearestDistance then
					currentTarget = v
					nearestDistance = distance
				end

				if currentTarget ~= nil then
					local savedTarget = currentTarget

					local targetRoot = currentTarget.PrimaryPart
					local human = currentTarget:FindFirstChildWhichIsA("Humanoid")

					if distance > range then
						currentTarget = nil
					end

					if player and distance <= attackRange then

					else
						humanoid.WalkSpeed = WalkSpeed
					end
				end
			end
		else
			currentTarget = nil
		end
	end
end

Stop sending the full code, just send the function that checks for the nearest

Raycasting is unnesecary for this.

local closestTarget = nil
for i, v in pairs(targets) do
    if closestTarget == nil then
        closestTarget = v
    end
    local d = v.Position - (origin.Position).Magnitude
    local closestD = (closestTarget.Position - origin).Magnitude
    if d < closestD then
        closestTarget = v
    end
end

I wrote this without studio. Probably the worst way to achieve this, but here you go.

Just so you know, this script will not work properly.

look into math.huge

The raycast is to check if the npc is behind a wall.

1 Like

Then run a raycast to all targets and use the same logic as my code. Should work.