Tower Defence Enemies Server/Client

Would this be the right category
If this is too long please read TL;DR

Currently on the server. To make an enemy I have a class. Skipping to the :Move function the movement can seem a bit off when I increase the speed. Currently, the speed is set to 1 which works perfectly fine but let’s say I want to increase the speed to 3 or more it will get stuck on a node due to the magnitude being greater than one. Is there any way to possibly fix this?

function Enemy:Move()
    if self.State == "Dead" then return end 
    if self.CurrentNode >= #Path:GetChildren() then return end 

    local nextNode = Path[self.CurrentNode + 1] -- Get's next node in path

    self.Position = self.Position + ((nextNode.Position - self.Position.Position).Unit * self.Speed) -- Not sure how I could improve this

    if (self.Position.Position - nextNode.Position).Magnitude < 1 then -- If it's reached the node
        self.Position = nextNode.CFrame -- Correct its position to the node
        self.CurrentNode += 1 -- Increase current node

Now I also have an EnemyService which will handle the information the client will receive. This is a server script and my main concern is the coroutine. Let’s say there are 100 enemies (very extreme case) on-screen how would this affect performance. Would there be a better way to do this? I get 100 enemies and will almost definitely have performance issues but could this be made more efficient so the performance is relatively.

   Creates an enemy via the enemy class and moves it
   @ Param: enemyName [string] -- Name of Enemy 
function EnemyService:CreateEnemy(enemyName: string)
    local newEnemy = -- Creates the enemy
    self:AddData(newEnemy) -- Adds itself to a table
    coroutine.wrap(function() -- Not sure the best way to go about moving this 
        while true do 

Lastly, and sorry for asking a lot, I just want to be sure before proceeding since I’ve spent a couple of hours on this so I may be missing something completely or overlooking something. So the client-side, when I receive the event from the

   Loops through the current enemies and checks if the data being given doesn't already exist
   @ Param: data [table]
   @ Returns Boolean
function EnemyController:_isDuplicate(data: table): boolean
    for _, enemyData in ipairs(CurrentEnemies) do
        if enemyData["ID"] ~= data["ID"] then continue end
        return true
    return false

   Adds enemy to the CurrentEnemies table
   @ Param: data [table]
   @ Returns Boolean
function EnemyController:_addEnemy(data: table)
    local isDuplcate = self:_isDuplicate(data)
    if isDuplcate then return end

    table.insert(CurrentEnemies, data)

    local EnemyModel = ReplicatedStorage:WaitForChild("Assets"):WaitForChild("Enemies"):FindFirstChild(data.Name)
    if not EnemyModel then 
        return warn("Enemy model doesn't exist!")

    -- What would be the best way to constantly update position?
    -- RenderStep?
    -- Is it best to make another class on the client?
    local Clone = EnemyModel:Clone()
    Clone.Parent = workspace:WaitForChild("Enemies")
    Clone.Position =

-- Using Knit
function EnemyController:KnitStart()
    local EnemyService = Knit.GetService("EnemyService")
        for _, EnemyData: table in ipairs(data) do -- Loops through data | data is given every 0.1 seconds using heartbeat on the server

First Codeblock - is there a better way to calculate position?
Second Codeblock - is there a better way to move the enemy?
Last Codeblock- look at _addEnemy function. What would be the best way to update/add an enemy?

The last code block is my main concern.

Hopefully, you are able to assist me and I do have more question if anyone is up to answer please dm me, if not have a great day I’m sure I’ll figure it out eventually :sweat_smile:

1 Like

Maybe EnemyService could have a table called Enemies. Each enemy gets inserted to this. Then, in a heartbeat loop, you loop through EnemyService.Enemies or whatever you call it and call :Move and :AddData. So rather than 100 while true do loops for 100 enemies you have 1 loop for 100 enemies. Much faster when the number of enemies increase

This is all I could come up with. Goodluck!

Thank you for the idea! I just tried it out and as long as I have it run every 10 heartbeats it works just fine. I’ll do a bit more testing to see but it’s looking great so far.