I would try looking at what some other tower defense games did and some tower defense kits to see what they did, granted 2000-3000 enemies is a ton of enemies which I’m not even sure if that’s possible,
So I did some testing I whipped up a prototype to test the performance of lerping vs tweening because those are the ones I would use personally, and using these scripts (i will explain later)
Lerp Script
My Stats:
2.2% - 3.1% Activity level
80/s - 91/s rate
wait(1)
print("Starting Tween")
local spawnCount = 3000
local tweenService = game:GetService("TweenService")
function spawnNew()
local newPart = Instance.new("Part")
newPart.Anchored = true
newPart.Parent = game.Workspace
newPart.Size = Vector3.new(0.5, 0.5, 0.5)
newPart.Transparency = 0.5
newPart.Material = Enum.Material.Neon
newPart.Color = Color3.fromRGB(8, 0, 255)
newPart.Position = Vector3.new(math.random(-150, 150), math.random(5, 75), math.random(-150, 150))
task.wait()
local info = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.In, 0, false)
local tweenPosition = tweenService:Create(newPart, info, {Position = Vector3.new(game.Workspace.Waypoint.Position)})
tweenPosition:Play()
end
game:GetService("RunService").Heartbeat:Connect(function()
if spawnCount > 0 then
spawnCount = spawnCount - 1
print(spawnCount)
spawnNew()
end
end)
Tween Script
My stats:
1.270%-2.3% activity level
55/s - 63/s Rate
wait(1)
print("Starting Tween")
local spawnCount = 3000
local tweenService = game:GetService("TweenService")
function spawnNew()
local newPart = Instance.new("Part")
newPart.Anchored = true
newPart.Parent = game.Workspace
newPart.Size = Vector3.new(0.5, 0.5, 0.5)
newPart.Transparency = 0.5
newPart.Material = Enum.Material.Neon
newPart.Color = Color3.fromRGB(8, 0, 255)
newPart.Position = Vector3.new(math.random(-150, 150), math.random(5, 75), math.random(-150, 150))
task.wait()
local info = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.In, 0, false)
local tweenPosition = tweenService:Create(newPart, info, {Position = Vector3.new(game.Workspace.Waypoint.Position)})
tweenPosition:Play()
end
game:GetService("RunService").Heartbeat:Connect(function()
if spawnCount > 0 then
spawnCount = spawnCount - 1
print(spawnCount)
spawnNew()
end
end)
Switch the scripts if you want it for testing, have one of the scripts initially disabled, have all scripts in server script service preferably in a blank baseplate
game.Players.PlayerAdded:Connect(function(player)
player.Chatted:Connect(function(msg, player)
if msg == "switch" then
script.Parent.TweenScript.Enabled = not script.Parent.TweenScript.Enabled
script.Parent.LerpScript.Enabled = not script.Parent.LerpScript.Enabled
end
end)
end)
Lerp script has the parts spawn in and move to an example waypoint with lerp.
Tween script has the parts spawn in and move to an example waypoint with TweenService.
The scripts have their activity level and rate (performance) from script performance in the view tab, and in conclusion it seems like tween is faster than lerp, id test more for you but it’s almost 4 am where I am,
Id go with ether but keep in mind I do not believe there is any low performance degrading way of handling movement at the very least if your going to have thousands of enemies, and you would likely have to have the models very simple.
like I said earlier, you could look at some open source kits for tower defense games and see how they did this for their game, or how they just work in general to gain a better understanding of what you could do.
Video example 1: Tower Defense Simulator Kit Easy! - YouTube
Video example 2: Tower Defense Kit | Roblox Studio #robloxstudio #towerdefense - YouTube
(I have a friend that used this kit, and I worked with it when helping him code some stuff so I believe this is a good kit)
I hope what I provided was helpful and best of luck in finding what works for your game.
Apologies for any grammar / spelling errors im pretty tired
Edits: clarifications