Performance considerations, improvements, and optimizations when making a game

Introduction


This post is intended to be a resource to provide valuable information on performance considerations when developing games/projects. To those who may come across this thread, if there are concerns of inaccurate information please contact me.


General

Introduction To General Performance Considerations

The general category will include general performance issues that are affected across other development categories.

Particles

Introduction To Particles

Particles are powerful tools that we can use in order to improve the quality of our games. Particles are automatically optimized (based on graphics level) by Roblox if they happened to be enabled in the workspace. However, there is a a method to ParticleEmitters known as :Emit(). You can read about this here.

As mentioned on the DeveloperHub:

TLDR; :Emit() ignores the automatic optimisation Roblox offers. One method of commonly practice is to have a module for emitting particles based on the local player’s quality. An example is shown below:

return function(Particle, EmissionAmount)
	local QualityLevel = UserSettings().GameSettings.SavedQualityLevel
	if QualityLevel == Enum.SavedQualitySetting.Automatic then
		Particle:Emit(EmissionAmount)
	else
		local Compressor = QualityLevel.Value/10
		Particle:Emit(EmissionAmount*Compressor)
	end
end

You can use this function by copy and pasting it into a ModuleScript. An example of the use case is shown below:

local Emit = require(Emit)
Emit(Particle, Amount)

The last notable thing on particles is to obviously avoid extremely high rates if possible. Increasing the rates on ParticleEmitters will hit performance quite notably on a lower-end device. Simply ask yourself if you really need that many particles.

Instance Count

Introduction To Instance Count

Though this is pretty self-explanatory I believe this topic is still worth discussing. It should be common sense that the less objects and instances you have in your world, the better it’ll be overall. That being said, there are times where reducing the amount of instances become impossible while keeping quality in mind. However, there are many methods you can apply depending on your scenario.

To those who have trouble with reducing instances with blocks (if you’re making a square based block game) you may find greedy meshing as an applicable scenario to your project, take a look at this.

Another example is datastores. I commonly see many beginners fall into the trap of converting a whole table into instances. using Folder as the table and StringValue, NumberValue, BoolValue to represent the values. Some data you simply don’t need displayed. A player may end up having 200+ instances just to represent data when a RemoteFunction could be used to retrieve the data. Some creation of instances is convenient (which is fine) but trying to create a whole list isn’t necessary.

There is another method that may help which is to let’s say compact your inventory that may look like this:

local inventory = {
{id = "apple", quantity = 3},
{id = "orange", quantity = 4},
}

into a compacted string like so:

local inventory = "id:apple,quantity:3;id:orange,quantity:4"

If you were to create instances of both, one would just require a StringValue object and the other would require:

Inventory (Folder):
→ 1 (Folder):
—> id (StringValue)
—> quantity (NumberValue)
→ 2 (Folder):
—> id (StringValue)
—> quantity (NumberValue)

7 instances. (The downside of the other one would be reading it and accessing the data from the server).

Using Multiple Places

Introduction To Having Multiple Places

Having multiple “Places” is extremely ideal for a heavy content game. While it may take a fair amount of work, with good organisation and with the help of Roblox Packages. You can easily reduce the amount of instances/maps/meshes and running scripts in your world.

If you happen to be a programmer, one thing to look forward to is TeleportService.

Taking advantage of multiple places is something everyone should definitely consider pre-development as it can be a huge game-changer.

Lighting

Introduction To Lighting

Lighting is more important than it seems and can drastically affect the performance of lower end users. The most expensive technologies are ShadowMap and Future because of how much more detailed they are and the more functionality it provides. Generally, just keep in mind that the lighting you choose does matter.

Memory

Introduction To Memory

Memory is important when it comes to developing your game. You usually want your game to be accessible to every platform allowing you to maintain the highest player base possible. If your game doesn’t support mobile/console then perhaps this may not be as relevant but do note that even lower end laptops/pcs can still be affected.


What is memory?

Memory is the amount of data needed to be stored for unique assets. These are a few assets that use up memory:

  • meshes
  • textures
  • animations
  • sounds
  • unions (CSG)

