Finding Nearest Target To Zombie

Hey there,

So I am currently working on making a zombie movement script. The purpose of this script is to get the zombie to follow the nearest player to it.

What seems to be happening is the zombie will follow one player, and then immidiately go to the next player even if they are not the nearest player to the zombie. I might be missing something very simple, but any help would be appreciated.

local hum = script.Parent.Humanoid
local humRoot = script.Parent.HumanoidRootPart

local rs = game:GetService("RunService")

local function DetermineTarget()
	local nearestTarget = nil
	
	for i,v in pairs(game.Players:GetPlayers()) do
		if v.Character then
			if v.Character.Humanoid.Health >= 1 then
				local Distance = (v.Character.HumanoidRootPart.Position - humRoot.Position).Magnitude

				if nearestTarget == nil then
					nearestTarget = v
				else
					if (nearestTarget.Character.HumanoidRootPart.Position - humRoot.Position).Magnitude <= (v.Character.HumanoidRootPart.Position - humRoot.Position).Magnitude then
						nearestTarget = v
					end
				end
			end
		end
	end
	
	return nearestTarget
end

while wait() do
	local target = DetermineTarget()
	
	if target == nil then
		print("No target found")
	else
		hum:MoveTo(target.Character.HumanoidRootPart.Position,target.Character.HumanoidRootPart)

		hum.MoveToFinished:Wait()
	end
end

Thank you for your help!

1 Like

I’ve learned thanks to @Forummer, that only == and ~= operators are valid when comparing strings

1 Like

So it appears that the issue is that the zombie is trying to go for eveyrone in the game. I printed the target after removing the wait for movetofinished, and it spams each username in a row. It appears that it cannot determine the nearest target. How do you think I could fix this?

1 Like

you need to check against the distance of the last target set to do this you can use a neerestDistance variable before you for loop like below

local hum = script.Parent.Humanoid
local humRoot = script.Parent.HumanoidRootPart

local rs = game:GetService("RunService")

local function DetermineTarget()
	local nearestTarget = nil
	local nearestDistance = math.huge -- sets at a very larger number.  use this to make sure its closer than last target
	
	for i,v in ipairs(game.Players:GetPlayers()) do  -- i use ipairs if i rem right its abit faster
		if v.Character then
			if v.Character.Humanoid.Health >= 1 then
				local Distance = (v.Character.HumanoidRootPart.Position - humRoot.Position).Magnitude
				
				if Distance < nearestDistance then  -- if the current player distance is less than the previous nearestDistance then set target 
					nearestTarget = v
                    nearestDistance = Distance  -- set nearestDistance to new distance..
				end
			end
		end
	end
	return nearestTarget
end

while wait() do
	local target = DetermineTarget()
	
	if target then   -- cleaned this to not use not...
		hum:MoveTo(target.Character.HumanoidRootPart.Position,target.Character.HumanoidRootPart)
		hum.MoveToFinished:Wait()
	else
		print("No target found")
	end
end
1 Like

So I had to replace math.huge to 1, because it threw an error. Now, the script is just printing β€œNo target found” forever

local hum = script.Parent.Humanoid
local humRoot = script.Parent.HumanoidRootPart

local rs = game:GetService("RunService")

local function DetermineTarget()
	local nearestTarget = nil
	local nearestDistance = 1  -- sets at a very larger number.  use this to make sure its closer than last target

	for i,v in ipairs(game.Players:GetPlayers()) do  -- i use ipairs if i rem right its abit faster
		if v.Character then
			if v.Character.Humanoid.Health >= 1 then
				local Distance = (v.Character.HumanoidRootPart.Position - humRoot.Position).Magnitude

				if Distance < nearestDistance then  -- if the current player distance is less than the previous nearestDistance then set target 
					nearestTarget = v
				end
			end
		end
	end
	
	return nearestTarget
end

