Using Addressables along with Google Play Asset Delivery.

Hello! We are developing Unity game using Addressable Asset System. It is really great feature and it solves many problems. But we stuck in need to convert our game in Instant to deploy in Google Play Instant platform. Here is facts we need to follow:

  1. We use Addressables everywhere in the project and follow documentation regarding using and deploying assets.
  2. We need to deploy Android App Bundle file that includes apks plus all Asset Bundles with On Demand feature enabled (they should serve as remote asset bundles).
  3. The only way we found to pack Android App Bundle along with assets is to use Google Play Asset Delivery package. It can pack asset bundles and provide methods to load them at runtime.

The problem is that we don’t know how to use Addressable Assets Systems to use assets packed by Play Assets Delivery or pack Addressables Assets in .aab to use them On Demand.

Is there such a way?

7 Likes

Same here, I am in the process of converting my content to addressables, and was looking into best priced cdn, when I stumbled upon Google Play Asset Delivery…
Is there a way to combine both?

2 Likes

I’m also interested.

2 Likes

OMG. we need this

2 Likes

I’m need it too.

Here a quite detailed explanation about how addressable may be used with asset delivery.

I literally did this yesterday :slight_smile:
Thnk @Fuduin for linking, I’ll repost it here

(note: updated post)

Steps:

Preprocessor to remove all bundles (except catalog.bundle) from streaming assets folder
This is to not include them in the base build, they will be added via the AssetPackConfig.

public class AddressablesPlayerBuildAndroidProcessor : IPreprocessBuildWithReport
{
    public int callbackOrder => 2;
 
    public void OnPreprocessBuild(BuildReport report)
    {
        IEnumerable<string> bundles = Directory.GetFiles(Addressables.PlayerBuildDataPath)
            .Where(file => file.EndsWith(".bundle"))
            .Where(file => !file.EndsWith("catalog.bundle"))
            .ToDictionary(file => file, AssetPackBuilder.GetAssetPackGroupSchema)
            .Where(pair => pair.Value != null)
            .Select(pair => pair.Key);
        foreach (string bundle in bundles)
        {
            File.Delete(bundle);
        }
    }
}

Callback order is import as you want it happening after the addressables Preprocessor, which copies the asset bundles to the StreamingAssets folder (PlayerBuildDataPath).

Generate AssetPackConfig based on all other bundles.
This is called during our build process after the addressables have been processed.

Update: Use AssetPackGroupSchema to determine which bundles should be included in an asset pack.

class AssetPackBuilder
{
     public static AssetPackConfig CreateAssetPacks()
     {
          IEnumerable<Tuple<string, AssetPackGroupSchema, string>> bundles = Directory.GetFiles(Addressables.BuildPath)
              .Where(file => file.EndsWith(".bundle") && !file.EndsWith("catalog.bundle"))
              .Select(file => new Tuple<string, AssetPackGroupSchema, string>(file, GetAssetPackGroupSchema(file), Path.GetFileNameWithoutExtension(file)))
              .Where(pair => pair.Item2 != null);
          foreach (var bundle in bundles)
          {
              assetPackConfig.AssetPacks.Add(bundle.Item3,     bundle.Item2.CreateAssetPack(bundle.Item1));
          }
          return assetPackConfig;
     } 

     public static AssetPackGroupSchema GetAssetPackGroupSchema(string bundle)
     {
          return AddressableAssetSettingsDefaultObject.Settings.groups
              .Where(group => group.HasSchema<AssetPackGroupSchema>())
              .Where(group => Path.GetFileName(bundle).StartsWith(group.Name))
              .Select(group => group.GetSchema<AssetPackGroupSchema>())
              .FirstOrDefault();
     } 
}

the result is passed to Bundletool.BuildBundle

Add custom AssetBundleProvider to asset bundles.
Note: I’m using a very custom AssetBundle Provider, which handles delivering asset bundles synchronously after initial launch. As that is part of project I work on, I’m unable to share the entire code. But it is based on the default unity implementations.

