Unity Addressables Pooling: Jump In!

Nov. 4, 2019
protect

If you plan on jumping into Unity Addressables Pooling, be careful. You better make sure the object pool is not empty.

[The original post with its formatting can be found at Unity Addressables Pooling]

Unity-Addressables-Pooling-Thumbnail

In previous posts, I showed you how to load content in your game without limits.

Well, sure there are limits, but you are not likely to reach them if you did a good job at implementing your asset content management system. And I'm pretty sure you'll get it right enough if you followed my advice.

But so far, I can say we are missing one important piece of the puzzle, though. I mean, we're missing many, but today I want to focus on a very specific one: latency.

What's latency?

Latency is the time it takes between starting something and finishing it. It is some sort of delay that we usually want to avoid.

You suffer latency when cooking your microwave popcorn, for instance. There, you start the microwave and have to wait for 3 to 5 minutes. And we want to eat popcorn right away, so this kind of latency is bad.

When we get into the field of games, things get worse than cooking popcorn.

In games, milliseconds matter. Everything above 20ms makes competitive multiplayer a bit more unfair.

But in this post, we're not talking about multiplayer games. We will be talking about the latency we suffer when we load and display an asset using Addressables for Unity.

And actually, we will do something about it.

We'll implement a simple Unity Addressables Pooling System.

Will you jump in the pool?

 

Quick Navigation (opens in a new tab)

Level 1 Developer: Simple Unity Addressables Loading

Level 2 Developer: Unity Addressables Pooling

    1. Warm-up the asynchronous pool

    2. Helping our Gameplay: take an item from the pool

    3. Saving CPU time: return the item to the pool

    4. Freeing up memory: disable the pool

Level 3 Developer: Smart Unity Addressables Pooling

    Performance

    Networking

    Automatic Pooling

The-Gamedev-Guru-Pool-Flag

Level 1 Developer: Simple Unity Addressables Loading

Yes, I know. We've done this several times.

We take a prefab, mark it as Addressable and we assign it to a script that loads the prefab whenever it makes sense.

And this gives you big benefits over traditional asset management workflows based on direct references. In short, using Addressables gives you...

To read more on this, visit my introductory post on Unity Addressables Benefits: 3 Ways to Save Your Game.

In this blog post, I'll stick to showing my tremendously complex sample project setup.

Unity Addressables Simple Setup

Unity-Addressables-Pooling-Initial-Setup

Unity Addressables Simple Setup

Oh nevermind, it was just a prefab instantiated through the Addressables API...

This works most of the time just fine for any game.

However...

This loading and instantiation process has some latency to it. Unity has to fetch the required asset bundle, load the prefab and its dependencies and instantiate.

The loading process should take well below 1 ms.

But things get messy when we add more complexity to this object. If we add animators, particle systems, rigid bodies and such, Unity can surely end up stealing 10 ms away from us. Activating these components can take a significant amount of time.

And if the asset bundles are served over the network and they were not ready, then we're speaking of seconds, even minutes.

How terrifying would your game be if by the time your final boss is spawned the player already reached the end of the dungeon?

This is my guess: as terrifying as profitable.

A typical solution in Unity relies on adding object pools

There're many object pools you can find online for Unity. The issue is, they're not Addressables-ready.

But now, you'll get one.

The-Gamedev-Guru-Laptop-Dark

Level 2 Developer: Unity Addressables Pooling

Let me warn you here: the needs for a pooling system greatly vary from project to project.

Here I'll be giving you a simple system that you can tweak to match your needs.

This is what you'll want from this pooling system:

In case you were wondering: yes, I re-used the icons from the previous section. Busy times here.

Before we jump into the code, I'll show you the test I prepared.

 

1. Warm-up the asynchronous pool

By now, the prefab and its content are not loaded in memory.

The pool is enabled and loads the prefab based on Addressables.

Then, it instantiates several objects and deactivates them all, paying the price of AwakeStartOnEnable and OnDisable.

By now, the prefab contents are in memory.

Addressables Pooling: Warm-up

Unity-Addressables-Pooling-Step-1

Addressables Pooling: Warm-up

 

2. Helping our Gameplay: take an item from the pool

A user takes an item from the pool and puts it somewhere in the scene through the synchronous method Take().

The user pays the activation (OnEnable) time, which depends on the complexity of their prefab.

Addressables Pooling: Take

Unity-Addressables-Pooling-Take

Addressables Pooling: Take

 

3. Saving CPU time: return the item to the pool

The user gets tired of their new toy and returns it to the pool.

The pool deactivates it and puts it under its hierarchy, paying the price of OnDisable.

Addressables Pooling: Return

Unity-Addressables-Pooling-Return

Addressables Pooling: Return

 

4. Freeing up memory: disable the pool

After some time, we know we will not need this item anymore.

We disable the pool and therefore it'll free up all the used memory, even though the indirect reference is still present in the pool.

Addressables Pooling: Disable

Unity-Addressables-Pooling-Disable

Addressables Pooling: Disable

 

The strength of this method relies on memory management. We pay the memory price when we decide to.

With traditional Unity Object Pools, we paid the memory overhead all the time, even if the prefab was never instantiated.

 

Now, how does the code look like?


public class GamedevGuruPoolUserTest : MonoBehaviour
{
    [SerializeField] private AssetReference assetReferenceToInstantiate = null;

    IEnumerator Start()
    {
        var wait = new WaitForSeconds(8f);
        
        // 1. Wait for pool to warm up.
        yield return wait;
        
        // 2. Take an object out of the pool.
        var pool = GamedevGuruPool.GetPool(assetReferenceToInstantiate);
        var newObject = pool.Take(transform);
        
        // 3. Return it.
        yield return wait;
        pool.Return(newObject);
        
        // 4. Disable the pool, freeing resources.
        yield return wait;
        pool.enabled = false;
        
        // 5. Re-enable pool, put the asset back in memory.
        yield return wait;
        pool.enabled = true;
    }
}

 

JikGuard.com, a high-tech security service provider focusing on game protection and anti-cheat, is committed to helping game companies solve the problem of cheats and hacks, and providing deeply integrated encryption protection solutions for games.

Read More>>