Game crashes when too many enemies?

So, I use many different enemies in my game, each one with its own AI script.

The issue is, the game tends to crash for the players!
And it’s not consistent, some just lag, and some others crash instantly (and they’re forced to stop task)
I don’t know if the amount of AI scripts is what causes this (From what I’ve seen in Script Performance, it can reach up to 30% with around 10 to 15 enemies, and the game is supposed to have up to 30 in some scenarios.)

I’ve tried to investigate making a universal AI for the enemies, but I have no idea how to get started (Since every enemy has a different walk speed, and they also deal different damage.)

Here’s an example of a script that an enemy uses, with some unnecessary parts removed:

sp = script.Parent
AttackSpeed = 0.75
TimeToJump = math.random(1, 10)

variance = 2

local Damage = 10
attackrange = 3
sightrange = 500
SpeedDebounce = false

function raycast(spos,vec,currentdist)
	local hit2,pos2=game.Workspace:FindPartOnRay(Ray.new(spos+(vec*.01),vec*currentdist),script.Parent)
	if hit2~=nil and pos2 then
		if hit2.Parent==script.Parent and hit2.Transparency>=.8 or hit2.Name=="Handle" or string.sub(hit2.Name,1,6)=="Effect" or hit2.Parent:IsA("Hat") or hit2.Parent:IsA("Tool") or (not hit2.Parent:FindFirstChild("Humanoid") and hit2.CanCollide==false) then
			local currentdist=currentdist-(pos2-spos).magnitude
			wait()
			return raycast(pos2,vec,currentdist)
		end
	end
	return hit2,pos2
end

function waitForChild(parent,childName)
	local child=parent:findFirstChild(childName)
	if child then return child end
	while true do
		child=parent.ChildAdded:wait()
		if child.Name==childName then return child end
	end
end

-- ANIMATION

-- declarations

local Torso=waitForChild(sp,"Torso")
local Head=waitForChild(sp,"Head")
local RightShoulder=waitForChild(Torso,"Right Shoulder")
local LeftShoulder=waitForChild(Torso,"Left Shoulder")
local RightHip=waitForChild(Torso,"Right Hip")
local LeftHip=waitForChild(Torso,"Left Hip")
local Neck=waitForChild(Torso,"Neck")
local Humanoid=waitForChild(sp,"Humanoid")
local pose="Standing"
local hitsound=waitForChild(Torso,"HitSound")
local Anim = Humanoid:LoadAnimation(sp.AttackAnim)

function attack(AttackSpeed,attackpos)
	if AttackSpeed then
		local hit,pos=raycast(Torso.Position,(attackpos-Torso.Position).unit,attackrange)
		if hit and hit.Parent~=nil then
			local h=hit.Parent:FindFirstChild("Humanoid")
			if h and hit.Parent.Parent.Name ~= "Enemies" then
				local creator=sp:FindFirstChild("creator")
				if creator then
					if creator.Value~=nil then
						if creator.Value~=game.Players:GetPlayerFromCharacter(h.Parent) then
							for i,oldtag in ipairs(h:GetChildren()) do
								if oldtag.Name=="creator" then
									oldtag:remove()
								end
							end
							creator:clone().Parent=h
						else
							return
						end
					end
				end
				if Anim then Anim:Play(nil, nil, 2) end
				
				local Character = h.Parent
				if Character:FindFirstChild("IsATower") then
					h:TakeDamage(math.floor(Damage))
				end
				hitsound:Play()
				wait(AttackSpeed)
			end
		end
	end
end

function populatehumanoids(model)
	if model.ClassName=="Humanoid" then
		if model.Parent.Name ~= sp.Name and model.Parent.Parent.Name ~= "Enemies" then
			table.insert(humanoids,model)
		end
	end
	for i2, model2 in ipairs(model:GetChildren()) do
		populatehumanoids(model2)
	end
end

while sp and Humanoid and Humanoid.Health > 0 do
	local TimeLeft = game.Workspace.Values.TimeLeft
	wait(0.5)
	humanoids={}
	populatehumanoids(game.Workspace)
	closesttarget=nil
	closestdist=sightrange
	local creator=sp:FindFirstChild("creator")
	for i,h in ipairs(humanoids) do
		if h and h.Parent~=nil then
			if h.Health>0 and h.Parent ~= sp then
				local plr=game.Players:GetPlayerFromCharacter(h.Parent)
				if creator==nil or plr==nil or creator.Value~=plr then
					local t=h.Parent:FindFirstChild("HumanoidRootPart")
					local towercheck = h.Parent:FindFirstChild("IsATower")
					if t ~= nil and towercheck ~= nil then
						local dist=(t.Position-Torso.Position).magnitude
						if dist<closestdist then
							closestdist=dist
							closesttarget=t
							
						end
					end
				end
			end
		end
	end
	if closesttarget~=nil then
		
		if TimeLeft.Value > 0 then
			if SpeedDebounce == true then
				SpeedDebounce = false
				script.Parent.Humanoid.WalkSpeed = script.Parent.Humanoid.WalkSpeed * 2
			end
			-- Move and attack towards the nearest player
		elseif TimeLeft.Value <= 0 then
			if SpeedDebounce == false then
				SpeedDebounce = true
				script.Parent.Humanoid.WalkSpeed = script.Parent.Humanoid.WalkSpeed / 2
			end
			-- Move towards the main objective and attack any player that gets in range
		end
	else
		if TimeLeft.Value <= 0 then
			if SpeedDebounce == false then
				SpeedDebounce = true
				script.Parent.Humanoid.WalkSpeed = script.Parent.Humanoid.WalkSpeed / 2
			end
			-- Move towards the main objective and attack any player that gets in range
		end
	end
	if TimeToJump == 2 and Humanoid.MoveDirection.magnitude <= 0 then
		Humanoid.Jump = true
	end
	TimeToJump = math.random(1, 10)
end
1 Like

Have you used any performance tools that are available to you?
I ask as they may indicate a memory usage problem.