Scripts also use memory, and scripts can cause memory leaks (which is basically the equivalent of cancer to a game). Memory leaks can be avoided through good coding practices. Finding memory leaks is usually a very difficult and tedious task.


What should my memory be at?

This is a common question asked quite frequently and that is how much memory should your game shoot for. Here is an image of the estimated memory storage available on each device:


You can find a post on it here.

One thing you can do is look at the front-page games of Roblox and check out how they are doing on memory. You can check memory by joining a game and pressing F9. I’d recommend looking at games that are similar to yours (if they exist).


How can I reduce memory?

Because memory is such a broad topic, I’m going to list what I know of here. I want to point out that not everything I know can be backed up with evidence. I will be labelling what I know works and am unsure about. :heavy_minus_sign: will represent unsure, :white_check_mark: will mean that there is evidence to support it. If I made a mistake or you know if displayed information is wrong, feel free to contact me to correct it.

Methods Factual
Reducing Image Dimensions :white_check_mark:
Compressing Image Size :heavy_minus_sign:
Reduce Amount Of Unique Images :white_check_mark:
Reduce Amount Of Unique Animations :white_check_mark:
Reduce Amount Of Unique Textures :white_check_mark:
Reduce Amount Of Unique Decals :white_check_mark:
Reduce Amount Of Unique Meshes :white_check_mark:
Reduce Amount Of Unique Unions :heavy_minus_sign:
Reduce Amount Of Sound Assets :white_check_mark:
Not Caching Variables And Functions In Scripts :white_check_mark:
Storing Assets In Server Storage Rather Than ReplicatedStorage :white_check_mark: (Reduces Client Memory)

Some of these are unavoidable. For example, functions and variables are extremely useful and makes things much easier while other methods such as meshes and images can possibly be reused. This is only here to provide information.

One last thing to note is for ModuleScripts. I believe that while you can not reduce the “total memory” of all the module scripts being required, you can require a module when you need it so that the memory eventually reaches the maximum rather than starting off with the max. This is a optimisation I’ve heard some developers may use.


Replies To Consider


Scripting

Introduction To Scripting Performance Considerations

Scripting is a huge part of game-development and overall there a lot of things you can do to optimise and improve performance of your projects. A lot of times, scripts are also the reason your game may have lag or remote latency. Algorithms and methods are sometimes applicable to make some impact changes.

Luau

Introduction to Luau

Luau, is an extension of Lua created by Roblox. Features from Luau proves to be much more performance beneficial over Lua. Luau is constantly being updated with new features such as a recent new feature implemented into the table library table.clear() which are much more optimized compared to clearing tables manually or making a new one. On the platform, it is recommended to use Luau, however, some of us may not be happy with it and may just stick towards official Lua/RLua more.

Loops

Introduction to Loops

Some individuals may like to compare the speed of loops. While I highly doubt it’s the reason your game is lagging, I still believe it’s worth noting as if you are looping through large lists/dictionaries it could make a potential impact (while unlikely). Here’s a post that is really an eye opener:
Don’t use ‘next’ instead of ‘pairs’ | by Stephen | Medium

RemoteEvents/RemoteFunctions

Introduction to RemoteEvents and RemoteFunctions

Remote events and remote functions are necessary for a game to establish a connection between the server and client. However, I’ve noticed in one of my projects, sending large amounts of data as an argument will simulate something similar to a bad-wifi/latency lag. I want to warn everyone that it’s always better to just keep the amount of information you send between the server and client as minimal as possible (I was sending giant tables).

Does the amount of RemoteEvents/RemoteFunctions matter?

I’ve come across this question several times. RemoteEvents do in fact have limitations. They have a data-size limitation as well as a rate limitation. Here are some posts that justify that.

RemoteEvent Rate:
Limits of RemoteEvents - Help and Feedback / Scripting Support - Developer Forum | Roblox

And a notable comment:

So, what does this mean? Well you could use one remote event / remote function for everything but sometimes it’s better to keep things separate for better readability and organisation. So, what’s the best option? Well:

Visual Effects

Introduction To Scripting Visual Effects

Throughout my experience communicating with other developers, I’ve noticed one thing I believe everyone should understand. The server should be running at maximum efficiency processing and handling important data and information. This means that majority of visual effects should be done on the client and replicated across the clients if necessary. The benefits of this is not only better performance on the server but smoother visuals on the client.

