[Check the original post at Unity Memory Management]
Years ago, I went to the asset store and, credit card at hand, I got greedy. I couldn't stop buying good-looking assets at highly discounted price, so I kept stacking them in the cart. More content would be a great fit for my game, I thought. Only that I didn't really have solid foundations for proper Unity memory management techniques. And so I paid the price for my ignorance.
Scale-Or-Fail-Thumbnail
I don't really know what bothered me most...
Was it that I spent over $200 on the asset store?
Or was it the fact that I ended up using just a quarter the content I bought?
It doesn't matter... I learned the lesson.
I experienced what happens if your game is not ready to scale and you trade dollars for extra content.
Today's post is about avoiding common pitfalls that won't let your game scale the way you want. No matter what, it's important to have the feeling we can keep adding content without excluding players with legacy devices.
Now, let me mention something first.
Every game is different, i.e. each game has different bottlenecks at different points.
The following suggestions are guidelines I've found especially helpful when coaching other game developers. At the end of the day, it is your call to decide which ones to adopt.
So no mater what...
Profile, profile, profile (hint: ctrl + 9).
Quick Navigation (opens in a new tab)
Avoid Direct References to Memory-Hungry Content
Textures: Tweak Import Settings
Mesh and Animation: Compression
Garbage Collection: Careful There
Glutton-RAM-Memory
Avoid Direct References to Memory-Hungry Content
Are you still using direct references in Unity?
I'm joking. That's fine.
Direct references are those links you make between components and content. A few examples: a Material directly referencing an albedo Texture, an AudioSource linking to an AudioClip, etc...
Every time you link a field to some content, that content is whole loaded in memory when the component shows up in your scene (with some possible exceptions such as videos and audio clips with certain import settings).
The trick is to not abuse direct references to content that is hungry on memory and you infrequently use. Let's see a few examples.
Case A: Global References
Let me explain this with an image.
Level 1 Asset Management - Detailed Memory Profiling
Did you catch that?
That's the memory profiler in detailed mode. And its pretty upset.
Because guess what it found?
That profiler shows a global object (SkyboxManager) containing direct references to heavy skyboxes, even though only a single one is used at a time.
As my grandmother used to say, what a waste of RAM.
Global objects are permanent and remain alive throughout the entire game, i.e. they do not cease to exist after scene transitions. Usually they invoke DontDestroyOnLoad on themselves and are commonly called Singletons.
This SkyboxManager is always present, pestering memory by keeping a list of direct references to big skybox textures. This adds a considerable overhead with no little to no benefits.
How many skybox textures are we using in that scene at any given time? One.
And how many are we paying for with RAM currency? Six.
Then why the heck would we keep them loaded? Is a lower latency worth the high price we're paying?
Think about it. More memory waste has nasty consequences:
Higher loading times
More (random) crashes
Angrier players leaving 1-star reviews and asking for refunds
That's a worse deal than Unity increasing their prices back in 2019 (I didn't say that).
Sure, your game might not do this with skyboxes...
But it might with other content type. Characters, particle systems, sounds, visual effects, AudioManager, you name it. I've seen this so often.
Does your game do this?
Ask the profiler and don't be afraid of the utterly uncomfortable answer.
The solution to this? Use indirect references (check out my Unity Addressables Tutorial)
But wait, of course there is more to this...
Unity-Addressables-Memory-Gain
Case B: Scene References
Normally, you will be better off by having direct references in your local scene rather than as global objects.
And that's much better, yeah.
The problem is, we might still pay way too much for way too little.
Let me explain.
You have a huge city in your game. In a small chest on the second floor of a bakery, you're storing your most precious slices of bread. So there you put a tiny particle system rendering high-quality god-rays. How else could you convey the mightiness of bread slices?
Since you'll be looking at it from up close, that particle system uses a huge sprite sheet animation. High resolution, high frame-rate spreadsheet that takes about 32MB of RAM.
Tell me, how often will you be staring at that chest? Like 0.01% of the time you spend in the city? Paying 32MB of RAM for that sounds like a steal to me, just like that time I spent over $200 in the asset store to just use a quarter of it.
The solution? Decrease the quality of the texture Use indirect references instead (check out my Unity Addressables Tutorial). Load the particle system when the player comes in the bakery, unload it when they leave.
And problem solved.
Do not decrease the quality of your game, really. Implement instead a solid memory management strategy. Your player will reward you for this with love (and fresh dollars).
Audio: (Maybe) Use Streaming
Audio can take a big chunk of your memory if you don't use the right import settings.
The worst case for your memory is parameters like decompress on load and disabling load in background. That will make sure that the audio is always present sucking as much memory as it can.
But of course, those were the best settings to improve the CPU performance of your game.
If you're trying to reduce memory usage, the best combination is to set longer clips to streaming and to enable load in background. Only small chunks of your audio clips will be in memory at a time.
It's a matter of deciding where to pay the price.
Unity-AudioCli-Load-In-Background-Streaming
Unity AudioClip Import Settings: Memory-Friendly
Surely, don't forget to set the audio clip compression settings to something that makes sense for your game's performance budget (hint: vorbis is great).