How we are developing an RTS game with good performance for more than 200+ units

When you are making an RTS game for Roblox you will encounter several problems in the process. I am writing this post, not only as a “dev-blog”, but as a resource for everyone interested in making an RTS. Im a developer of a game in development called “Conquest Napoleonic Wars”, a spiritual sequel of the original game called “Conquest”, alongside with a team.

First of all, the problem I faced at the start: network usage. I will talk about it in this topic, but here is another very good post from a dev of Astro Force, that is the post I got the information from to make the system for the game.

How we reduced bandwidth usage by 60x in Astro Force (Roblox RTS) - Resources / Community Resources - Developer Forum | Roblox

Optimizing network usage

It is pretty obvious that when you are handling a lot of parts both server-side and client-side, the network usage skyrockets. The way you can check the network usage in your game is by going to dev console → ServerStats. There are some things, but the most important one is the “Total Data KB/s”, that, as the name suggests, is the amount of data being sent / managed.

In the cate of a RTS game, of course, you will not have a problem when handling 5 or 10 units. But what about 100? 150? Thats when network optimization comes.

The most important think to take in mind is that every unit must be ““fake””. The server should only contain the data of the unit, like its positions and health. This is because that if you not only store and handle the data, but you also create the units models and such server-side, the network usage will get a lot higher than it should be. This is the reason why you shouldnt make any part to represent the RTS units in the server. Instead, only the clients should do it. Here is an image to represent it. Not the best, but it works.

Everytime a unit moves, attacks, or dies, the client creates the visual effects or whatever needs to be created to show it. This way not only the network usage is a lot lower, but the server CPU and memory usage is a lot lower too!

Take in mind that when sending the data to the client, it should be not in a dictionary, but as an array, this is because arrays have a lower size than dictionaries, so the network usage is lower.

Bad

{
UnitID = “123456”,
Position = Vector3.new(0, 0, 0)
}

Good

{
“123456”,
Vector3.new(0, 0, 0)
}

Of course, this makes readability a bit worse because this

local ID = UnitData.UnitID

Is easier to read than

local ID = UnitData[1]

But its the price to pay for good network performance. A very good thing that Roblox has is the type Vector2int16. Basically a 16 integer number that allows u to send numbers with a much lower size, saving bandwith. The disadvantage is that it only supports from 32767 to -32767 integers, so be carefull when using that. Here is a post with an example and more info about it:

Using Vector2int16 to conserve bandwidth when sending integers across remotes - Development Discussion - Developer Forum | Roblox

Thats basically it about network usage optimization. Send as few data as possible and in the most optimized way possible. It is also a good thing to limit the rate the server sends the data. In Conquest Napoleonic Wars the server does it around every 10 frames.

Server CPU usage

We talked about network usage, but regarding to server CPU usage, there are a few things to take in mind too. First of all, always try to use parallel luau for every unit calculation that it can be used for. it is faster than calculating everything on one thread.

Finally, the most important part about server CPU optimization for RTS games is making every unit search for a target to attack performant. Looping through every unit to check which to attack is very expensive. We can use a swarm module to make it a lot more performant, lowering server CPU usage. A swarm module works by dividing units positions in a grid and then just making every unit checking for a target loop through each square.

image

I will not talk much about it in this post, but here you have a link to a post from a Roblox staff member where I took the previous image from: Optimizing Unit Targeting in Roblox with a Swarm Module - Resources / Roblox Staff - Developer Forum | Roblox

Client replication

The idea is the same as network optimization. Do as few as possible, and do it in the most performant way. You wanna make particles? optimize them. Units models? optimize them. But a problem that seems not so easy to fix is making a lot of units move in the client without lag. To do this. dont move units models by

Model:PivotTo()

It is easy, but it is not performant. Prefer to attach every unit part to the primary part of the model with weld constraints, unanchoring them all except the primary part, and then changing the pivot of the primary part instead of the model’s one.

Model.PrimaryPart:PivotTo()

I dont know exactly why, but it is a lot more performant than doing it the other way. Finally, merge as many parts of the units models as possible. This also improves prformance. Also, pretty obvious, but always set your units meshes with every property they dont need disabled and with RenderFidelity set to performance.

When replicating moving units positions, use lerping to smooth it. But check first if the unit model is too far away from the player. If it is, then just change the position, dont smooth it.

Extra

Pathfinding - Fast Flow

As everyone knows, pathfinding can get very expensive, specially for a RTS game, where you need to calculate pathfinding for a lot of units. You can improve the performance by using a pathfinding module like this one

FastFlow

Take in mind that even with an optimized pathfinding system it is still expensive. Consider only checking units path every, for example, 5 frames instead of 60. This way the server CPU wont get that much pressure.

Conclusion

These are some things to take in mind when optimizing your RTS game, sure, there are more details, but this is a resumed post about all the three points. Let me know your opinion!

17 Likes

Fantastic post! If anyone’s interested in swarm-based pathfinding for similar projects, FastFlow is a great tool to try out. It’s optimized for efficient flow field generation and pairs really well with the techniques shared here.

Astroforce demonstrated that handling hundreds of units is no longer a pipe dream. With new discoveries like these optimization techniques, now is the perfect time to be developing performant RTS games on Roblox.

2 Likes

Didnt know about your pathfinding system. I added it to the post, may interest people who want to make a rts game

200 units is very little for an rts and you can achieve way more than that (1000+). Are you using humanoids for your units?

1 Like

I know that 200+ units are low for an RTS. Our game does not use humanoids and it is able to handle more than 800+ units without any problem. It just that 200+ was the first number that came to my mind for the title.

Thanks for mentioning the humanoid thing tho, imma include it in the post soon.

1 Like

Thx!
I already have a system that works for 500+ units without lag working and have been using it for a while.
Quick question, how would I make units be able to collide with each other if the units in the server are only represented as CFrame values instead of parts? Sorry if it seems obvious but i still dont get how

You gotta make your own collision system. It will be the most efficient way. Just look up axis alligned cylinder collision. For performance reasons you should use an acceleration structure like spatial hash grids and also multithreading

1 Like