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
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.
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.
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.
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
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
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)