The Hitchhiker's Guide To Optimization - Optimize Your Game Using Just One Post Instead of Many

Hello distinguished users of the devforum! I am here to give you all what I call my “Hitchhiker’s Guide to Optimization” (those who know the reference, good on you for knowing it). This contains all of my research and knowledge that I have built up over the years on optimizing my games to 99.9% perfection!

NOTICES BEFORE BEGINNING

  • This is mostly meant for newer users to the DevForum. It is not meant to be for professional, full stack developers. However, you are free to make your own guide for full stack developers in the replies to ensure this guide helps all areas of the developer ladder.
  • I am not the best programmer, but I did my best with writing down stuff for scripting optimization.
  • DO NOT USE ANTI-LAG SCRIPTS. PLEASE.

  • Any time you are unsure of something, please look it up yourself. This post is not meant to be a directory to everything. I am not including many links because I want to avoid overwhelming the average devforum newbie.
  • I am not going to be active that muchin the replies of this post. I will see your posts, and maybe heart a few of them, but I will only be responding to make clarifications where I feel it is needed. I feel that the less I help, the more the person will learn.
  • These are all things I have had experience with. I have ran countless tests with my community to see what works, and what doesn’t. What you are seeing here is both research that I combined together throughout the years, tied in with results from my Community. Devices that were used to test this range from a Kindle Fire (I don’t know if they are still supported) to an iPad 2, all the way up to a computer running some of the best hardware out there.

GENERAL OPTIMIZATION

  • Set all your meshparts to Performance or Automatic. Run these two bits of code SEPARATELY put forth by 440obama in your command bar. I have modified it to make it be to performance and for unions.

for _, object in pairs(game.Workspace:GetDescendants()) do
if object:IsA(“MeshPart”) then
object.RenderFidelity = “Performance”
end
end

for _, object in pairs(gPreformatted textame.Workspace:GetDescendants()) do
if object:IsA(“UnionOperation”) then
object.RenderFidelity = “Automatic”
end
end

  • Set StreamingEnabled to be on. For bigger games, I have attached my preferred settings below.
    image

  • To fix any issues that arise from Streaming, make the part be a model and make the ModelStreamingMode be Persistent. It will fix the issue.

  • To fix audio, I use this plugin. It allows me to find the ID and the sound info, and I then line it up with a screenshot I got from my game to find out what audio is bugging out. https://create.roblox.com/store/asset/9173750549/Bulk-Audio-Manager-10

  • To fix part count issues, use this plugin. Here is the key for it:
    https://create.roblox.com/store/asset/208249680/Part-Counter

    • Voxels are Terrain. Keep this below 100 million for an ideal gameplay environment for all devices.
    • Instances are everything in your game not terrain-related. This number doesn’t matter too much.
    • Parts are all of the parts and meshparts. The best spot for this count to be at is around 75k, because it’s still able to be managed by mobile devices of all sorts.
  • HOLLOW OUT YOUR TERRAIN. This is very, VERY important for ensuring your game will not lag. Use Subtract to make the process faster, be sure you do not create any holes in the terrain the player is meant to see.

    • From my experience, flat surfaces mean more voxels, meaning more memory taxed on the player, and a decrease in FPS. I ran a test with my development team, and the results sent back showcased an increase of 15 to sometimes 48 FPS. More voxels = more GB of RAM used/more memory used!
    • Try to avoid the underside of the terrain making complicated shapes. This can cause some issues with performance.
  • Try to avoid using regular water in bulk. it is REALLY LAGGY.. If you are making an ocean-related game, use infinite water. If you know what you are doing, then you can make it be swimmable.

  • Convert your Unions into Meshparts. ROBLOX does not optimize Unions the same way they optimize Meshparts, so the best way to fix that is doing Export Selection to convert it into a meshpart. YOU WILL NEED TO RESIZE AND RE-POSITION IT THOUGH!

  • For all noncollidable meshparts, set the CollisionFidelity to “Box”. This saves ROBLOX the issue of rendering that stuff. Please do not use this if that part is being used for CanTouch-related things.

Thank you sandofamber for contributing this script to the effort (Run it in the command bar):

for i, v in next, game:GetDescendants() do
if v and (v:IsA(‘MeshPart’) or v:IsA(‘UnionOperation’)) and not v.CanCollide then
v.CollisionFidelity = Enum.CollisionFidelity.Box
end
end

  • For all meshparts, try your best to avoid using DoubleSided. It is not optimized. Same goes for PreciseConvexDecomposition.
  • Try to keep CanTouch and CanQuery off. These can cause a lot of lag once they build up. You will only see CanQuery if CanCollide is turned off. Turn them on if you are actually needing to use them though.
  • This is optional, but convert all of your audio to ogg files. This will help a ton for mobile players. ROBLOX does NOT do this automatically.
  • Another way to help mobile players is to not use Textures. Rather, use custom materials or SurfaceAppearances. I have never found textures to be a good thing for mobile devices.
    • ROBLOX has to check each side of the part for a texture if a texture is on it, from what I have seen. Using the knowledge I gained from testing, SurfaceAppearances and Custom Materials are the best way to go because ROBLOX doesn’t need to check for all 6 sides. of the part. Decals are also a bit weird like this, but they aren’t as laggy from experience. This will help you with optimizing your decals and textures, it was linked in another devforum post but I forgot where it is. https://imagecompressor.com/
  • Added on from wastbo’s reply: Try to re-use meshes and be careful regarding changing things like transparency and color on them.

