Hello! A while ago, I made a zombie script using magnitude.
Full Code
local humanoid = script.Parent.Humanoid
local primary = script.Parent.PrimaryPart
local debounce = false
humanoid.Died:Connect(function()
wait(3)
script.Parent:Destroy()
end)
primary.Touched:Connect(function(hit)
if debounce == false then
if hit.Parent:FindFirstChildWhichIsA("Humanoid") and humanoid:GetState() ~= Enum.HumanoidStateType.Dead and hit.Parent.Name ~= script.Parent.Name then
debounce = true
hit.Parent.Humanoid:TakeDamage(10)
wait()
debounce = false
end
end
end)
while wait() do
for _, v in pairs(workspace:GetDescendants()) do
if v.Parent:FindFirstChild("Humanoid") and v:IsA("Part") and game.Players:GetPlayerFromCharacter(v.Parent) ~= nil then
if (primary.Position - v.Position).Magnitude <= 20 then
humanoid:MoveTo(v.Position)
end
end
end
end
Basically, if you would insert a script with this code inside a dummy, the script would check every descendant of workspace, and if there is a part that is 20 studs away from the primary part and is a child of a character, it would go to the character to attack it. Here’s a video I recorded showing what it does if you wanna see it in video:
while wait() do
for _, v in pairs(workspace:GetDescendants()) do
if v.Parent:FindFirstChild("Humanoid") and v:IsA("Part") and game.Players:GetPlayerFromCharacter(v.Parent) ~= nil then
if (primary.Position - v.Position).Magnitude <= 20 then
humanoid:MoveTo(v.Position)
end
end
end
end
Now this is the only part of the code that needs to improve because it checks every single descendant in workspace, and I think it checks a lot of parts; like too much. Any ideas on how I could improve?
Yeah, you’re looking at some massive server-side slowdowns if you run GetDescendants on the Workspace every time the task scheduler resumes this thread. Bonus points because it seems like this script is meant to be placed in an NPC instead of being a single script that handles all NPCs.
A better idea might be to just iterate through the game’s players provided your enemies do not need to target other NPCs as well, judging from the code. You can then check for distance between the players’ characters if they have them and move the zombie over.
Worth noting as well that your script doesn’t arbitrarily switch targets to the next closest target if any enter within its search range.
Here’s my recommendation for player searching. I usually run this check every 0.1-0.3 seconds or include it in a node in a behaviour tree (and oh please stop using while wait). Do not copy and paste it as the entirety of your code. This is a suggestion and rough code, it’s up to you to implement it properly if you do end up using the code or at least the structure of it.
local Players = game:GetService("Players")
local SEARCH_RANGE = 20
local function findNearestPlayer()
local nearbyPlayers = {}
for _, player in ipairs(Players:GetPlayers()) do
-- Skip handling this player if they have no character
if not player.Character then continue end
-- Get a HumanoidRootPart, skip handling if it's not there
local humanoidRootPart = player.Character:FindFirstChild("HumanoidRootPart")
if not humanoidRootPart then continue end
-- Get distance between the NPC and HRP, skip if it's over SEARCH_RANGE
local distanceAway = (primary.Position - humanoidRootPart.Position).Magnitude
if distanceAway <= SEARCH_RANGE then continue end
-- Track this player as they are nearby
nearbyPlayers[#nearbyPlayers + 1] = {RootPart = humanoidRootPart, Distance = distanceAway}
end
-- Prematurely return nothing if the table is empty
if #nearbyPlayers == 0 then
return nil
end
-- Sort the player table by their distances; ignore contested distances
table.sort(nearbyPlayers, function(first, second)
return first.Distance < second.Distance
end)
-- Return the first element in the table, which'd be the lowest distance root
return nearbyPlayers[1].RootPart
end
This function just returns the root, provided that’s all you need. Then it’s just a matter of moving the NPC towards the returned part if any is given.
local nearestRoot = findNearestPlayer()
if nearestRoot then
humanoid:MoveTo(nearestRoot.Position)
end
You may want to loop through the player list instead of the descendants in the workspace. That is pretty expensive on performance, especially when you add more than one of them. Just a tip.