What would be the best way to handle enemies in a tower defense game?

Hi, I am making a Tower Defense game and I want to know how to handle enemies movement so it is not choppy. If movement and animations was done on the server then it will be a little laggy, but on the client then it is smooth however, people can easily exploit it and the enemies may not appear in the same place on each device.

8 Likes

Using PathFinding is probably the easiest method of making enemies follow a certain path. to reduce lag, you can use Client Sided Rendering (Its on the devforum but I cant seem to find it)

this Post might help you understand some stuff: What is the most efficient way to render client sided mobs?

Basically, when you use Client SIded Rendering, The Server sees a bunch of parts, and the client sees enemies, which reduces lag by a lot

5 Likes

I imagine that enemies in a well-made tower defense game are wrote as classes.

You should write a class that has properties:

  • Enemy type
  • Objects (the actual parts/models that exist as the enemy)
  • Position
  • Health

In code this would be wrote through a constructor inside a ModuleScript.

Handle actual movement on the server, and if you are worried about it, Tween movement on the client or use RunService. Only trust the server with actual positions.

Hook events into your enemy class module that trigger your Tweens to occur on the client. On the server they would be moved to that position and yield code until either Clients are ready or you assume that they are.

Exploiters cannot “easily exploit it” if your enemies are actually based server-side, that’s not how exploiting works.

3 Likes

Being one of the creators of Toy Defenders, I have actually spent a lot of time on this problem. There are two main solutions I came up with that completely depend on your preference and tower defense style.

Server-sided Movement & Client-sided Rendering

When to use this method: If your enemy movements are very frequently interrupted by effects such as slows, stuns, freezes, ect…


You create an object within a script on the serverside that holds all the enemy’s properties and functions for movement, health, ect… This was the original way I designed the enemies. You can have either a cframe value or a CFrameInstance to hold the position/orientation of the mob and update it every heartbeat. You can then send this value to all the clients telling them which enemy it is, where it is, and any other information you would like the client to know/display. On the client side, you will receive the mob positions and other attributes and use the TweenService to make their movement fluid and smooth. Note: On the server side, you don’t not need to send the server information every heartbeat, that is far too often. Instead, send it perhaps every 0.1 seconds. I do this like so…

local count = 0
RunService.Heartbeat:Connect(function()
  count += 1
  if count >= 10 then
    count = 0
    -- send information from server to client
  end
end)

Server-sided Validation & Client-sided Movement / Rendering

When to use this method: When the enemies will have little to no status effects influencing their movement.


You create mob objects on the server, and send a one time information package to the client telling them the path nodes and the enemy’s speed. Using this information you can have logic on the client to know fully where and when to tween the enemies entirely on the client side. This is by far the most efficient way to do it as you’re letting the client do all the hardcore calculations and using the server to validate what is going on. If you need to get a mob’s position on the server side, you would be able to figure out where they are on the path by using the path node points, the length of the entire path, and the speed of the mob in a modified d = rt sort of function. If you want to have minimal status effects on the mobs, you can simply send that information in a one time message to the client telling them to stop/slow the mob and for how long (factoring in the ping with tick()). The issue is when there is frequent movement influences because of the delay it take to send information from the client to the server, you will inevitable get a server/client movement desync.


This is a super high level overview of my process designing this system, it took me a while to get it right for my specific game. These frameworks need to be tailored toward what your game is going to be like, there is no one method fits all solution.

I also suggest checking out this post, as it has some extremely useful information on reducing the amount of data you send to the client.

Here is a piece from the Toy Defenders mob framework that convert’s server-sided mob information to send to the client in a more efficient form…

23 Likes

Ok, thank you. My solution right now will be to use OOP to make the enemy class then every 0.1 second I will send data to the client and client will use tweenService to move the enemy. I will mark your answer as a solution once I have finished it. Here is my code thus far on server:

6 Likes

Thank you, it works like a charm!

2 Likes