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.