Hello fellow developers,
I require help figuring out what happens when you tick the box in the atlas window -
Include In Build and mark the atlas itself as an Addressable.
Does it get duplicated in the built in assets and the asset bundles?
How do direct references in prefabs to sprites in the atlas interact with the atlas? Do they actually use the atlas or do they ignore it and the sprite itself gets dragged into the asset bundles?
What’s even the best practise here?
There are so many possibilities, it’s kinda of confusing. If anyone knows how this system works I would be grateful if you could help me.
What was the result of this? I’m curious as well.
Is this explained anywhere? I am also curious what occurs with the sprite references, sprite textures that are in an addressable group and sprites being packed? Where do all the resulting packed sprites atlas textures go? How does that affect asset bundles?
from my own experimenting:
1) “include in build” ENABLED + marked addressable in remote bundle = atlas textures are duplicated, one copy will be packed with the build and one will be packed in the bundles.
Any client embedded assets that reference sprites will reference the built-in one, and stuff via addressables will reference the remote one. functionally it works fine, but from a memory management perspective, this is bad and should be avoided.
2) “include in build” DISABLED + marked addressable in remote bundle = one copy in a remote bundle.
Any references to sprites in the atlas that are embedded in the client resources (ex: UI prefabs with Image components that reference sprites) will show up as white squares, but will trigger SpriteAtlasRequested flow* (Unity - Scripting API: U2D.SpriteAtlasManager.atlasRequested), which you can setup to do late-binding in code, which will auto-magically re-assign all the sprites and make everything work. (you will have to go through this flow every time you unload / reload the atlas). You’ll need to load the sprite atlas asset explicitly via addressables for this, not the sprites.
I have not tested anything with sprite atlas variants.
*note on SpriteAtlasRequest flow: these calls don’t trigger in the editor in “Use Asset Database” mode.
sample code:
public class AssetManager
{
private Dictionary<SpritePreloadTag, List<SpriteAtlas>> _loadedSpriteSheets = new Dictionary<SpritePreloadTag, List<SpriteAtlas>>();
private Dictionary<string, SpriteAtlas> _guidToSpriteSheet = new Dictionary<string, SpriteAtlas>();
private Dictionary<string, Action<SpriteAtlas>> _lateBindingCallbacks = new Dictionary<string, Action<SpriteAtlas>>();
public AssetManager()
{
SpriteAtlasManager.atlasRegistered += (spriteAtlas =>
{
// this gets called anytime a sprite sheet is loaded explictly
//UnityEngine.Debug.Log("spriteAtlas loaded ???: " + spriteAtlas.name);
});
SpriteAtlasManager.atlasRequested += ((name, bindAction) =>
{
UnityEngine.Debug.Log("spriteAtlas requested: " + name);
_lateBindingCallbacks[name] = bindAction;
});
}
public async Task PreloadSprites(SpritePreloadTag tag)
{
Log.Debug("Start loading sprites tag: " + tag);
var preloadSet = new HashSet<AssetReference>();
var spriteSheetGuids = new HashSet<string>();
foreach (var guid in PreloadableUtility.GetSpriteSheets(tag))
{
spriteSheetGuids.Add(guid);
}
// this is here for sprites that are serialized in client packed prefabs.
// unity will strip them out at build time, and these callbacks will load them back
// into the UI prefabs
// we also keep track of these so we can explicetly unload them
foreach (var guid in spriteSheetGuids)
{
if (_guidToSpriteSheet.ContainsKey(guid))
{
Log.Debug(_guidToSpriteSheet[guid].name + " already loaded");
continue;
}
var result = await Addressables.LoadAssetAsync<SpriteAtlas>(guid).Task;
if (result != null)
{
if (!_loadedSpriteSheets.ContainsKey(tag))
{
_loadedSpriteSheets[tag] = new List<SpriteAtlas>();
}
_loadedSpriteSheets[tag].Add(result);
_guidToSpriteSheet[guid] = result;
Log.Debug("load " + result.name);
if (_lateBindingCallbacks.ContainsKey(result.name))
{
_lateBindingCallbacks[result.name].Invoke(result);
}
else
{
#if !UNITY_EDITOR
Log.Warn("no callback registered for spritesheet " + result.name);
#endif
}
}
}
stopwatch.Stop();
Log.Debug("sprite loading took " + stopwatch.Elapsed);
}
public void UnloadSpriteSheets(SpritePreloadTag tag)
{
Log.Debug("release sprite sheets: " + tag);
if (_loadedSpriteSheets.TryGetValue(tag, out var sheets))
{
_loadedSpriteSheets.Remove(tag);
foreach (var s in sheets)
{
foreach (var kvp in _guidToSpriteSheet)
{
if (kvp.Value == s)
{
_guidToSpriteSheet.Remove(kvp.Key);
break;
}
}
Addressables.Release(s);
}
}
else
{
Log.Error("no sprite sheets to unload for " + tag);
}
}
}
4 Likes
I know that thread is 2 years old, but I’d like to add one more solution here, cause this took a long time for me to figure it out.
my solution for handling SpriteAtlas + Addressable: