Loading assets from Asset Bundles for Play Asset Delivery in Unity

1.6k views Asked by At

I am currently maintaining an old project which I would very much like to keep the old code and avoid refactoring of the old code if it's possible. To upload it to google play store, I have used play asset delivery PAD system which is requiring asset bundle system.

I am able to successfully loading asset bundles in an asynchronous way with coroutines following the tutorials.

My question is that; is it possible to access bundled assets without changing original way of accessing such as

UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/Prefabs/Game/Tiles/Tile.prefab", typeof(Tile)) as Tile;

instead of implementing the following code?

var assets = AssetDatabase.FindAssets("t:Sprite", 
        new[] {"Assets/Images"});
    foreach (var guid in assets) 
    {
        string assetPath = AssetDatabase.GUIDToAssetPath(guid);
        Sprite loadedSprite = null;
        if (imageDictionary.TryGetValue(assetPath, out loadedSprite) 
            == false)
        {
            loadedSprite = AssetDatabase.LoadAssetAtPath<Sprite>(
                assetPath);
            if (loadedSprite != null)
            {
                imageDictionary.Add(assetPath, loadedSprite);
                AddImageToList(assetPath, loadedSprite);       
            }
        }
    }

I am currently using 2 asset bundles consists of texture and spine animation folders.I have created 2 asset bundles which are texture folder (110 MB) and spine animation bundle (23 MB) and base asset. When I created the aab file, file size increased to 370 MB. (I know that I need to split my bundles to match play store requirement of 150 MB for base assets but this is a different issue.)

And also, my educated guess is that, unity is not only adding asset bundles but also adding assets in old fashioned way which is an issue might relate to this question.

2

There are 2 answers

0
Rodrigo Abreu On

@Alp, Google's PAD tutorial is quite limited and it only guesses you're trying to access textures, which might not be always the case. You can actually access the bundles directly, and load them into memory, and if you've packed your prefab as an assetBundle, when you load your assetBundle this way you can just cast it to a GameObject later and then use the prefab as a GameObject to instantiate it or do whatever you need.

There is only one official way of loading asset bundles if you packed them as install-time,

public static IEnumerator LoadAssetBundleFromMemoryAsync(string packName, string assetName, Action<AssetBundle> callback)
{
    var packRequest = PlayAssetDelivery.RetrieveAssetPackAsync(packName);
    while (!packRequest.IsDone) 
    {
       yield return null;
    }

    AssetLocation location = packRequest.GetAssetLocation(assetName);
    long size = (long)location.Size;
    long offset = (long)location.Offset;
    
    using (Stream stream = File.OpenRead(location.Path)) 
    {
       byte[] bytes = new byte[size];
       stream.Seek(offset, SeekOrigin.Begin);
       stream.Read(bytes, 0, bytes.Length);

       AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(bytes);
       yield return request;

       callback(request.assetBundle);
    }
}

And then, I also figured out an unofficial way of loading asset bundles directly from file. It turns out that the location we get for a given assetBundle is going to be inside the split apk module with the same name as the assetPack. But if we just replace the split_pack.apk with base.apk, we can actually read it directly from the File without needing to actually pre-load the bundle into memory. Load from memory will put it into memory in advance, while using the LoadFromFile way, you can actually load the bundle to memory when it's needed. Something like this:

public static IEnumerator LoadAssetBundleFromFileAsync (string packName, string assetName, Action<AssetBundle> callback) 
{
    var packRequest = PlayAssetDelivery.RetrieveAssetPackAsync(packName);
    while (!packRequest.IsDone)
    {
        yield return null;
    }

    AssetLocation location = packRequest.GetAssetLocation(assetName);
    string path = location.Path.Replace($"split_{packName}.apk", "base.apk");    
    string basePath =  $"{path}!assets/assetpack/{assetName}";

    AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(basePath);
    while (!request.isDone) 
    {
        yield return null;
    }

    callback(request.assetBundle);
}

You could also maybe only return the 'assetPath' and load it later. These are only for install-time asset packs.

If you're trying to load any file from a fast-follow or on-demand asset pack, then all you need is the location.Path to load it from, as a raw file.

public static IEnumerator GetAssetLocationFromAssetPack (string packName, string assetName, Action<string> callback) 
{
    PlayAssetPackRequest packRequest = PlayAssetDelivery.RetrieveAssetPackAsync(packName);
    while (!packRequest.IsDone) 
    {
        yield return null;
    }
    
    AssetLocation location = packRequest.GetAssetLocation(assetName);
    callback(location.Path);
}

I hope it helps, and be wary that any folder or assets contained in your StreamingAssets if also set to be in an assetPack will be added in double, everything contained in StreamingAssets will be in your base.apk (within the AAB), so if you have something from StreamingAssets that will actually be loaded from a pack, delete it from Streaming to avoid an AAB bigger than expected.

0
Lee On

I was also facing the same problem. The initially built AAB file exceeded 150MB and could not be uploaded to the store. But I found a way to upload.

order

  1. Put the build scene in "File - BuildSetting - Scene In Build".

  2. Check "Build App Bundle" in "File - BuildSetting".

  3. Close BuildSetting and click "Google - Build Android App Bundle" Build. (In this process, I did not create an AssetBundle. I used the original program as it was)

  4. Ignore the large size alarm in the middle of the build.

  5. When the build is completed, the size will be larger than 150MB. Don't worry about the size of the build file and upload it to the Google console.

"Google - Build Android App Bundle" The file built with this goes up. I hope the problem is resolved.