Just one more song...
Those famous last words of a videogame musician convincing a programmer to add the last audio track to the game that was about to be released.
What could possibly go wrong?
I'll tell you: that last song added a mere water drop to a glass that was about to spill.
And effectively, it spilled.
These extra 10MB added by the song were needed when Bobby received an SMS from his girlfriend asking him to do the laundry.
And so the Operating System decided to take its memory back. The evil OS killed the game Bobby was playing for the last two hours.
And you know what was the funny part? The savegame got corrupted.
Do you know what our friend Bobby did?
I'll tell you what he didn't do.
He did not buy a more powerful device.
Instead, he furiously flipped the table that was in front of him. Only that the table was made out of glass.
There's something Bobby did before cleaning all the shards from the floor. He promised to himself to do all he could to prevent this from happening to other users.
Bobby went to the app store and wrote the most famous 1-star review in history. People emotionally connected so well with him that his review got incredibly upvoted. So upvoted, that it surpassed and eclipsed all 5-star reviews.
And indeed, no more crashes ever happened to any other user, because new players held their fingers off the install button.
What could Bobby have done instead?
Well, Bobby could have messaged the developer instead to send him the article you're about to read.
You've seen the potential benefits of Addressables and you want to give it a shot in your game. But there're so many possibilities! Where to start with your Unity Addressables Migration? Here's an option for you: Music.
[The original blog post with its formatting can be found at Unity Addressables Migration for Music]
Lowering your memory consumption to reduce crashes? Increasing performance? Reducing loading times?
No matter what your goal is. If you want to start using Addressables right away, you'll experience the Unity Addressables Migration process.
Migration is nothing else than moving from where you are to the place you want to be.
In the context of this series, we'll be migrating the asset management system you're currently using over to an Addressables-based one.
Yes, even if you're not aware of what an asset management system is, you're already using one by default: Unity's direct references.
As I'll show you later, you can say you have a direct reference to an asset every time you assign this asset from the Project view to a field of a component in the inspector. For example, assigning a sound to the field AudioClip of an AudioSource is considered as having a direct reference to that sound.
But what's the crack with these direct references?
This should be your main concern: Unity will automatically have directly referenced assets loaded in memory. And there's nothing you can do about it other than not having that direct reference present.
What a burden.
The alternative is using indirect references.
And the benefit?
With Addressables you choose when and whether to load the asset you're pointing to. Particles, prefabs, textures, music, you name it. By doing this you'll reduce memory consumption, increase performance and gain other several huge advantages such as the possibility of shipping DLCs etc..
In this blog post you and I will start the level-up journey and migrate one of the most common types of content in a beautifully crafted game: soundtracks.
Quick Navigation (opens in new tab)
Unity Addressables Migration: Requirements
Level 1 Developer: Directly Referenced AudioClips
Level 2 Developer: Indirectly Referenced AudioClips
Unity Addressables Migration: Where do we go from Here?
Unity Addressables Migration: Requirements
This one is easy, unless you're stuck to an old version of Unity, that is.
You'll want to install the Unity Addressables Package for Unity 2019+.
I posted a step-by-step guide on my Unity Addressables Tutorial blog post, but you don't have to go through it now. This is what you want instead:
- Open Unity and go to Window → Packages to open the package manager interface
- Scroll down and look for the Addressables Package
- Install it
Press start to begin our quest
Level 1 Developer: Directly Referenced AudioClips
We'll start our quest on leveling up by migrating one of the easiest assets: music.
I'll assume you have a global MusicPlayer object laying around your project. That guy has an AudioSource component attached to it so we can play the music of our choice through its AudioClip component.
Generally speaking, we also have a script that contains a list of tracks to play, so we play them after another.
Below you see an example of a direct reference hidden in plain sight.
Direct Reference to an AudioClip
Coming back to our topic, you really don't want to have too many direct references to different AudioClips at once. That would mean, they are all loaded in memory, even though we are potentially playing only one song.
Having long AudioClips can and will increase the memory usage of your application along with your loading times.
You'll be stealing from your memory budget instead of spending it on the actual cool stuff.
Either you have to be very careful with the direct references you use or, even better, you can use Unity Addressables to profit from indirect references.
Naturally, you and I are going for the second approach here.
Level 2 Developer: Indirectly Referenced AudioClips
Let your performance explosion begin.
Start the migration by navigating through our music tracks in the Project View. In my case, my audio clips are called 8-bit-music-track-1, 8-bit-music-track-2 and so on.
With each AudioClip selected, we then tick their Addressables checkbox in the inspector.
Unity Addressables Migration: AudioClip import settings
These AudioClips are now part of the Addressables family and they're ready to be indirectly referenced.
About our next task...
With great power comes great responsibility
These indirect references will not manage themselves!
We'll create a neat MusicPlayer script that will play each music track one after another. We'll add this script to a new game object of the same name.
So we go over to type some basic code.
01: namespace TheGamedevGuru
02: {
03: public class MusicPlayer : MonoBehaviour
04: {
05: [SerializeField] private AudioSource _audioSource = null;0
06: [SerializeField] private AssetReference[] _soundtracks = null;
07: private int _playingSoundtrack = 0;
08:
09: IEnumerator Start()
10: {
11: while (true)
12: {
13: var currentOperationHandle = _soundtracks[_playingSoundtrack].LoadAssetAsync();
14: yield return currentOperationHandle;
15: var newAudioClip = currentOperationHandle.Result;
16: _audioSource.clip = newAudioClip;
17: _audioSource.Play();
18:
19: yield return new WaitUntil(() => _audioSource.isPlaying == false);
20:
21: _audioSource.clip = null;
22: Addressables.Release(currentOperationHandle);
23:
24: _playingSoundtrack = (_playingSoundtrack + 1) % _soundtracks.Length;
25: }
26: }
27: }
28: }
This sample script does quite a few simple things.
First, in line 5 we keep a (direct) reference to an audio source component. This component is purely behavior, it has (almost) no data associated with it, so a direct reference is the way to go.
The referenced AudioSource component will play the audio clip of our choice later on.
In line 6 we keep an array of indirect references to sound tracks that will be played sequentially. Each element of this array corresponds to a track to play while the user goes through their mission. What is critical here is to notice that we're using AssetReference to hold indirect references to the sound tracks. If we were to use direct references, we'd have an array of AudioClip instead.
But using indirect references this way will prevent Unity from automagically loading them all and skyrocketing your and my memory consumption and loading times.
Unity Addressables Indirect References to AudioClips
In line 7 we just keep an integer to know which soundtrack index we're currently playing. Bookkeeping and such.
Then, in line 9, we tell Unity to spawn a coroutine to handle the main loop of our logic to play music. There are several ways of doing the same without coroutines, but I'll stick to clarity.
What's that in line 13?
There, my friend, is where all the magic juice happens.
We command Addressables to load the AudioClip that our integer number points to. As this takes some time, we wait for it to finish in line 14 before proceeding.
And when the loading is done? We play it! The resulting AudioClip can be found in the next line under the Result field of the handle we waited on.
We then assign the newly loaded clip in line 16 and proceed to play it.
BAM!
What was that?
That was an explosion of sound
Your player starts spilling cold sweat and feels the adrenaline rush
All of this thanks to the creepy music you just started pushing into their ears
But all tracks sadly come to an end. So we wait for it to finish in line 19 and then we unassign that AudioClip from the AudioSource.
Finally, line 22 releases the data we previously loaded through the Addressables API.
All clean like a (well-taken care of) baby.
In line 24 we point to the next AudioClip to load and play in the next iteration of the loop. Rinse and repeat.
Hey, we got our digital jukebox up and running in a matter of minutes. Not bad.
But what's the benefit?
Now your game will be super performant
Your game will hold only one track in memory at a time
(instead of 4+)
This solution scales just well, period.
As a side note, AudioClip's Load Type field can be set to streaming. This gives you the false impression that you'll not profit from Addressables.
I'll make the argument that both streaming and compressed in memory give you memory gains, but this happens at the cost of increasing your CPU load. Addressables will give you the maximum performance such as in Decompress On Load while keeping memory overhead low, as you'll only load the music you're currently playing.
Unity Addressables Migration: Where do we go from here?
We barely scratched the surface of Addressables.
But now your music composer has now some new toy to play with. That's all that matters.
In the next post, we'll be migrating something more.. how to say, challenging. But I'm not spoiling it yet.
If I arose your curiosity on the topic, have a look at the Addressables Tutorial I wrote for you.
Till then, I'd be happy if you commented below and shared your experiences with Addressables with the community.
Ruben