I’ve seen far too many people tween/lerp meshes/models on the server or spawn particles on the server when they could just replicate the effects onto a singular client or many using a RemoteEvent and the :FireAllClients() or :FireClient() method.

Algorithms

Introduction To Algorithms

There are ton of Algorithms. Not every single one may be applicable to you. Algorithms are different ways to approach certain situations to improve efficiency of code. You can read more about it here. For a more friendlier article you can read this.

While algorithms are extremely helpful, they aren’t universally applicable, certain algorithm can only be used under specific situations. For example, binary search is a well known searching algorithm, however, it can only be applied on sorted lists/arrays. Depending on the situations, algorithms may be the game changer in performance, in other times, perhaps you don’t need them.

Bad Practices

Introduction To Bad Scripting Practices

When it comes to scripting, bad practices are considered from typos to code structure. While some bad practices may just be a personal preference. Some may have a minor to major impact in your project. There are many code smells and bad practices I could list, however for the purpose of this post, I will mention a few that have direct impact on performance.

I must also say, there are plenty of posts out there and one I find very informative is this one:
https://devforum.roblox.com/t/what-are-bad-programming-habits-that-you-see-often-by-scripters/761033


Bad Practices:

Using The Second Argument Of Instance.new()

Using The Second Argument Of Instance.new()

There was a public PSA on this topic here.

TLDR: Parent last after setting all the attributes/properties. The reason for this is so that the server does not need to replicate all the changes to the client. It’d be preferred if all the properties were set and then parented so it’s all replicated at once. For more information read the PSA.

Using Loops Improperly

Using Loops Improperly

Loops are extremely helpful and they are capable of helping us in many ways. But one way that I’ve seen many using loops improperly is using loops over built in RBLX events. Some built in events may have specific triggers that avoids using loops that can greatly help and other times are much more optimised.

An example would be: using a infinite loop (while loop) to update a TextLabel. Instead use .Changed or :GetPropertyChangedSignal.

You’ll quickly notice that unnecessary looping can cause game crashes with enough of them. Don’t be scared to use Roblox events as they have been created for a purpose.

Not Localizing Variables

Localizing

Localizing is mostly minor. There is a difference but not that big of a difference. There are definitely other things you could prioritise over. Here’s an example:

local Clock = os.clock
local Start = Clock()
local RunService = game:GetService("RunService")
local VarToEdit

for i = 1, 1000 do
    VarToEdit = os.clock() -- this does not use our cached variable
end
print(Clock() - Start)

Start = Clock()
for i = 1, 1000 do
    VarToEdit = Clock() -- This one does
end
print(Clock() - Start)

And here are your results:


You’ll notice by a very small amount, that using the cached variable is faster. But this doesn’t mean you should change everything in your game. It’s just something to know and trust me, if your game is lagging, it’s not because you didn’t localise your functions.

I do want to mention that recent changes to Luau has already localized the libraries. You can read about changes here and here. That being said, if you wish to stick closer to official lua you can still cache libraries as practice.

Not Caching Your Variables As A Local Variable

Not Caching Your Variables As A Local Variable

As mentioned earlier, the more functionality you give to something, the more expensive it usually is.


Memory Leaks

Introduction To Memory Leaks

Memory leaks, the cancer of a game. Memory leaks are caused by scripts that are not cleaning up / removing instances/assets that were created and is no longer being used. Memory leaks are also caused by internal code (usually RBXSignals that don’t get disconnected when finished with). There are 2 posts that explain really well on this topic here and here.

The easiest way to prevent memory leaks is to ensure that you are destroying instances and disconnecting your connections. If you are constantly creating things but never destroy them the game will slow down and eventually crash.

One useful tool to new developers is to use the maid class created by Quenty. You can find it here.

Techniques And Methods

Introduction to Scripting Techniques And Methods

On the platform of Roblox, developers are constantly looking for new ways to approach things in order to improve performance. I’m going to be listing a few interested topics here that you may want to look into!

PartCaching / PartPooling

Introduction to PartCaching and PartPooling

