Basic

July 21 2020

5 comments

Unity Addressables & SpriteAtlas: How to Efficiently Use Sprites

By Rubén Torres Bonet

July 21, 2020


__CONFIG_colors_palette__{"active_palette":0,"config":{"colors":{"6c4de":{"name":"Main Accent","parent":-1},"67ed2":{"name":"Accent Dark","parent":"6c4de","lock":{"saturation":1}}},"gradients":[]},"palettes":[{"name":"Default","value":{"colors":{"6c4de":{"val":"var(--tcb-skin-color-0)"},"67ed2":{"val":"rgb(59, 65, 63)","hsl_parent_dependency":{"h":160,"l":-0.05,"s":0.04}}},"gradients":[]},"original":{"colors":{"6c4de":{"val":"rgb(51, 190, 127)","hsl":{"h":152,"s":0.57,"l":0.47}},"67ed2":{"val":"rgb(59, 65, 63)","hsl_parent_dependency":{"h":160,"s":0.04,"l":0.24}}},"gradients":[]}}]}__CONFIG_colors_palette__

Are you using Unity UI or Sprite Renderers in your game?

You might then have heard of the Unity SpriteAtlas feature...

SpriteAtlas helps you reduce your game draw calls when rendering UI or sprites in Unity.

However, sprite atlases are hard-to-tame beasts that can bite your hand off.

For example, a single reference to a SpriteAtlas' sprite will make you load the entire atlas in memory, even if you just need one sprite... or even if you're not actively using the sprite itself.

Just the reference can skyrocket your memory usage and loading times.

What you want is to have a say about how Unity manages your sprite atlas' memory so you don't choke your game performance budget.

Well, you can do exactly that by using the super Unity Addressables & SpriteAtlas wombo combo.

Can they work together?

You bet they can.

TL;DR:

  • Mark only your SpriteAtlas as an addressable asset
  • Use AssetLoadAsync to load a specific sprite from an addressable sprite atlas...
    • Through a AssetReferenceAtlasedSprite variable, OR
    • Through a hardcoded subasset path such as "myspriteatlaskey[myspritename]
  • Use Addressables.Release to unload your sprite

Who's Ralf and Why Is Ralf Furious?

It's a sunny Tuesday in summer.

You wake up and, for some reason, you feel extremely happy.

You're so happy that you start singing a made-up song about how today you are going to improve the architecture of your game.

It's been decided. You take no more crap.

So you read some documentation and decide your next step: you're going to put your heavy sprites in a single SpriteAtlas.

That must be a great idea, you think.

Just like you expected, you manage to reduce:

  • Draw calls by 10x
  • Memory usage (RAM + Flash).

So you hit the submit button and call it a day.

It was a great day... and you reward yourself with a pineapple pizza*.

* My italian girlfriend doesn't approve of this.

However, just as you're about to sleep, your phone rings... loudly.

"Wh... Who is it?", you ask, your voice trembling.

"I'm your boss, Ralf. You fu***d up the game. You're canned for good".

Hearing these words stuns you for very long 5 seconds, losing a stamina point in the process.

"W...ait what..?" You barely manage to ask as an eerie chill runs down your spine and cold sweat pours down your forehead.

"Nah I'm joking, you're not fired yet. But get your ass down here and fix the crash that happens in the score screen", Ralf says just before disconnecting.

Shaking badly anth fever symptoms, you boot up your PC and wait 10 minutes for Unity to load your project.

Eager, you get to the score screen and BAM.

The memory usage exploded...

because Unity loaded the entire sprite atlas in the worst moment possible

After some debugging, you realized that you left out a single reference to a sprite that you did not even draw.

That sprite reference was just there.

Smilying at you.

Waiting for the right moment to get you into trouble.

And... that sprite really got ya.

But you fixed it and learned the BIG lesson:

“Always be careful with memory in game development.”


Rubén
(The Gamedev Guru)

The Problem: Unity SpriteAtlas' Default Behavior

When you reference a sprite that lives within a SpriteAtlas, you load the entire atlas into memory and then use the specific region your sprite takes.

Packing and referencing your sprites unoptimally can have nasty side effects, such as crashes and increased loading times.

The first thing you will want to do in Unity when dealing with sprite atlases is to engineer your sprite atlases in a way that it maximizes the economy of your sprites.

That means, you want to pack together the sprites that you use at the same time.

But even if you pack your sprites perfectly, you might still run into trouble.

For instance, you might load the entire sprite atlas too early... when you cannot really afford to.

Imagine you have a complex UI system with TONS of sprites and sprite atlases... a game like The Sims with 4000+ expansions and millions of different item preview sprites.

Here's the truth: you can't pay the price of having all these sprite atlases loaded in memory at the same time. And yet, you need to load them on certain user interactions.

Bad news: just referencing them for later ("just in case") will actually load them immediately in memory, causing the evil final boss Ralf to fire you.

But you can keep your paycheck: we can use Addressables to load/unload your sprites on sprite atlases on demand, no matter the references you have.