while wait() do
	local target = DetermineTarget()

	if target then   -- cleaned this to not use not...
		hum:MoveTo(target.Character.HumanoidRootPart.Position,target.Character.HumanoidRootPart)
		hum.MoveToFinished:Wait()
	else
		print("No target found")
	end
end
2 Likes

Most likely because 1 is too low, if you print the calculated distance even if your as close as possible to the zombie it will be greater than 1.

1 Like

An easier method to find the closest target is to use table.sort:

Old (Ignore)
local character = script.Parent
local humanoid = character:WaitForChild('Humanoid')
local humanoidRootPart = character:WaitForChild('HumanoidRootPart')
local targets = workspace:WaitForChild('Targets')

local alive = true
local died
died = humanoid.Died:Connect(function()
	alive = nil
	died:Disconnect()
	died = nil
end)

while alive do
	task.wait()
	local near = targets:GetChildren()
	local sort = function(a,b)
		return (a.Position-humanoidRootPart.Position).Magnitude < (b.Position-humanoidRootPart.Position).Magnitude
	end
	table.sort(near,sort)
	near = near[1]
	print(near)
end

This would be a bit more efficient actually:

while alive do
	task.wait()
	local near = targets:GetChildren()
	local list = {}
	for _,target in pairs(near) do
		table.insert(list,{Target=target,Distance=(target.Position-humanoidRootPart.Position).Magnitude})
	end
	local sort = function(a,b)
		return a.Distance < b.Distance
	end
	table.sort(list,sort)
	local target = list[1].Target
	print(target)
end

That way it only calculates the magnitude for each target once, rather than every target per target.
βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»βΈ»

local character = script.Parent
local humanoid = character:WaitForChild('Humanoid')
local humanoidRootPart = character:WaitForChild('HumanoidRootPart')
local targets = workspace:WaitForChild('Targets')

local alive = true
local died
died = humanoid.Died:Connect(function()
	alive = nil
	died:Disconnect()
	died = nil
end)

local sortDistance = function(a,b)
	return a.Distance < b.Distance
end

while alive do
	task.wait()
	local near = targets:GetChildren()
	local list = {}
	for _,target in pairs(near) do
		table.insert(list,{Target=target,Distance=(target.Position-humanoidRootPart.Position).Magnitude})
	end
	table.sort(list,sortDistance)
	local target = list[1].Target
	print(target)
end
1 Like

I will try that, and let you know how it works.

1 Like

Aside from @MightyDantheman’s solution, your code was fine, you just needed to set the nearestDistance variable to the distance of the closest target (you were already calculating this). I also made some other changes and put some comments.

local hum = script.Parent.Humanoid
local humRoot = script.Parent.HumanoidRootPart

local rs = game:GetService("RunService")

local function DetermineTarget()
	local nearestTarget = nil
	local nearestDistance = math.huge -- sets at a very larger number.  use this to make sure its closer than last target

	for i,v in game.Players:GetPlayers() do -- you can remove pairs completely and just pass a table and it will automatically determine which is better
        local character = v.Character
        local humanoid = character and character:FindFirstChildOfClass("Humanoid")

        if humanoid and humanoid.Health > 0 then
		    local Distance = (character.HumanoidRootPart.Position - humRoot.Position).Magnitude

			if Distance < nearestDistance then -- if the current player distance is less than the previous nearestDistance then set target 
                nearestTarget = v
                nearestDistance = Distance -- remember to set the nearestdistance when you set a new target
			end
		end
	end
	
	return nearestTarget
end

while true do
	local target = DetermineTarget()

	if target then -- cleaned this to not use not...
		hum:MoveTo(target.Character.HumanoidRootPart.Position, target.Character.HumanoidRootPart)
		hum.MoveToFinished:Wait()
	else
		print("No target found")
	end

    task.wait() -- it is bad practice to put wait() as the condition for the while loop
end
3 Likes

what was the error from math.huge and the distance has to be very large at the start

sorry i miss typed on that it should be just math.huge not with the ()

1 Like