PartCaching/PartPooling is caching and reusing parts to avoid creating new ones. There are 2 posts I’ve found on this topic that provide premade modules and more information here and here.


Building

Introduction To Building Performance Considerations

When people first start building, usually they don’t consider optimisation as their priority.

There a few things that should be mentioned in this topic so I’ll go ahead and break this into categories.

Textures And Decals

Introduction To Textures and Decals

If you haven’t read the section on Memory in General, I suggest you do. Using multiple unique textures and decals is one way to increase memory in your world. That being said, certain built-in materials render easier than others (look at BasePart Properties).

Part/Texture Count

Introduction To Part/Texture Count

If you haven’t checked out General > Instance Count then I suggest you read that section first.
Moving on, in my personal experience, Building has been one of the most guilty in overusing objects/instances. As mentioned in the section of Instance Count, there are ways to reduce part count through a method known as Greedy Meshing. Unfortunately, greedy meshing isn’t applicable in every situation so the best tip for you builders is be aware on how many parts you are using and look for anything you can combine.

That being said, BaseParts are not the only thing that are overused. If you must use Textures try to use textures on faces that matter. If a face of a Part will never be seen by a person, don’t add a texture on that face.

Here is a nice visual example from Mariofly5’s post:
image

Mariofly5 has also made a post on building optimization which you can read about here.

Level Of Detail Property

Introduction To Level Of Detail

Level Of Detail is a property of Models that was recently introduced which works with the property called StreamingEnabled in Workspace. For more information, check out this post.

In short, Level Of Detail will help with the system knowing what to render out and what to keep. Having the property on Automatic or Disabled can benefit lower end devices.

BasePart Properties

Introduction To BasePart Properties

When you build a map, you need to consider if certain parts need collision or not. The reason for this is because Roblox performs physic calculations on parts with specific properties. The best properties to have for builds would be:

Property Value
Anchored :white_check_mark:
Material SmoothPlastic
CollisionFidelity Box
CastShadow :heavy_minus_sign:
CanCollide :heavy_minus_sign:
Transparency 0 or 1

These would be the ideal properties to have in every BasePart. If you’re making a simulator and detail isn’t required consider turning off CastShadow and changing the CollisionFidelity to box. In most cases, unless you really need Roblox’s physic calculations on your BasePart, Anchored should always be set to true.

As for transparency, here’s a quote from colbert2677

from here

BasePart Material

If you’ve played games on Roblox, you may have seen certain games provide an option for lower end devices which basically changes all the material to SmoothPlastic and removes textures. Now, this is true that SmoothPlastic is much better as a material for performance. Materials such as Neon and ForceField will tend to be much more expensive. ForceField has extra functionality such as adding a texture to have a “ForceField” affect while Neon just has extra glow.

If you plan offering a lower graphics option to players, consider doing the following:

  1. Change the lighting to something less expensive locally
  2. Change all materials to SmoothPlastic locally
  3. Disable CastShadow for all BaseParts
Tips

Introduction Tips

Here are a few great tips I’ve found that have it explained better than I could so. Some of these may apply to other categories such as CSG’s and Meshing.


Source


Meshing and CSGs

Introduction To Meshing Performance Considerations

Since I’m not a modeler, I don’t know much about performance considerations on this topic. I’ve done some research and well meshing comes down to a few things. The post I used for research can be found here and is much more informative.

Triangles

Introduction To Triangles

Every BasePart in Roblox has geometry to it and are conformed by triangles. Meshes and unions tend to have more triangles because of the more complex shape compared to Roblox parts. The less triangles you have the better.

Here’s a quote from here.

RenderFidelity Property

Introduction To RenderFidelity Property

If you haven’t checked out LevelOfDetail property in the Building category for models, I suggest you take a quick look at that.

RenderFidelity is similar to LevelOfDetail except it doesn’t require StreamingEnabled. Instead of completely removing the Model, instead, it’ll drop in quality. This can be very beneficial to players with lower end devices. Setting your RenderFidelity to Automatic would be the best option for performance.

Collisions

Introduction To Collisions

I’ve already mentioned collisions in Building > BasePart Properties. Meshes and CSG’s are considered BaseParts so please refer to BasePart Properties in building.

LowPoly Vs HighPoly

