Tower defense game movement

hi, im trying to make a tower defense game and im making the enemies

now ive come across a reply which is this:

and so i would basically need help with this since i cannot reply to a topic that old

so, as he said he’s doing client sided rendering, so on the server side enemies are just tables with the position and stuff and that position gets sent from the server to the client and on the client is where the actual enemy model is made

what i dont understand is moving the position on the server side, he said in that topic that he’s using CFrame:ToWorldSpace to move enemies to the next node, which i might be misunderstanding because on the server enemies are just tables, and tables dont have cframes

so yea just basically trying to understand what hes doing on the server to make the enemies move to the next node

5 Likes

Hi, guy in the reply here.

When I mention “position” in the table, it’s actually a CFrame. You can store a lot in tables, including CFrames. A CFrame in the table and a CFrame of a part are still both CFrames.

1 Like

oh yea i used cframe too there is another reply you made thats a bit longer, what im having trouble understanding is how youre moving the cframe

so just on the server side, there’s the table and i’d just set it to node 1 right away:

local enemyData = {
	["HP"] = 100;
	["CFrame"] = CFrame.new(workspace.Node1.CFrame
}

like this

so how do you move this cframe to the second node?

1 Like

To move the enemies on the server I run a Heartbeat event, which runs 30 times a second, and in the heartbeat I make the enemy move forward.

Using ToWorldSpace we can make the enemy move in the direction its looking no matter what, so it will always move forward and not sideways or backwards. We give ToWorldSpace another CFrame, which is how far it moves.

When I set the position, I use the second argument of CFrame.new which is like the direction its looking. I give it the next nodes position so it’ll face towards the next node. Say all your nodes are number ordered, we spawn it at 1 and make it face towards 2.

Lets also store the current node so we know where we are. We can check if we’re near the next node by doing a magnitude check every time we move with the next node. If we’re close enough then we’ll increase our node, and make our enemy face the next node.

This code would move the enemy by 1 stud 30 times a second, going through the nodes. This code is untested so it might not actually run, but its a super simple version of what you would want to do.

local enemyData = {
	["HP"] = 100;
    ["Node"] = 1
	["CFrame"] = CFrame.new(workspace.Nodes[1].Position, workspace.Nodes[2].Position)
}

game:GetService("RunService").Heartbeat:Connect(function()
    enemyData.CFrame = enemyData.CFrame:ToWorldSpace(CFrame.new(1, 0, 0))

    if (enemyData.CFrame.Position - workspace.Nodes[enemyData.Node + 1].Position).Magnitude <= 1 then
        enemyData.Node += 1
        enemyData.CFrame = CFrame.new(workspace.Nodes[enemyData.Node].Position, workspace.Nodes[enemyData.Node + 1].Position)
    end
end)
3 Likes

so the tower faces the next node which works, but then the :ToWorldSpace makes it go on the wrong axis and not towards the node, making it go sideways

i dont know why its doing this

1 Like

I think thats my mistake, try using the Z axis instead of the X axis.

enemyData.CFrame = enemyData.CFrame:ToWorldSpace(CFrame.new(0, 0, 1))
2 Likes

alr it works with -1 on the z axis

so if you dont mind, i cant figure out how to even begin with the wave system and multiple enemies

so for the enemies would you have all of the enemies’ stats in the same script or should they each have different module scripts?

1 Like

Its really up to you how you organize stats, I made a single enemy module for all the stats but its mostly preference, I prefer not having 50 scripts open to edit multiple enemies.

1 Like

so in your video i saw this:


and so here what i assume is that this a serverscript and that EnemyInfo is kind of like just a general info for all enemies, because things like the health and such are defined by the modulescript that you said all your enemies are in

and in your wave script:


ive noticed that youve managed to make it so that the enemies are just strings

so im not sure how to describe this, but how did you achieve gettings the specific enemy like walker, getting its info from the module script and spawning it

Yes, EnemyInfo is a basic info for all enemies, and EnemyModule is the specific info for that enemy.

For waves, I loop through all the enemies. I have the enemy name so I can get it inside the EnemyModule.

--EnemyModule (named Enemies)

local module = {
    ["Walker"] = {
    --enemy stats
    }
}

return module
--Wave spawning

local EnemyModule = Enemies[v.Name] 
-- 'v' here is the table that holds Name, Amount, TimeBetween, and TimeUntilNextGroup
1 Like

what im confused by v, wouldn’t v be “1” inside the “enemies” table

thats what has the timebetween and timeuntilnextgroup

Yes, to get all the enemies in the table we can use a for loop

for i,v in Enemies do
    print(i, v) --prints 1, table with TimeBetween etc.
end