There are a few minor optimizations you could be doing here.
First and foremost, any place where you do anything that looks like;
local variable = 1
variable = variable + 1
should be transferred into:
local variable = 1
variable += 1
This is a micro-optimization you can do that’s a new feature introduced in Luau, and will reduce the amount of info calls the calculation makes from 2 to 1, which is significant when you are trying to micro-optimize. An example of what lines I mean specifically is where you do frameCount = frameCount + 1
, you can change this to frameCount += 1
and it will work identically, but faster.
The same applies to the line where you do before, moveTo = moveTo, moveTo + 1
, instead, you should be separating that statement into two lines, to look like this;
before = moveTo
moveTo += 1
this will allow you to leverage the former Luau optimization more.
Something else you should keep in mind is that creating a variable that will only be used once is pointless, and will only take up runtime. This means that you should not be creating the “cframe” variable inside of your UpdatePos loop.
Currently, it looks like this;
local cframe = enemyHandler:CalculatePos(enemy)
table.insert(enemyTable, enemy.RootPart)
table.insert(posTable, cframe)
instead, it should look like this:
table.insert(enemyTable, enemy.RootPart)
table.insert(posTable, enemyHandler:CalculatePos(enemy))
This way, you will not be creating an unnecessary variable that would be taking up unnecessary memory (even though it is a tiny memory difference), technically, it also takes time to create that variable so this counts as a micro-optimization.
To add on top of that, you have a variable that is not being used, that variable being “moveFrom”. I am unsure whether or not you will actually need this but, if you don’t, make sure you remove it for extra performance.
Lastly, there is one more change that you could make (which may or may not improve performance, I’m leaving this to you to try out and see the difference but, Luau has something called generic loops, where you don’t have to use “pairs”, “ipairs” and “next” anymore, and instead should just directly reference the table. What I mean by this is that rather than doing;
for id, enemy in pairs(enemyContainer) do
you should try doing:
for id, enemy in enemyContainer do
This will automatically select whether it is best to use pairs, or ipairs automatically for you, and might even outperform picking the method yourself as the functionality is written in C++ (which is way faster than Lua and Luau).
There are a few other places where you are creating a variable for no reason as well such as the alpha variable, however, it may be best to keep those since otherwise the code will be completely unreadable. The “cframe” variable case was not needed as it would not necessarily sacrifice readability for performance.
Here’s an updated version of your code with all of my micro-optimization changes applied in case you’re confused:
--!native
local replicatedStorage = game:GetService("ReplicatedStorage")
local replicatedFirst = game:GetService("ReplicatedFirst")
local modules = replicatedStorage.Modules
local clientModules = replicatedFirst.ClientModules
local enemyContainer = require(modules.EnemyContainer)
local playerSettings = require(clientModules.PlayerSettings)
local enemyHandler = {}
local frameCount = 0
function enemyHandler:CalculatePos(enemy)
local nodesFolder = workspace.Nodes
local currentTime = workspace:GetServerTimeNow()
-- Remove moveFrom is it is not needed?
local moveFrom, moveTo, before, spawnTime = enemy.Before, enemy.MoveTo, enemy.Before, enemy.SpawnTime
local enemyTimes = enemy.nodeTimes
while enemyTimes[moveTo] < currentTime - spawnTime do
before = moveTo
moveTo += 1
end
local startPos, endPos = nodesFolder[before].Position, nodesFolder[moveTo].Position
local timeToFinish, timeLeft = enemyTimes[moveTo] - enemyTimes[before], enemyTimes[moveTo] - (currentTime - spawnTime)
local alpha = (timeToFinish - timeLeft) / timeToFinish
enemy.Before, enemy.MoveTo = before, moveTo
return CFrame.new(startPos:Lerp(endPos, alpha)) * enemy.RootPart.CFrame.Rotation
end
function enemyHandler.UpdatePos()
frameCount += 1
if frameCount <= playerSettings.RefreshRate then
return
end
frameCount = 0
local enemyTable = {}
local posTable = {}
for id, enemy in enemyContainer do
local rootPart = enemy.RootPart
local timeElapsed = workspace:GetServerTimeNow() - enemy.SpawnTime
if timeElapsed >= enemy.FinishTime then
if rootPart.Parent then
rootPart.Parent:Destroy()
end
table.remove(enemyContainer, id)
else
table.insert(enemyTable, enemy.RootPart)
table.insert(posTable, enemyHandler:CalculatePos(enemy))
end
end
workspace:BulkMoveTo(enemyTable, posTable)
end
If you’ve got any questions, don’t hesitate to ask them!