Introduction To Lowpoly vs Highpoly

You’e probably heard of the term of Low Poly and High Poly before. What does it mean and how does this affect your world? Well, rather than explaining I’ve found a very informative article here that you can read about. Generally, simulators sticks to “Low Poly” because of how simple it looks as well as the best choice when creating a game across all platforms (Mobile, Console, PC) because of the performance benefits.


Animations

Introduction To Animation Performance Considerations

After some researching, I was unable to find anything that really should be considered for animations. We can probably assume the more unique animations there are along with the more keyframes each animation has the more memory it will consume.

My suggestion would be don’t bother with animations that much. If your game is lagging it’s likely not due to animations.


Gui

Introduction To GraphicalUserInterface (GUI) Performance Considerations

After researching upon this topic, I’ve found a few things that you should consider looking at to optimize your user interface.

GUI Visibility

Introduction To GUI Visibility

One common thing people do is like to tween their GUI. Usually, when people animate their UI, they animate it off the screen to hide it. While this accomplishes what the programmer wants, if they do not hide the user interface with the Visible property, it’s still being rendered.

So how does toggling the visibility of a GUI help? Well, you may have noticed already but turning the visibility of a GUI off means that you are disabling functionality of the user interface. This means clicking, hovering and all that neat stuff gets disabled as well.

You can read more about GuiObject here, the article talks about other benefits to toggling the visibility such as disabling rendering for that time and etc.

Static GUI

Introduction To Static Graphical User Interface

Static GuiObjects will reduce rendering calculations which will in return benefit you.

Replies To Consider

You can find more information here.

ViewportFrames

Introduction To ViewportFrames

ViewportFrames were recently introduced Roblox allowing 3D objects to be rendered onto a 2D screen. ViewportFrames are now very popular among games and are a extremely powerful tool. That being said, you should consider how expensive it is rendering a 3D object onto a screen.

This isn’t the end though, recently WorldModel’s were introduced allowing physic calculations to be done in the GUI. This allowed animations to be played and rendered as well as adding other functionality to the ViewportFrame. While this is awesome you should limit the amount of WorldModel’s used since these are very expensive.

One tip I’d recommend is reusing camera instances of the same viewport object if possible. Having just a static model is preferred for a viewportframe.

UICorners

Introduction To UICorners

Like Viewportframes, UICorners are relatively new and come in very handy. Obviously, with this new tool, there are downsides. Roblox has listed the pros and cons which I will quote here:

So when should you use UICorner? Well, if you’re simply trying to make frames with smooth corners I suggest using 9-Slice. For cases where this is unavoidable (for example, needing to curve a ViewportFrame or ImageLabel’s Corner) you should use UICorner’s.


Resources And References

Scripting

Don’t use ‘next’ instead of ‘pairs’ | by Stephen | Medium
Garbage Collection and Memory Leaks in Roblox - What you should know
PSA: Connections can memory leak Instances!
https://devforum.roblox.com/t/what-are-bad-programming-habits-that-you-see-often-by-scripters/761033
https://devforum.roblox.com/t/localizing-variables-but-not-functions/369038
How to use a Maid class on Roblox to manage state
Consume everything - how greedy meshing works
Algorithm efficiency and alternatives to table.find
PSA: Don't use Instance.new() with parent argument
Part Pooling - Increase performance with many parts
PartCache, for all your quick part-creation needs

GUI

Static UI Performance Improvements

Meshing

Differences between High Poly Vs Low Poly 3D models | by Arjun Patel | Medium
Levels of Detail for Mesh Parts
Quick Guide Into CSG: Increasing Performance of Unions & Meshes
Introducing Level of Detail for CSG Parts

Building

Introducing Level of Detail for CSG Parts
Building Optimisation | Tips and tricks!
BasePart | Documentation - Roblox Creator Hub


Credits

Contributors: @Mystifine // @EDmaster24 // @colbert2677 // @DragRacer31

Feel free to contact me with more information I can add onto this post.


Last Update: 2023-08-12T04:00:00Z

217 Likes

I feel like these resources are really useful.
Meshing and Animations are blank, though.
I’m guessing you’ll fill them later.

2 Likes