Basic idea is that after checking if file path exists locally and before using a web request. The provider will check if the bundle is part of an asset pack. Here I do it very quickly by checking if the path starts with RuntimePath and ends with .bundle.

    internal class CustomAssetBundleResource : IAssetBundleResource
    {
...
        private void BeginOperation()
        {
...
            if (File.Exists(path))
            {
                ...
            }
            else if (TryHandleAssetPackFileAsynchronously(path))
            {
                return;
            }
            else if (ResourceManagerConfig.ShouldPathUseWebRequest(path))
...
        }

        private bool TryHandleAssetPackFileAsynchronously(string path)
        {
            if (!path.StartsWith(Addressables.RuntimePath) || !path.EndsWith(".bundle"))
            {
                return false;
            }
            string assetPackName = Path.GetFileNameWithoutExtension(path);
            playAssetPackRequest = PlayAssetDelivery.RetrieveAssetPackAsync(assetPackName);
            playAssetPackRequest.Completed += request => OnPlayAssetPackRequestCompleted(assetPackName, request);
            return true;
        }

        private void OnPlayAssetPackRequestCompleted(string assetPackName, PlayAssetPackRequest request)
        {
            if (request.Error != AssetDeliveryErrorCode.NoError)
            {
                m_ProvideHandle.Complete(this, false, new Exception($"Error downloading error pack: {request.Error}"));
                return;
            }
            if (request.Status != AssetDeliveryStatus.Available)
            {
                m_ProvideHandle.Complete(this, false, new Exception($"Error downloading status: {request.Status}"));
                return;
            }
            var assetLocation = request.GetAssetLocation(assetPackName);
            m_RequestOperation = AssetBundle.LoadFromFileAsync(assetLocation.Path, /* crc= */ 0, assetLocation.Offset);
            m_RequestOperation.completed += LocalRequestOperationCompleted;
        }
....
}

It’s also possible to load the bundle from the asset pack synchronously :slight_smile: (atleast for fast-install, haven’t tested rest.) (note: this is a requirement for the project I work on, so it’s great that it works ;))

        private bool TryHandleAssetPackFileSynchronously(string path)
        {
            if (!path.StartsWith(Addressables.RuntimePath) || !path.EndsWith(".bundle"))
            {
                return false;
            }
            string assetPackName = Path.GetFileNameWithoutExtension(path);
            playAssetPackRequest = PlayAssetDelivery.RetrieveAssetPackAsync(assetPackName);
            Exception exception = null;
            if (playAssetPackRequest.IsDone)
            {
                // asset pack was downloaded on initial launch of the game, so it should be done when loading it synchrounsly.
                var assetLocation = playAssetPackRequest.GetAssetLocation(assetPackName);
                m_AssetBundle = AssetBundle.LoadFromFile(assetLocation.Path, /* crc= */ 0, assetLocation.Offset);
            }
            else
            {
                exception = new Exception($"Asset Pack was not retrieved asynchronously: '{assetPackName}'.");
            }
            m_ProvideHandle.Complete(this, m_AssetBundle != null, exception);
            return true;
        }

Download progress
This is currently not working because addressables still thinks of the asset bundle as being a local bundle and not a remote.
So when it computes the download size it returns 0. The data for this is generated during the building of the asset bundles, changing this would require a custom build script (copy & changing the existing default one doesn’t work as it depends on a lot of internals…).

The solution to this is to call PlayAssetDelivery.GetDownloadSize for each pack that needs to be downloaded (sadly no combined call for this).

