Hey so hopefully this is the correct category this time!
Im working on an clone controlling system, where every player can have up to 15 clones spawned that fight other players clones.
Ive been scripting it and im trying to make it as performance friendly as possible while still maintaining good quality. What I have now works, but its not smooth, the clones pause and im not sure its very efficient. Ill share the code below:
Client code:
local Players = game:GetService("Players")
local player = game.Players.LocalPlayer
local clonesFolder = workspace:WaitForChild("PlayerClones")
local PlayerClonesFol = clonesFolder:FindFirstChild(player.Name)
while not PlayerClonesFol do
wait()
PlayerClonesFol = clonesFolder:FindFirstChild(player.Name)
end
local battlefield = workspace:WaitForChild("BattleGround")
local repStorage = game:GetService("ReplicatedStorage")
local RemotesFolder = repStorage:WaitForChild("Remotes")
local CloneRemote = RemotesFolder:WaitForChild("CloneRemote")
-- Define battlefield boundaries
local minX = battlefield.Position.X - (battlefield.Size.X / 2)
local maxX = battlefield.Position.X + (battlefield.Size.X / 2)
local minZ = battlefield.Position.Z - (battlefield.Size.Z / 2)
local maxZ = battlefield.Position.Z + (battlefield.Size.Z / 2)
local yPosition = battlefield.Position.Y + 5
local ClonesToUpdate = {}
local running = false
-- Generate a random position within the battlefield
local function getRandomPosition()
return Vector3.new(
math.random(minX, maxX),
yPosition,
math.random(minZ, maxZ)
)
end
-- Update the clone UI
local function UpdateCloneUI(Clone)
local head = Clone:FindFirstChild("Head")
if not head then return end
local cloneUI = head:FindFirstChild("CloneUI")
if not cloneUI or not cloneUI:FindFirstChild("Frame") then return end
local hum = Clone:FindFirstChild("Humanoid")
if not hum then return end
local healthBar = cloneUI.Frame.HealthBarBg
local healthLabel = healthBar.Health
local healthBarChange = healthBar.Bar
local levelLabel = cloneUI.Frame.Lvl
local ownerLabel = cloneUI.Frame.OwnerName
healthLabel.Text = hum.MaxHealth .. " / " .. hum.Health
healthBarChange.Size = UDim2.new(hum.Health / hum.MaxHealth, 0, 1, 0)
levelLabel.Text = "Lvl: " .. player.leaderstats.Level.Value
ownerLabel.Text = player.Name
end
-- Find the closest enemy clone
local function returnClosestCloneEnemy(Clone)
local closestClone, minDistance = nil, 100 -- Max search range is 100
local ourRoot = Clone:FindFirstChild("HumanoidRootPart")
if not ourRoot then return nil end
for _, folder in pairs(clonesFolder:GetChildren()) do
if folder.Name ~= player.Name then
for _, enemyClone in pairs(folder:GetChildren()) do
local enemyRoot = enemyClone:FindFirstChild("HumanoidRootPart")
if enemyRoot then
local distance = (ourRoot.Position - enemyRoot.Position).Magnitude
if distance < minDistance then
minDistance = distance
closestClone = enemyClone
end
end
end
end
end
return closestClone
end
-- Check for clone death and update state
local function DieCheck(Clone)
local hum = Clone:FindFirstChild("Humanoid")
if not hum then return end
hum.Died:Connect(function()
CloneRemote:FireServer("Died", {Clone})
for i, cloneData in pairs(ClonesToUpdate) do
if cloneData[1] == Clone then
cloneData[3] = "Idle"
cloneData[4] = nil
end
end
end)
end
-- Main clone behavior loop
local function cloneBehavior()
while #ClonesToUpdate > 0 do
for _, cloneData in pairs(ClonesToUpdate) do
local Clone, cooldown, action, target = unpack(cloneData)
if cooldown <= 0 then
if action == "Idle" then
local enemy = returnClosestCloneEnemy(Clone)
if enemy then
cloneData[3] = "Attack"
cloneData[4] = enemy
else
local randomPos = getRandomPosition()
CloneRemote:FireServer("MoveTo", {Clone, randomPos})
cloneData[2] = math.random(20, 40)
end
elseif action == "Attack" then
if not target or not target:FindFirstChild("Humanoid") then
cloneData[3] = "Idle"
cloneData[4] = nil
else
local distance = (Clone.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude
if distance < 5 then
CloneRemote:FireServer("attack", {Clone, target, 20})
else
CloneRemote:FireServer("MoveTo", {Clone, target.HumanoidRootPart.Position})
end
end
end
else
cloneData[2] = cloneData[2] - 1
end
UpdateCloneUI(Clone)
DieCheck(Clone)
end
task.wait(0.2)
end
running = false
end
-- Handle new clones
PlayerClonesFol.ChildAdded:Connect(function(Clone)
table.insert(ClonesToUpdate, {Clone, 0, "Idle", nil})
if not running then
running = true
task.spawn(cloneBehavior)
end
end)
-- Handle clone removal
PlayerClonesFol.ChildRemoved:Connect(function(Clone)
for i, cloneData in ipairs(ClonesToUpdate) do
if cloneData[1] == Clone then
table.remove(ClonesToUpdate, i)
break
end
end
end)
SERVER CODE:
local rs = game:GetService("ReplicatedStorage")
local remotes = rs:WaitForChild("Remotes")
local clonesFol = workspace:WaitForChild("PlayerClones")
local bodyClones = workspace:WaitForChild("Bodies")
local PlayerModule = require(game:GetService("ServerScriptService").Modules.PlayerData)
local deathAnimations = {"rbxassetid://93369397898594"} -- Add more animations here
local cloneLastHit = {}
local function PlayDeathAnimation(rigClone)
local hum = rigClone:FindFirstChild("Humanoid")
if not hum then return end
local animator = hum:FindFirstChildOfClass("Animator") or Instance.new("Animator", hum)
local deathAnim = Instance.new("Animation")
deathAnim.AnimationId = deathAnimations[math.random(1, #deathAnimations)]
local track = animator:LoadAnimation(deathAnim)
track.Looped = false
track:Play()
task.delay(5, function()
if rigClone then
rigClone:Destroy()
end
end)
end
remotes.CloneRemote.OnServerEvent:Connect(function(Player, action, data)
if action == "MoveTo" then
local Clone, Position = unpack(data)
local hum = Clone:FindFirstChild("Humanoid")
if hum then
hum:MoveTo(Position)
end
elseif action == "attack" then
local attacker, target, damage = unpack(data)
local targetHum = target:FindFirstChild("Humanoid")
if targetHum then
cloneLastHit[target] = Player
targetHum:TakeDamage(damage)
end
elseif action == "Died" then
local Clone = data[1]
local hum = Clone:FindFirstChild("Humanoid")
if hum and hum.Health <= 0 then
local PlayerWhoKilled = cloneLastHit[Clone]
if PlayerWhoKilled then
PlayerModule.AddWon(PlayerWhoKilled, 10)
end
Clone.Parent = bodyClones
PlayDeathAnimation(Clone)
end
end
end)
Any help on how I can go at this, make it perform better and improve the looks of it in game would be greatly appreciated!! Thanks y’all