Thanks, dude. I’ve always looked for ways to increase performance. This is so helpful.

2 Likes

No problem, I’ll be updating this post as I gather more information. You may have noticed meshing and animations are blank. I’ll be filling those out sooner or later.

EDIT: I’ve just gone through the whole post again and made adjustments and added a few topics such as UICorner.

3 Likes

This is more than enough for me lol. I’m just glad I can figure out optimizations now.

1 Like

This is actually the limit to how much memory that device can use, not a recommended amount.

1 Like

Thank for letting me know, I’ll go make changes. I believe it’s a estimated value:

1 Like

This is a nice collection of research available. Unfortunately won’t do much with the whole issue of people not doing their own searching, but still helpful for those who pay attention to the Resources category at the very least.

So comments time.


General - Particle Count

This variable isn’t used. Additionally, the maximum in game quality level is 10. I don’t know why Studio chooses to differ and mark it up to 21 though.

Enums have a value, use that instead of the name. All quality levels have a value equivalent to the actual quality level they’re running at, with automatic having a value of 0 (unfortunately that means you can’t determine much about automatic, including how it’s currently configuring graphics).

-- That division by 10 is where you were supposed to use MaximumQualityLevel.
local Compressor = QualtiyLevel.Value/10

It’s good to do this in practice (glad my boy Emit has some love, I don’t see it used that often especially in places where it matters) but using Emit may not be practical in all cases. Emit will instantly emit n amount of particles at one time and that may not be desirable in all cases where you want to do trailing or let particles off via Enabled. Use with discretion.


General - Memory

Another important missed consideration, although your list probably wasn’t exhaustive, is sound. Sound is a huge consumer of memory enough to warrant a presence on the thread.

Developers often find it easier to store sounds in storage services, especially music, however sound is capable of consuming a lot of memory especially for longer, powerful music. You can clear off a few tens of memory usage by not placing sounds in the DataModel. Sounds may be fine but music is a huge consideration, especially if you have a lot of it.

Sounds can stay if you find that creating and removing them is expensive or causes delays, however for music best practice is to not load it in until you’re sure you need it for a certain duration. You can get rid of it once it’s no longer needed to clear up some memory.


Building - Textures and Decals

I actually want to counter this and say don’t. Materials are equally, if not more expensive, than custom textures and you have less control (for example, being able to display the texture across only faces that are possibly visible to the player).

If performance is big on your mind and takes precedence over aesthetic, disabling all materials (using SmoothPlastic) is significantly more beneficial for performance. There is a reason why games with settings features are often featuring “disable materials” as a setting.

I see you covered that in BasePart Material though, which is good. It’s just that the information is sprawled out when it should be joined or available all at once.


Gui - Missed point

Something that was missed that would be good to touch on is the fact that static Guis are less expensive to draw than dynamic Guis. That meaning, it’s quicker to draw Guis every frame that don’t change than it is to redraw them.

So for all the folks out there who like tween-vomit for their Guis, just know that any spikes in Gui draw time (they’ll be visible on the MicroProfiler, not too much to the player circumstance depending) are probably coming from the fact that there’s a lot of tweens operating. A game doesn’t need tweens everywhere to look good (a sentiment I wish my own team shared).

Citation:


's all.

7 Likes

What about voxel usage for terrain? Is there any documentation on stats for that?

Oops! That is my mistake. Thank you for pointing that out. I was going to use that value but it seems like I’ve forgotten to do so.

I forgot QuaalityLevel.Value returns a number.

This is a whole other category I missed. I completely forgot about sounds. Thank you for mentioning that.

Would it be fine If I made edits to my post using your new information?

Unfortunately, I’ve never worked with terrain before. I’ll see what I can find and I’ll make edits if I find anything.

1 Like

Feel free to do so. The feedback was there to help you improve your post or prompt a discussion if the information I had wasn’t correct and could be countered with a citation.

3 Likes

how do i stop the loop and like resume it do i just
if loopneeded then
–loop
else
–loopnot
end
or is there smthg else

You can have a variable:

local Loop = false;
if LoopNeeded then
   Loop = true;
   while Loop do
      -- loop here
   end
else
  Loop = false; -- should break the loop
end
1 Like