I have a script that can control multiple npcs at once, and i manage to get over 100 npcs with a little lag, the console shows about 18~20% rate on the scripts tab. But how do i optimize this even more?
I did read some articles about the optimizing npcs by disable some unused states, remove some humanoid properties thats not needed, but it still not reduce that much lags from many npcs.
Script:
local CollectionService = game:GetService("CollectionService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local stunmodule = require(game.ReplicatedStorage.EffectsDebuff)
local AI = require(game.ReplicatedStorage.SimplePath)
local muchachohitbox = require(game.ReplicatedStorage.MuchachoHitbox)
local dmggoal = require(game.ReplicatedStorage.DamageCalculator)
local fastskill = require(game.ReplicatedStorage.Skill)
local ZOMBIE_TAG = "zombie"
local ATTACK_RADIUS = 5
local DAMAGE = 5.5
local WALKER_ATTACK_COOLDOWN = 1.25
local FAST_ATTACK_COOLDOWN = 3
local function getPlayersInWorkspace()
local playersInWorkspace = {}
for _, player in ipairs(Players:GetPlayers()) do
local character = player.Character
if character and character:FindFirstChild("HumanoidRootPart") then
table.insert(playersInWorkspace, character)
end
end
return playersInWorkspace
end
local function getNearestPlayer(zombie)
local root = zombie:FindFirstChild("HumanoidRootPart")
if not root then return nil end
local closestPlayer, closestDistance = nil, 800
for _, character in ipairs(getPlayersInWorkspace()) do
local targetRoot = character:FindFirstChild("HumanoidRootPart")
local targetHumanoid = character:FindFirstChild("Humanoid")
if targetRoot and targetHumanoid and targetHumanoid.Health > 0 then
local distance = (root.Position - targetRoot.Position).Magnitude
if distance < closestDistance then
closestPlayer, closestDistance = targetRoot, distance
end
end
end
return closestPlayer
end
local function cloneStuffs(instance, base)
local clonedInstance = instance:Clone()
clonedInstance.Parent = base
if clonedInstance:IsA("Sound") then
clonedInstance:Play()
game.Debris:AddItem(clonedInstance, clonedInstance.TimeLength)
end
end
local function attackStunZombie(zombie, target)
local canAttack = zombie:GetAttribute("CanAttack")
local lastAttackTime = zombie:GetAttribute("LastAttackTime") or 0
local root = zombie:FindFirstChild("HumanoidRootPart")
local humanoid = zombie:FindFirstChild("Humanoid")
if not root or not humanoid or humanoid.Health <= 0 or not canAttack then return end
if os.clock() - lastAttackTime < FAST_ATTACK_COOLDOWN then return end
local attackAnim = root:FindFirstChild("punch")
local attackTrack = attackAnim and humanoid:LoadAnimation(attackAnim)
zombie:SetAttribute("LastAttackTime", os.clock())
if attackTrack then attackTrack:Play() end
local pushForce = Instance.new("LinearVelocity")
local attachment = Instance.new("Attachment",root)
pushForce.Attachment0 = attachment
pushForce.MaxForce = 10000
pushForce.VectorVelocity = (target.Position - root.Position).Unit * 70
pushForce.Parent = root
game.Debris:AddItem(pushForce, 0.15)
game.Debris:AddItem(attachment,0.3)
local fasthitbox = muchachohitbox.CreateHitbox()
fasthitbox.Size = zombie:FindFirstChild("Head").Size
fasthitbox.CFrame = zombie:FindFirstChild("Head")
fasthitbox.Visualizer = true
fasthitbox.OverlapParams = OverlapParams.new({
FilterType = Enum.RaycastFilterType.Exclude,
FilterDescendantsInstances = CollectionService:GetTagged(ZOMBIE_TAG)
})
local debounceTable, hitboxActive = {}, false
task.spawn(function()
fasthitbox.Touched:Connect(function(hit, hum)
print(hit,hum,hit.Parent)
if hitboxActive then return end
local hitParent = hit:FindFirstAncestorOfClass("Model")
if hitParent and not hitParent:GetAttribute("zombie") and hum and hum.Health > 0 and not hitParent:GetAttribute("Tackling") then
if not debounceTable[hitParent] then
hitboxActive = true
debounceTable[hitParent] = true
fastskill:Maul(zombie, hitParent)
task.delay(0.65, function()
debounceTable[hitParent], hitboxActive = nil, false
end)
end
end
end)
end)
task.delay(0.3, function()
fasthitbox:Start()
task.wait(0.35)
if getmetatable(fasthitbox) then fasthitbox:Stop() end
end)
end
local function attackZombie(zombie, target)
local root, humanoid = zombie:FindFirstChild("HumanoidRootPart"), zombie:FindFirstChild("Humanoid")
if not root or not humanoid then return end
local lastAttackTime = zombie:GetAttribute("LastAttackTime") or 0
local canAttack = zombie:GetAttribute("CanAttack")
if os.clock() - lastAttackTime < WALKER_ATTACK_COOLDOWN or not canAttack then return end
zombie:SetAttribute("LastAttackTime", os.clock())
local attackAnim = root:FindFirstChild("punch")
local attackAnim2 = root:FindFirstChild("punch2")
local attackTrack = attackAnim and humanoid:LoadAnimation(attackAnim)
local attackTrack2 = attackAnim2 and humanoid:LoadAnimation(attackAnim2)
local randomizeattack = math.random(1,2)
if randomizeattack == 1 then
if attackTrack then attackTrack:Play() end
elseif randomizeattack == 2 then
if attackTrack2 then attackTrack2:Play() end
end
local defaulthitbox = muchachohitbox.CreateHitbox()
defaulthitbox.Size = Vector3.new(5,6,5)
defaulthitbox.Offset = CFrame.new(0,0,-1)
defaulthitbox.CFrame = root
defaulthitbox.Visualizer = false
local debounceTable, hitdb = {}, false
task.spawn(function()
defaulthitbox.Touched:Connect(function(target, hum)
if hitdb then return end
local targetCharacter, targetHumanoid = target.Parent, target.Parent:FindFirstChild("Humanoid")
local victimstats = targetCharacter:FindFirstChild("Stats")
local defensive = victimstats and victimstats.Defensive.Value
if targetHumanoid and targetHumanoid.Health > 0 and humanoid.Health > 0 and not targetCharacter:GetAttribute("zombie") then
if not debounceTable[targetCharacter] then
hitdb = true
debounceTable[targetCharacter] = true
local finaldamage = dmggoal:Calculate(DAMAGE,1,defensive,nil,targetCharacter)
targetHumanoid:TakeDamage(finaldamage)
cloneStuffs(root:FindFirstChild("hit"), targetHumanoid)
targetCharacter:SetAttribute("infectedrate", (targetCharacter:GetAttribute("infectedrate") or 0) + 1.5)
task.wait(0.5)
debounceTable[targetCharacter], hitdb = nil, false
end
end
end)
end)
defaulthitbox:Start()
task.delay(0.35, function()
if getmetatable(defaulthitbox) then defaulthitbox:Stop() end
end)
end
local function updateZombie(zombie)
local humanoid, root = zombie:FindFirstChildOfClass("Humanoid"), zombie:FindFirstChild("HumanoidRootPart")
if not humanoid or not root then return end
local path = AI.new(zombie, {
AgentRadius = 3,
AgentHeight = 6,
AgentCanJump = true,
AgentJumpHeight = 7,
AgentMaxSlope = 45,
WaypointSpacing = 50,
Costs = { Water = 1, Neon = 1 }
})
humanoid.Died:Connect(function()
if CollectionService:HasTag(zombie, ZOMBIE_TAG) then
CollectionService:RemoveTag(zombie, ZOMBIE_TAG)
end
if path.Status == AI.StatusType.Active then
path:Stop()
end
end)
humanoid:SetStateEnabled(Enum.HumanoidStateType.Climbing,false)
humanoid:SetStateEnabled(Enum.HumanoidStateType.Swimming,false)
humanoid:SetStateEnabled(Enum.HumanoidStateType.Seated,false)
while task.wait(0.1) do
if humanoid.Health <= 0 then break end
local target = getNearestPlayer(zombie)
if not target then continue end
local distance = (root.Position - target.Position).Magnitude
task.spawn(function()
if distance <= ATTACK_RADIUS and zombie:GetAttribute("walker") then
attackZombie(zombie, target)
elseif distance <= 15 and zombie:GetAttribute("fast") then
attackStunZombie(zombie, target)
end
end)
task.spawn(function()
if distance < 20 then
humanoid:MoveTo(target.Position)
if path.Blocked then
path:Run(target)
end
else
path:Run(target)
end
end)
end
end
CollectionService:GetInstanceAddedSignal(ZOMBIE_TAG):Connect(function(zombie)
zombie:SetAttribute("LastAttackTime", os.clock())
task.spawn(function()
updateZombie(zombie)
end)
print("spawned")
end)
for _, zombie in ipairs(CollectionService:GetTagged(ZOMBIE_TAG)) do
zombie:SetAttribute("LastAttackTime", os.clock())
task.spawn(function()
updateZombie(zombie)
end)
end
1 post - 1 participant