How to detect the closest Mob within a Tower / NPCs radius

So I am making a Tower Defense themed game, and I want the NPC or the Tower In this case to Lock onto the closest mob, right now the Tower is locking in Order, so If I were to spawn in 4 Mobs, the Tower would only lock on to the first, than 2nd than 3rd and so on, and I really got no solution this is my method below :

> local FirstMob = nil

System.CheckRadius = function(Tower,Mob)
	local MainPoint = workspace:WaitForChild("EndPoint")
	local _Data = System.TowerData(Tower)
	local TowerRoot = Tower:FindFirstChild("HumanoidRootPart")
	local MobRoot = Mob:FindFirstChild("HumanoidRootPart")
	--local Difference = (MobRoot.CFrame:toObjectSpace(TowerRoot.CFrame).p.magnitude)
	local Difference = (MobRoot.Position - TowerRoot.Position).magnitude
	if Difference <= _Data["Range"] / 2 then
		FirstMob = MobRoot
		print("In Range")
		if (MobRoot.Position - MainPoint.Position).magnitude > (FirstMob.Position - MainPoint.Position).magnitude then
			FirstMob = MobRoot
		end
		print("OK !")
		Tower.Tool.Shot:Play()
		System.DamageMob(_Data["Damage"],Mob)
		GiveCash(Tower.Owner.Value,_Data["Damage"])
		Tower:SetPrimaryPartCFrame(CFrame.new(TowerRoot.Position,FirstMob.Position * Vector3.new(1,0,1) +TowerRoot.Position * Vector3.new(0,1,0)))
		 wait(_Data["Rate"])
		return true	
	end
end

System.SearchMobs = function(Tower)
	local MobFolder = workspace:WaitForChild("Mobs");
	for _,Mob in next,(MobFolder:GetChildren()) do
		if Mob:FindFirstChild("HumanoidRootPart") then
			System.CheckRadius(Tower,Mob)
		end
	end
end

System.RunMain = function(Tower)
	while true do wait()
		System.SearchMobs(Tower)
	end
end

You are forgetting to check the distance from tower to all the mobs and then checking which one has a smaller distance. right now you just loop through mobs and check radius on the one and then ignore the rest of the mobs until the first one is dealt with.

Fix your CheckRadius function to not worry about a distance check. So you can rename it to Attack or something as it wont be checking radius.

Your distance check will now be in SearchMobs and will look like this:

local searchRadius = 1000 -- the tower's range
System.SearchMobs = function(Tower)
	local nearestMob = nil
	local distance = searchRadius
	local MobFolder = workspace:WaitForChild("Mobs");
	local TowerRoot = Tower:FindFirstChild("HumanoidRootPart")
	if not TowerRoot then print("Tower Root Missing.") return end
	for _,Mob in next,(MobFolder:GetChildren()) do
		local MobRoot = Mob:FindFirstChild("HumanoidRootPart")
		if MobRoot then
			if (MobRoot.Position - TowerRoot.Position).magnitude < distance then
				nearestMob = MobRoot -- or MobRoot.Parent
				distance = (MobRoot.Position - TowerRoot.Position).magnitude
			end
		end
	end
	if nearestMob then
		System.CheckRadius(Tower,nearestMob) -- should rename CheckRadius to something like "Attack"
	end
end
2 Likes

Okay so no loop would be needed ? Just this ?

There is a loop still, since you have to loop through all the mobs. But what I did in the example was add a distance comparison between the tower and each mob and then letting the tower deal with the mob with the shortest distance which is what your original code did not handle correctly as it was just looping through all mobs and attacking the first one it looped that was within radius of the tower but not the closest mob to the tower of all the mobs.