Any optimization ideas for my enemy NPC AI scripts?

The enemy NPCs all use Actors instead of models, to try and optimize them. The game still freezes every few seconds (since there are a large amount of enemies at a given time, maximum being 20 enemies of a certain type. Only one type currently exists, zombies.) Any ideas on how to increase performance, or better use actors?

local myHuman = script.Parent:WaitForChild("Monster")
local myRoot = script.Parent:WaitForChild("HumanoidRootPart")
local lowerTorso = script.Parent:WaitForChild("LowerTorso")
--animations
local grab = script.Parent:WaitForChild("Grab")
local grabAnim = myHuman:LoadAnimation(grab)
grabAnim.Priority = Enum.AnimationPriority.Action
--sounds
local grabSound = myRoot:WaitForChild("Attack")
local screamSound = myRoot:WaitForChild("Scream")

function walkRand()
	local xRand = math.random(-50, 50)
	local zRand = math.random(-50, 50)
	local goal = myRoot.Position + Vector3.new(xRand, 0, zRand)

	local path = game:GetService("PathfindingService"):CreatePath()
	path:ComputeAsync(myRoot.Position, goal)
	local waypoints = path:GetWaypoints()

	if path.Status == Enum.PathStatus.Success then
		for _, waypoint in ipairs(waypoints) do
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				myHuman.Jump = true
			end
			myHuman:MoveTo(waypoint.Position)
			local timeout = myHuman.MoveToFinished:Wait(1)
			if not timeout then
				print("Stuck :(")
				myHuman.Jump = true
				walkRand()
			end
		end
	else
		print("Path failed :(")
		task.wait(1)
		walkRand()
	end
end

function findPath(target)
	local path = game:GetService("PathfindingService"):CreatePath()
	path:ComputeAsync(myRoot.Position, target.Position)
	local waypoints = path:GetWaypoints()
	
	if path.Status == Enum.PathStatus.Success then
		for _, waypoint in ipairs(waypoints) do
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				myHuman.Jump = true
			end
			myHuman:MoveTo(waypoint.Position)
			local timeout = myHuman.MoveToFinished:Wait(1)
			if not timeout then
				print("Stuck :(")
				myHuman.Jump = true
				findPath(target)
				break
			end
			if checkSight(target) then
				repeat
					myHuman:MoveTo(target.Position)
					attack(target)
					task.wait(0.1)
					if target == nil then
						break
					elseif target.Parent == nil then
						break
					end
				until checkSight(target) == false or myHuman.Health < 1 or target.Parent.Humanoid.Health < 0
				break
			end
			if (myRoot.Position-waypoints[1].Position).magnitude > 20 then
				findPath(target)
				break
			end
		end
	end
end

function checkSight(target)
	local ray = Ray.new(myRoot.Position, (target.Position - myRoot.Position).Unit * 40)
	local hit, pos = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent})
	if hit then
		if hit:isDescendantOf(target.Parent) and math.abs(hit.Position.Y - myRoot.Position.Y) < 10 then
			print("I see them")
			return true
		end
	end
	return false
end

function findTarget()
	local dist = 60
	local target = nil
	local potTargets = {}
	local seeTargets = {}
	for i, v in ipairs(workspace:GetChildren()) do
		local human = v:FindFirstChild("Humanoid")
		local torso = v:FindFirstChild("HumanoidRootPart")
		if human and torso then
			if (myRoot.Position - torso.Position).magnitude < dist and human.Health > 0 then
				table.insert(potTargets, torso)
			end
		end
	end
	if #potTargets > 0 then
		for i, v in ipairs(potTargets) do
			if checkSight(v) then
				table.insert(seeTargets, v)
			elseif #seeTargets < 1 and (myRoot.Position - v.Position).magnitude < dist then
				target = v
				dist = (myRoot.Position - v.Position).magnitude
			end
		end
	end
	if #seeTargets > 0 then
		dist = 60
		for i,v in ipairs(seeTargets) do
			if (myRoot.Position - v.Position).magnitude < dist then 
				target = v
				dist = (myRoot.Position - v.Position).magnitude
			end
		end
	end
	if target then
		if math.random(20) == 1 then
			screamSound:Play()
		end
	end
	return target
end

function attack(target)
	if (myRoot.Position - target.Position).Magnitude < 6 then
		grabSound:Play()
		grabAnim:Play()
		grabAnim.Stopped:Wait()
		if target.Parent ~= nil then
			target.Parent.Humanoid:TakeDamage(10)
		end
		task.wait(0.3)
	end
end

function died()
	task.wait(1)
	game:GetService("Debris"):AddItem(script.Parent,0.1)
end

myHuman.Died:Connect(died)

lowerTorso.Touched:Connect(function(obj)
	if not obj.Parent:FindFirstChild("Humanoid") then
		myHuman.Jump= true
	end
end)

function main()
	local target = findTarget()
	if target then
		myHuman.WalkSpeed = 18
		findPath(target)
	else
		myHuman.WalkSpeed = 8
		walkRand()
	end
end

while task.wait(0.1) do
	if myHuman.Health < 1 then
		break
	end
	main()
end```
2 Likes