SCRIPTING OPTIMIZATION

  • Set every wait you can to task.wait(). wait() was deprecated by ROBLOX in favor of task.wait(), meaning it can lead to issues in the future, and can be unoptimized for ROBLOX’s newest code changes.
  • Try to keep as few heartbeats going as possible.
  • Set all RaycastFilterTypes to Include and Exclude, to keep up with ROBLOX’s changes.
  • Try to store stuff in ServerStorage instead of ReplicatedStorage. This helps with the player being able to handle the game better from the clientside, which is arguably more important in order to keep performance going well.
  • From AGoodGuyHere: “Dont use FindFirstChild if you know it will always be there”
  • DO NOT USE ANTI-LAG SCRIPTS. THEY DO THE EXACT OPPOSITE OF WHAT THEY SAY THEY DO.
  • !IN BETA AT THE TIME OF WRITING THIS! Use buffering in some scripts to help alleviate issues. Read more about it, here.
  • Use Native Lua to help streamline the code for your game. Do not use it everywhere though! Read into it more, here.

Hope this helps everyone here. Feel free to add on anything you think would help.

All of this guide has helped me, and it has helped others. I am not expecting it to satisfy everyone, but this is the best I can do. And, before making your reply, I encourage you to do tests and research yourself, as this will help a lot with learning what works for everyone and what doesn’t.

For more context, you can read the replies below. I specifically recommend the posts by Abcreator and TimeFrenzied, along with the note I added onto Abcreator’s reply.

22 Likes

Bookmarked!

There are syntaxes you can search by in the explorer! You can simply just search is:particleemitter or is:seat or c:part or c:particleemitter etc! You can even search by properties, such as locked==true (or locked=true)!

You can then press the “select all matching” button (or press CTRL + A) to select them all.

Also, don’t use waitforchild on the server unless something is created during runtime. I know I worded that horribly so here’s videos on it.

Summary

https://www.youtube.com/watch?v=7tzy1DuPcBQ
https://www.youtube.com/watch?v=mRA0AytlAak

Never even thought of this… I wonder if it’s possible to make a plugin that can do this automatically.

4 Likes

How bad is it to use it frequently on the server if its not waiting for things during that aren’t exactly there?

I quite literally have a thing for smacking anything im looking for into a WFC just incase it doesn’t throw an index nil error.

2 Likes

I’m not sure exactly how bad it is, but most of the time you never need to use WaitForChild:() on the server.

If an object is created during runtime after the script runs you might have to. The videos I sent go over when to use it, and when not to use it.

3 Likes

Just though I’d add a little bit of extra context from my personal experience in this sphere. Do let me know if I’ve made any mistakes, though.

Note that Performance can grant poor results for some meshes, Automatic is distance-based (and the default option), so far away meshes that don’t look accurate enough to be visually pleasing should still be set to Precise RenderFidelity.

Note also that this removes most of the benefits of StreamingEnabled for any model you enable it on. Plus Persistent models aren’t necessarily guarenteed to solve your scripting problems either since they aren’t guarenteed to have loaded until PersistentLoaded has been fired. Ideally, you should code around StreamingEnabled being enabled.

As a little correction here, CollisionFidelity doesn’t change the rendering afaik (except for shadows sometimes) but rather just opts for setting the collision box to a box, this makes it unsuitable for any parts which you use the Touched event (or other collision methods/events) on. It is, however, good for decreasing memory.

As far as I know from what I’ve heard, Roblox converts files to ogg before serving it to users. Maybe there is a mobile bottleneck for mp3 uploaded files, but if so, I personally haven’t heard of it? For any looping tracks, you should upload ogg files, though, since mp3 has an inherent piece of silence at the end of each track.

Do you have benchmarks for this. This feels like it should be the other way around?

For added context on why, task.wait has a shorter minimum waiting time and doesn’t throttle. Those are the main benefits for it afaik. When not using the number parameter, you are probably better off using RunService events, though.

Keep in mind that the reason this works is because it isn’t being streamed to the client. Only items you don’t need to use the client should be stored there.

From my understanding of buffers, you probably don’t need to use them unless you are working with your own binary-like format. Most scripts won’t need to use it.

Only enable this if you see a worthy performance benefit. There are some drawbacks to native code:

  • Code compilation time is required which can increase the startup time of servers.

  • Extra memory is occupied to store natively compiled code.

  • There’s a limit on the total allowed amount of natively compiled code in an experience.

Source

It is also only currently supported on the server.

5 Likes

