Pixel Simulator Optimization

Hello! In this post I will be showing how I took my game that I’m working on from bad frame times and high network usage to good frame times and lower network usage! This goes off of the basis that you already are familiar with programming in Luau.

The Issue

In my game players can go through different fields collecting pixels however the issue is in spawning the pixels. Right now the way it works is each field has it’s own lifecycle that spawns pixels. In the future I want players to be able to have fields spawning hundreds of pixels a second meaning that there is alot of data that needs to be sent to the client since the pixels are created on the client and just stored in a table on the server. The data that needs to go to the client is as follows:

  • The ID of the pixel (generated using HttpService:GenerateGUID)
  • The field the pixel was spawned in (just a number)
  • The type of pixel that spawned (the string for the type of pixel)

Well how do we get all this data to the client? Well the most basic solution is just to send a table with that info however this is not great for performance since we are going to be sending alot of of pixel spawn requests.

Original

Network

The first time that I tried this like mentioned above I send a string for the type of pixel and a string for the pixel id and a number for the field id. For the stress test I spawned 100 pixels at the rate 1 / 100 (0.01s) and that was being sent to the client with the former data. This was causing around 150~200mbs in network receive which is really bad and alot of throttling was happening.

Pixel Creation

When the pixel spawn request was on the client I would find the pixel model in replicated storage clone it and then spawn it above the field in a random spot. This was also really bad since we where calling clone / destroy every single time we created a pixel which was thousands of times a second.

New

Network

For the network I was able to optimize it by continually stripping down data. The original data looked like this:

local data = {
    pixelId = string,
    fieldId = number,
    pixelType = string
}

Like mentioned this is alot of data to be sending thousands of times so in order to reduce it the first time I turned it into an array which made it look like this:

local data = {
    string,
    number,
    string
}

This helped a little but wasn’t really noticeable. So the next idea I had was to change the type of data inside. Right now for the ID of the pixel I was using HttpService:GenerateGUID which would generate a string that looked like the following a4793358-a605-4c0a-8202-798f7c026e2. This is a long string that’s unique but another more simple way would to be just to have a number that goes up each time a pixel is created which is what I did by introducing a nextPixelId number which would keep track of the next pixel id (like the name states). The next thing that I did was create a pixel type map to convert pixel types to numbers and pixel numbers to types meaning that all our data could be send like this now:

local data = {
    number,
    number,
    number
}

This meaning that our data was all numbers now which did lower the receiving to be around ~100mbs which is still not where I wanted it to be. So now that all the data being sent are numbers we can use bit packing to pack all this data into a single number to send over. This works by taking the bits of a number and shifting them over and then on the client you shift them back. You can find out more about this in this great post here.

After doing this the network is at about ~30mbs while receiving thousands of pixel spawn requests a second which is unlikely in the main game but now it’s future proof!

Final data:

local data = number

Pixel Creation

Creating (using clone) and destroying pixels thousands of times a second is a good way to turn your computer into a toaster. The main issue is when we use Instance:Clone so to remove this I used a concept called instance caching where instead of removing an instance when you are done with it you store it for later use. Then when you need to create an object you can just check the pool of objects. The psudo code for this would look like so:

function Create
    instance = getFromPool()
    
    if not instance then
        instance = createNew()

function Destroy()
    instance.Parent = pool

Now for my game when I create new pixels I reset the data stored in the pixels attributes each time since there will be old data if it was pulled from the instance pool. Before this the fps would be around ~30ms however after this im running at ~60ms without any spikes or anything. The pixels are also animated each frame to move up and down using a sine wave and we do this each frame. To get the best performance we can use Workspace:BulkMoveTo to move a list of parts to a list of cframes.

Conclusion

Thank you for reading this post and I hope you found it helpful. Make sure you optimize your code to avoid performance issues however make sure not to over optimize code that works fine. Remember:
“Premature optimization is the root of all evil” -some smart guy

10 Likes