What did you use to work on collectible physics? I’m guessing it’s either just using roblox’s default physics or you got some kind of other magic up your sleeves?
…guy, why so much to read over that one comment…
Which? The comment about me having 0 proof or data about streaming not being good? Fair enough but ultimately to show him said data it may create a giant text of wall and images in the process. Sorry about that!
Awesome article but out of curiosity why aren’t you using the task library for yielding I took a look and saw you use the old wait()?
Haha oh no >_<’
Smartypants Answer: old wait() will automatically throttle in high CPU situations, which is useful if you’re using a coroutine to balance load.
Real answer: I forgot
Thank you so much for this article! I found all the tips really helpful for me as a developer trying to optimize my games.
I noticed that this script doesn’t do what the quote says it does, it only removes a small detail object if the character’s HumanoidRootPart is 300 studs away from it, which doesn’t really account for a camera being zoomed in and out so I rewrote it to do that. Here’s the beginning of the heartbeat function, I just redefined the pos variable to be the CurrentCamera in the workspace. It works for me now whenever I zoom in and out. I’m not sure if this was intended but this is just a heads up that you should edit the game (or the quote) to be more accurate.
RunService.Heartbeat:Connect(function(dt)
local pos = workspace.CurrentCamera.CFrame.Position
What about having them both connected together?
Just like the camera position and HumanoidRoot
Yeah the text doesn’t quite match the code there.
Imho it’s a tiny bit nicer to have distance culling tied to the player rather than the camera if only because the camera is orbital, and if you spin it around you could cause a lot of updates.
That makes sense. Thank you for clarifying!
Thank you for this very detailed post! This is a great overview of several ways to optimize a Roblox game!
This is an excellent way to summarize which is the limiting factor. Thanks!
I have a question - how did you create the budget for how many triangles, drawcalls, CPU usage, etc. in Chapter 1? I’m curious as to what went into the numbers.
It’s just an approximation I came up with by adding stuff to a test place until it wasn’t running at 60 anymore. Divided and conquered.
Awesome resource, I found the building & models chapter especially useful.
I think there’s a better alternative to the suggested coin system though.
Instead of creating a serial number for each coin, we could store the max possible amount of coins to pick up on the server, and then only check if this amount wasn’t exceeded.
Do you have any intuition regarding vertex color performance? I currently have levels in blender where most of the objects are sharing materials via large texture atlases, and I was considering using vertex painting in combination with textures to add additional shading variation to the map/objects.
I’m concerned that
- vertex paint would somehow interfere with batching,
- could the additional memory cause problems for the lowest of low end mobile when it comes to vertex fetch/worse gpu cache performance? It’s not the case that all objects have vertex color attributes assigned, is it? As in vertex color performance is already paid for and any gained visual fidelity by using it is free.
In the spirit of the older gamedev titans and intimate hardware optimization, I would like my game to be performant on say the iPhone 5, even if it is more theoretical given the bloated nature of the Roblox app itself
here’s a rough example of vertex color and texture blending btw!
I’m probably not going to go with vertex painting because Blender really does not support it well and it causes several major workflow issues like not being able to simultaneously paint across multiple objects, issues with Blender’s Principled BSDF shader not blending vertex color by itself, requiring the use of a color mix node as shown below (which then results in the default FBX exporter in blender not being able to properly identify the assigned texture map )
Was just trying around with different instances and their draw-calls and just to add on here; beware of the SelectionBox
instance (and likely similar instances too). Not only do they count for up to 12 draw-calls on your scene budget depending on your distance to it. But when far enough away from it, it ends up being a bit sneaky and counts 1 to your “Draw (total)” budget without affecting any of your other budgets making you feel like you’re getting it for “free” when in-reality, you are not. Unless there is something I’m missing here?
As far as I’m aware of, vertex colors are a complete freebie.
This was really interesting! I have built a super efficient and custom renderer myself, my game is procedurally created via various variable algorithms, then rendered objects are compressed to a reference of their source object and a cframe, and also for the wall tiles I use imposter models to save on tens of thousands of polygons.
All the models have different categories and are of different rendering distances, I use occlusion culling to render them in, in conjunction with streaming enabled.
In the realm of procedural rendering in creating simulations you can effectively write a renderer that scales very well.
I’ve used some of your methods such as spacial hashing for efficient collission detection. This was a really good read!
With the distance/zone culling, should StreamingEnabled be turned off? I noticed it’s off in your game, but surely having it on just allows even more improvements to performance and draw calls?
Follow up question, wouldn’t custom LOD/culling systems conflict/disrupt roblox’s batching systems?
Amazing, i honestly already knew most of these, but i never really learnt how to use the microprofiler. Thanks!
Update for those interested:
After digging into the scene rendering stats, I found that the further away a model is, the fewer draw calls are made. Essentially, moving a model far away (e.g., using math.huge
) has a very similar impact on scene draw calls as parenting the model to nil
. So, for practical purposes, both methods appear to achieve nearly the same result in terms of performance.
My choice for using math.huge
over parent = nil
stems from performance spikes caused by reloading textures when models are reparented in and out, especially in large quantities.
Hope this clears things up!