In short: let's you choose when & how to load these sprite atlases.

Level Up: Unity SpriteAtlas & Addressables

[Check the Unity Addressables Benefits first if you don't know much about addressables]

Loading/unloading sprites from a sprite atlases becomes easy with Addressables.

Just mark the SpriteAtlas (not the individual sprites) as an addressable asset.

This is the wombo combo you're looking for:

public class AddressableImage : MonoBehaviour
{
public AssetReferenceAtlasedSprite addressableSprite;
IEnumerator Start()
{
var image = GetComponent<Image>();
yield return new WaitForSeconds(6);
// Option A) Load atlas & take a sprite
var asyncOperationHandle = addressableSprite.LoadAssetAsync<Sprite>();
// Option B) Addressable asset key + subasset selection
//var asyncOperationHandle = Addressables.LoadAssetAsync<Sprite>("Sprite Atlas[TheGamedevGuru-Sample-1]");
yield return asyncOperationHandle;
image.sprite = asyncOperationHandle.Result;
// Release at some point
yield return new WaitForSeconds(6);
image.sprite = null;
Addressables.Release(asyncOperationHandle);
}
}
public class AddressableImage : MonoBehaviour { public AssetReferenceAtlasedSprite addressableSprite; IEnumerator Start() { var image = GetComponent<Image>(); yield return new WaitForSeconds(6); // Option A) Load atlas & take a sprite var asyncOperationHandle = addressableSprite.LoadAssetAsync<Sprite>(); // Option B) Addressable asset key + subasset selection //var asyncOperationHandle = Addressables.LoadAssetAsync<Sprite>("Sprite Atlas[TheGamedevGuru-Sample-1]"); yield return asyncOperationHandle; image.sprite = asyncOperationHandle.Result; // Release at some point yield return new WaitForSeconds(6); image.sprite = null; Addressables.Release(asyncOperationHandle); } }
public class AddressableImage : MonoBehaviour
{
public AssetReferenceAtlasedSprite addressableSprite;

IEnumerator Start()
{
    var image = GetComponent<Image>();
    yield return new WaitForSeconds(6);
    
    // Option A) Load atlas & take a sprite
    var asyncOperationHandle = addressableSprite.LoadAssetAsync<Sprite>();
    
    // Option B) Addressable asset key + subasset selection
    //var asyncOperationHandle = Addressables.LoadAssetAsync<Sprite>("Sprite Atlas[TheGamedevGuru-Sample-1]");
    
    yield return asyncOperationHandle;
    image.sprite = asyncOperationHandle.Result;
    
    // Release at some point
    yield return new WaitForSeconds(6);
    image.sprite = null;
    Addressables.Release(asyncOperationHandle);
}
}

Let me translate that from C# to English...

It reads:

A Letter to Unity

Dear Unity,


Thanks for not loading this sprite atlas automatically as you would by default.


I know that you always tell me to use Addressables if I want control over its memory lifecycle, so that's exactly what I did (line 3).


Now, the time has come. Please load this sprite asynchronously (lines 11/14) and assign it to this blessed UI Image (line 17).


But don't forget: in a few seconds, I'll ask you to unload the whole thing from memory when I have no further use for it (line 22).


Thanks,

Your happy Unity Pro subscriber

The result?

You only pay the price when you really need it because Addressables lets you manage this memory yourself.

Doing this is now easier than ever with the subasset referencing possibility (both in inspector and code).

By using addressables with sprite atlases you will reduce your draw calls and your loading times.

Better performance, in turn, will engage your players further into your game.

Addressables Course - Forest

What's Next?

If you want to learn more about developing high-performing games that respect your memory boundaries, read my Unity Addressables Tutorial post.

It's a great one, I promise.

Happy #gamedev

~Ruben

  • How to use addressable assets in this case sprite atlas if the assets are used in a scene to build the UI and gameplay elements.
    Since the atlas is marked as addressable asset It won’t load when the scene is loaded? I still have that doubt.
    Is Addressable suppose to be used only at runtime? like how you showed in this post?

    • Hi Sathyaraj,
      When your UI or gameplay scene is loaded, Unity will see you have a indirect reference to the sprite atlas and therefore will NOT load it by itself.
      You can decide when to load it yourself like you saw in this post.
      You need to do this in runtime, correct, i.e. in code. The benefits are huge, though 🙂
      Ruben

  • Why all sprites of an atlas are including in the asset bundle although I set only the atlas as Addressable? There is also no direct reference of any sprite. Version 1.15.1.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

    Rubén Torres Bonet

    About the author

    Born Game Developer, now ready to help you develop better games. Primary programmer on Star Trek Bridge Crew (Oculus Quest), Diamond Dash. Programmer on Time Stall, Catan Universe, Anne Frank House VR, Jelly Splash, Blackguards Definitive Edition. I also worked in minor XR experiences for HoloLens and Vive for clients such as Audi and Volkswagen.

    You might also like

    Simplify Your Life With Unity Mesh Simplifier
    >