Some of it is good but most of it is bad

  1. Your main focus on optimizing visuals shouldn’t be to lower the rendering fidelity as low as possible. It should be to minimize draw calls.
  2. If you still need more performance then you can try minimizing triangles.
  3. FindFirstChild is faster than indexing since it skips checking properties.
  4. Use type checking to get the most out of native
3 Likes

And @TimeFrenzied

This Announcement actually says the opposite: Heightmaps Go to New Altitudes! - #27 by JoshSedai
As I understand it having a flat bottomed mountain is better than having it hollowed out since there is more surface area and detail that has to be stored.

However with the new system coming up they are planning to not render hidden faces of Parts but I don’t know how that’ll work for terrain.

5 Likes

I’ll break my truce of not replying because I like this reply, and your reply is a reply meant to add context to help other individuals. Will also probably edit my post to include that I will do some clarifications in the replies lol.

I have added on information on my guide to help clarify some things in this post, specifically regarding your concerns, but I’ll add it in here.

I agree with this part. However, this is to help all kinds of developers, from the ones who just found the DevForum to your professional developer. If you know what you are doing this would be a good way to go about this, but the method I use is what I recommend for your average builder or 3D modeler. I use the method I stated only when I find there is no other way to do so without re-doing a lot of things (either I don’t know what is going on or I am just feeling lazy). To each their own though!

Automatic is NOT the default RenderFidelity option. Perhaps you are thinking of LevelOfDetail? The default option is Precise. I encourage both Performance and Automatic to be used, but Performance should be used for bigger games.

ROBLOX does in fact NOT convert files into OGG format. However, I did not even know about the bit of silence at the end of mp3 files, this is actually really helpful, thanks!

As I’ve stated, I am going off of what I have experienced. I have found most of the posts on the DevForum for optimization to not work completely well for me. For textures, ROBLOX needs to check each and every side, from the experience I have had with this. With custom materials, it just uses the material like your average material, and behaves differently from textures. However, custom materials and surfaceappearances shouldn’t be used that much, and same goes for textures. A lot of people, including myself, want to use PBR more, so this is the best way I have found with making PBR work on ROBLOX. I have attached an image compressor as a result of this though to help developers with optimizing their textures and decals.

Regardless, this is a great reply for adding on more context, and I do appreciate you for that!

While the post you linked is made by a ROBLOX admin, I do want to mention that it is not only made in 2021 but that there has been a lot of research done on this. I would also like to refer you to this post, here. What's better: Hollow or filled terrain?

I do understand the concern with this information though, and will add on something regarding it. I also almost forgot something I wanted to mention about water in this post.

the mp3 I uploaded to roblox has been converted to OGG.

I believe this fact has actually been a bit of a pain point for people archiving audios uploaded to roblox,

they even seem to have trimmed the bit of silence off the beginning of my mp3


(qFu6MdSL is the redownloaded one)

even the ogg i uploaded seems to have been re-encoded anyway, so you’re probably not saving much quality either

the only exception I’ve personally seen to this is raining tacos, which was actually a 320kbps mp3

I’m 99% sure indexing should always be faster than FindFirstChild. Even if it is, at some point it’ll just be slower once there are enough children to look through. There’s a reason you don’t iterate through a dictionary to check for a key when you already have it.

Benchmark it for yourself. FindFirstChild is faster for random access since it skips properties. There’s also no reason for it to internally use a loop when index is a hash. Think about it

1 Like

This is the exact kind of thing my post was meant to avoid. This post is meant to be something everyone can understand, and if some fresh developer is looking for an optimization guide, they will not know what most of these things you listed are without doing a ton of research.

Not everyone has the time to sit around and learn all of this stuff. If you want to really criticize this post, then you can do it like others did, and actually say things in a term that everyone will understand. Minimize Draw Calls is something that barely any avid developer knows about, it’s something that would be higher up.

Basically, speak in a language everyone can understand to make your critique worth it. Don’t speak in dev lang.

You do know FindFirstChild is internally a loop right? I’m pretty sure whatever benchmark you did was wrong. Roblox says it themselves:

If it didn’t loop there would be no recursive option.

1 Like

They don’t know because they are not taught that yet, which is what I thought this resource was about

Besides, FindFirstChild serves a different purpose, so if you already know something exists you should just index it. (Using variables also helps)

You seem to not be acknowledging the crystal clear learning curve in developing… smh. I’m not going to argue with you further because this post is meant to help people, not be a place to argue.

Most of the stuff he said was right (except the find first child thing)

Just because it has a recursive option doesn’t mean it uses a loop

Yes, but it’s important that everyone can understand it. That’s what I’m getting at. What I listed has worked out for me, anyways, and it’s something that most people can easily understand and do. Plus, you can add on what he said into what I put in to optimize further.

I’ve found that a lot of developers starting out have a huge problem with optimizing. They don’t know how to do it, so it’s best to explain it in layman’s terms so that everyone can have smoother games. It should not be locked behind those who have been doing this kind of thing for years.