How to optimise a system

I’m working on a crowd system currently, and this is one of my first large scale systems and I need to think of optimisation. Because if I am looping hundreds, or even thousands of pathfinding scripts, along with their animations and the physical parts, the game will definitely not run smoothly.

I want to ask about ways I can optimise this. Nothing I’ve seen has really helped me.

-- Services
local players = game:GetService("Players")
local rs = game:GetService("ReplicatedStorage")
local workspace = game:GetService("Workspace")
local pathfindingService = game:GetService("PathfindingService")

-- Variables
local cloneTemplate = rs:WaitForChild("BaseCharacter")
local cloneFollowEvent = rs:WaitForChild("CloneFollowEvent")

local db = false
local cancelPath = false
local clonesPerCircle = 8  -- Adjust this to set how many clones are in each circle
local radiusIncrement = 5  -- Distance between concentric circles

-- Function to copy appearance from character to clone
local function copyAppearance(character, clone)
	for _, item in pairs(character:GetChildren()) do
		if item:IsA("Accessory") or item:IsA("Clothing") or item:IsA("BodyColors") then
			local newItem = item:Clone()
			newItem.Parent = clone
		end
	end
end

-- Function to calculate clone's target position in a circular pattern around the player
local function getPositionAroundPlayer(cloneIndex, circleNumber)
	local clonesInCurrentCircle = clonesPerCircle * (2 ^ (circleNumber - 1))

	local angle = (cloneIndex / clonesInCurrentCircle) * math.pi * 2  -- Divide full circle into clone slots

	local radius = circleNumber * radiusIncrement  
	local offsetX = math.cos(angle) * radius
	local offsetZ = math.sin(angle) * radius

	return Vector3.new(offsetX, 0, offsetZ)  -- Return 3D offset for clone's target position
end

-- Pathfinding function for each clone
local function pathfind(player, clone, cloneIndex)
	local char = player.Character

	while true do
		if not clone or not char or not char:FindFirstChild("HumanoidRootPart") or not clone:FindFirstChild("HumanoidRootPart") then
			break -- Ensure clone and player still exist
		end

		local plrHrp = char.HumanoidRootPart
		local cloneHrp = clone.HumanoidRootPart

		-- Determine the circle and position within the circle for the clone
		local circleNumber = math.floor(math.log(cloneIndex / clonesPerCircle + 1) / math.log(2)) + 1
		local clonesInCurrentCircle = clonesPerCircle * (2 ^ (circleNumber - 1))

		-- Get the relative position of the clone in the circular formation
		local relativePosition = getPositionAroundPlayer(cloneIndex % clonesInCurrentCircle, circleNumber)

		local targetPosition = plrHrp.Position + relativePosition

		-- Use pathfinding to move the clone to the target position
		local path = pathfindingService:CreatePath({
			AgentRadius = 3,
			AgentHeight = 6,
			AgentCanJump = true,
		})

		local success, errormsg = pcall(function()
			path:ComputeAsync(cloneHrp.Position, targetPosition)
		end)

		if success and path.Status == Enum.PathStatus.Success then
			local waypoints = path:GetWaypoints()

			for _, waypoint in ipairs(waypoints) do
				if cancelPath then
					cancelPath = false
					break
				end
				clone.Humanoid:MoveTo(waypoint.Position)
			end
		else
			warn(errormsg)
		end

		wait() -- Pause before recalculating path
	end
end


-- Saving player and character
game.Players.PlayerAdded:Connect(function(plr)
	local char = plr.Character or plr.CharacterAdded:Wait()

	local folder = Instance.new("Folder")
	folder.Name = "ClonesFolder"
	folder.Parent = char

	-- Function to create a clone and set it to follow the player
	local function createCloneAndFollow(player, cloneIndex)
		local character = player.Character
		if character then
			-- Create a new clone and set its name uniquely
			local clone = cloneTemplate:Clone()
			clone.Name = character.Name .. "_Clone_" .. tostring(cloneIndex)
			clone.Parent = char.ClonesFolder

			-- Copy appearance from character to clone
			copyAppearance(character, clone)

			-- Start the pathfinding in a coroutine for each clone
			local cloneCoroutine = coroutine.create(function()
				pathfind(player, clone, cloneIndex)
			end)
			coroutine.resume(cloneCoroutine)
		end
	end

	-- Detect when a player walks up to the BaseCharacter
	for _, part in pairs(char:GetChildren()) do
		if part:IsA("Part") or part:IsA("MeshPart") then
			part.Touched:Connect(function(hit)
				if hit.Parent.Name == "BaseCharacter" and hit.Parent.Parent == workspace then
					if db then return end
					db = true

					-- Get current clone count in the folder and create new clones accordingly
					local clones = char.ClonesFolder:GetChildren()
					local cloneCount = 0
					for _, clone in pairs(clones) do
						if clone.Name:find(char.Name .. "_Clone") then
							cloneCount = cloneCount + 1
						end
					end

					-- Create the next clone with a unique index
					createCloneAndFollow(plr, cloneCount + 1)

					wait(1)
					db = false
				end
			end)
		end
	end
end)

Not sure how the formatting looks as I am on mobile. Let me know and I’ll update it when I get on my PC.