How would I make a more efficient version of this?

Hello. How could I make this target searching algorithm more performant.

I’ve been thinking a while on how I could optimise this very unoptimised algorithm.

local function findTarget(pos, distance)
	local closestTorso

	for _, obj in ipairs(workspace:GetChildren()) do
		if obj:IsA("Model") then
			local foundTorso = obj:FindFirstChild("HumanoidRootPart") or
			local humanoid = obj:FindFirstChildOfClass("Humanoid")

			if humanoid and foundTorso and humanoid.Health > 0 and obj ~= script.Parent and obj.Name ~= script.Parent.Name then
				if (foundTorso.Position - pos).Magnitude < distance then
					closestTorso = FoundTorso
					distance = (closestTorso.Position - pos).magnitude
				end
			end
		end
	end

	return closestTorso
end

The worse part is that I have to recalculate all of this quite frequently, like 0.5 to 5 seconds.

I have thought of a few solutions for this:

  • Caching the models that have humanoids and updating that cache a certain amount of time. (Would still once in a while have to do an expensive calculation)
  • Cache the models that have humanoids and for other models have a .ChildAdded connection to check if humanoids are added and insert them in to the cache. (Would require having lots of connections)

The problem with option 1 is that I still have to sometimes recalculate the expensive check.
The problem with option 2 is that I have to have a .ChildAdded for every model.

I think I’m going to pick option 2.

Also I’d still prefer better optimisation on how to handle this.

I heard that you can use CollectionService for this, simply add a tag to all the enemies and loop trough them, otherwise it’s good o think
You would do this with a script in ServerScriptService and make a Variable for it in your script, then in the ServerScriptService script for every ChildAdded check if it’s the right one and if it is add the tag to it
Plus you can do the same with another script to control all NPCs

1 Like

It isn’t very unoptimized, but as said here I’d recommend caching the results and using ChildAdded.

If you want to go further, you can control all your NPCs using one script in the server and perform one check for all of them.

1 Like

Maybe add a child inside the characters and check if the model has that child, then find the closest torso.

It looks fine to me, there is nothing really wrong with it besides maybe a couple of changes you can do to make the search faster.

  1. Getting Players / Objects
    If you are trying to get a specific Entity, you should be keeping track of them so you can get them very fast, if you are getting just players, you should use game.Players:GetPlayers, and Look through their characters for their distance, Alternatively you can use CollectionService to tag a specific Entity, and have the Mob look for it using CollectionService:GetTagged, Instead of searching through Hundreds, and maybe Thousands of Instances, this would make the Search faster.

  2. Calculating the Distance
    From what I see here, you are getting the Distance between the Object’s Twice, you can just do it once like this:

local Magnitude = (closestTorso.Position - pos).Magnitude

if Magnitude < Distance then
    closestTorso = FoundTorso
    distance     = Magnitude
end

Which there might be a slight difference in Speed.

  1. Arguments
    Instead of updating a function Argument, you should Instead create a Variable as changing the Argument will not change the provided Argument for the function, so like this:
local maxDistance  = distance -- so you can check for objects within that distance
-- the 'maxDistance' variable is the one you should update for the Distance
local closestTorso -- The Normal one from your Original Script

1 Like

Yeah thats what I’ll probably end up doing.

One other thing is that I thought of doing is to to cache the closest humanoids so it doesn’t have to check all humanoids inside workspace. Although that probably would be a bit hard so I’ll probably just decide to multi thread them instead.

But yeah the caching of all workspace Humanoids with a .ChildAdded check in combination of multithreading is probably the path I’ll take.


Yeah I would do these if possible. Unfortunately due to a few reasons I can’t do a central entity managment system. Thanks for the help though :slight_smile:

Just so if anyone is curious here is the script I came up with:

local humanoids = {}

local function onObjectAdded(obj)
	if obj:IsA("Model") and obj ~= script.Parent and obj.Name ~= script.Parent.Name then
		local humanoid = obj:FindFirstChildOfClass("Humanoid")
		local connection1, connection2, connection3

		if humanoid and not table.find(humanoids, humanoid) then
			table.insert(humanoids, humanoid)
		end

		connection1 = obj.ChildAdded:Connect(function(child)
			if not humanoid and child:IsA("Humanoid") then
				humanoid = child
				table.insert(humanoids, humanoid)
			end
		end)

		connection2 = obj.ChildRemoved:Connect(function(child)
			if child == humanoid and table.find(humanoids, humanoid) then
				table.remove(humanoids, table.find(humanoids, humanoid))
				humanoid = obj:FindFirstChildOfClass("Humanoid")

				if humanoid then
					table.insert(humanoids, humanoid)
				end
			end
		end)

		connection3 = obj.AncestryChanged:Connect(function()
			if obj.Parent ~= workspace then
				connection1:Disconnect()
				connection2:Disconnect()
				connection3:Disconnect()
			end
		end)
	end
end

local function findTarget(pos, distance)
	local foundPart

	for _, humanoid in ipairs(humanoids) do
		local rootPart = humanoid.RootPart or humanoid.Parent:FindFirstChild("HumanoidRootPart")

		if rootPart and humanoid.Health > 0 and (rootPart.Position - pos).Magnitude < distance then
			foundPart, distance = rootPart, (rootPart.Position - pos).Magnitude
		end
	end

	return foundPart
end

for _, v in ipairs(workspace:GetChildren()) do
	onObjectAdded(v)
end

workspace.ChildAdded:Connect(onObjectAdded)

Why can’t you?
Sorry, I’m just corious, if there’s a moment where you can’t do this I would like to know it

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.