improvements
for production ready code, you’ll probably will need to handle things like waiting for wifi (for large packs larger than 150MB), errors and per bundle configuration so you can specify the AssetPackDeliveryMode for each bundle (probably via adding a Schema to the Group).

    [DisplayName("Play Asset Delivery")]
    public class AssetPackGroupSchema : AddressableAssetGroupSchema
    {
        public enum AssetPackDeliveryMode
        {
            InstallTime = 1,
            FastFollow = 2,
            OnDemand = 3,
        }
      
        [SerializeField]
        AssetPackDeliveryMode deliveryMode;
    }

note
the current game I’m working is in no rush to add support for this as we are currently projected to be at the AAB cap in 8 to 9 months :), but it was a fun exercise and test to see if this would work.

7 Likes

Stupid spam protection not allowing me to update my post :frowning: saw it was missing a method in the AssetPackGroupSchema

        public AssetPack CreateAssetPack(string bundle)
        {
            return new AssetPack
            {
                DeliveryMode = (PlayAssetPackDeliveryMode) deliveryMode,
                AssetBundleFilePath = bundle
            };
        }
1 Like

Hey @Fuduin thank you for the link!
@supersolid-jelte much appreciate for the full solution. I’ll check it on occasion. This is a great feature!

For those interesting making this a package: GitHub - jelte/be.khepri.play.assetdelivery.addressables: Android Play Asset Delivery support for Unity Addressables

4 Likes

This should work out of the box and I don’t understand why Unity has not already deliver a solution to be compatible with Play Asset delivery. @unity_bill can we expect a solution soon ?

2 Likes

Hi, how use it, from editor menu?

@pamfeeling I’ve just released the first Release Candidate. Hopefully the README provides enough information :slight_smile:
It’s a little untested at the moment, but I got it planned to be included in the next release of our game, scheduled mid-November. At which point this implementation will have gone through a proper QA process and not just dev-testing.

1 Like

First post is from june and still no Unity staff answer. I’m have already switched to AssetBundle instead AddressableAssets but it’s quite note as convenient as AddressableAsset. We really need a solution to use them. Unity push users to use it and still provides no solution to use it with Google Play Asset Delivery.

@unity_bill , @TreyK-47 we need an update/answer/solution here.

Thanks

2 Likes

Can someone from Unity provide some comment on this? Is there an ETA? Or are you not going to do this? It’s been 5 months.

I’ll flag this for the team, and will pass along any updates they have to share!

Finally an answer ! Thanks.

Please, use a red flag :wink:

Sorry for missing this thread for so long. The short answer is, we are currently looking into this (literally had meetings over the past couple weeks with various teams in Unity to get our heads wrapped around it). How we’ll support this in the future is still a little fuzzy. Perhaps a native feature within addressables, perhaps with clear documentation, perhaps something else.

Either way we do know this needs some guidance.

In the interim, I can give advice based on my current understanding of Google’s plugin, though I admit I could misunderstand some aspects.

Overall, my advice is pretty close to what was already posted in this thread by @supersolid-jelte . The only alterations I might give would be that I think some situations could simplify the flow, but the overall description is likely best for many cases.

My short version…
To build the content, you currently need the Google plugin to build the bundles into their format. But it’s worth noting, their tool takes bundles as an input, and Addressables build gives bundles as an output. So you just need to set the addressables build output path to wherever you are building the asset pack from. In the sample above it’s building to StreamingAssets and then moving the files. Not sure why that step is needed.

At runtime, you have a couple options. If you want content on-demand, then you need the custom provider that is shown above. If you are good with the entire download happening in advance, then I believe you can either just set a different load path, or at minimum have a much simpler provider. In this instance, it would not be Addressables triggering the download.

Again, I know a high level guide isn’t quite what everyone is looking for. We are chasing down a good solution now and will keep you posted.

5 Likes

Any updates when Google Play Asset Delivery with Unity Addressable will be supported?
Can you provide an example or tutorial how to make it work for now?

5 Likes

I would really appreciate an “official” guide on this, from mid-2021 Google is forcing us to use the Android App Bundles with its 150MB limitation, the widely used apk + obb file combination will not be possible anymore!

5 Likes