Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  2. Ever participated in one our Game Jams? Want pointers on your project? Our Evangelists will be available on Friday to give feedback. Come share your games with us!
    Dismiss Notice

Generating AssetBundles in a separate project for multiple platforms.

Discussion in 'Addressables' started by Murcho, Oct 10, 2018.

  1. Murcho


    Mar 2, 2009
    I'm trying to get my head around the Addressable system, but trying to apply it to my specific use case. We will have users uploading images to a server, which I want to then import into Unity, and export AssetBundles for both iOS and Android.

    My understanding is that I should be able to set up an empty project (referencing the Addressables package), import the image, mark it as an addressable asset, then perform a build stage to generate the AssetBundles. In this build stage a json file with information on the addressables is created, and if I were to provide this to my other application, it should be able to load it using Addressables.LoadCatalogsFromRuntimeData() and then be aware of these assets that weren't around at it's build time?

    This information was gathered from this post :

    I'm missing a couple of stages, so some direction on how to mark the assets programatically for Bundle Exports would be greatly appreciated.
    EirikWahl likes this.
  2. Murcho


    Mar 2, 2009
    Version : 0.3.5-preview

    OK, so I worked out how to build Addressable assets in one project and load them in another successfully. I have a few manual steps right now that I need to clear out, and I also think I came across a bug. For anyone interested in the process here's a step by step of what was needed. and where the bug was. I'll ask some further questions below as well so if any devs happen to see this I'd really like some guidance on how to extend the system.

    Project 1 (the project to generate the assets)
    • Add the assets to group(s) in the Addressables window.
    • In the AddressableAssetSettings ScriptableObject, set the remote path to the server you'll be hosting them on. In my case I tested with a public S3 bucket, so my remote path looked like:
    • For each of the groups, set:
      • LoadPath to RemoteLoadPath
      • BundleMode to Pack Separately (this might not be important, but I need separate bundles for individual assets)
    • In an editor script I call BuildScript.PrepareRuntimeData().
      • NOTE : This script is marked obsolete and will be replaced in the next version apparently, however it is what I had to work with here.
      • DEVS: There is a problem with calling this directly in regards to switching platforms.
        • e.g. If I'm on Android, and I call BuildScript.PrepareRuntimeData() passing in iOS as the Build Target, The editor switches to iOS, however the BuildTarget used for the output file paths is left as Android. I haven't tested whether the output bundles are iOS or Android formatted.
        • In order to work around this I would manually change platform in script, then using EditorPrefs and UnityEditor.Callbacks.DidReloadScripts work out if I had to call BuildScript.PrepareRuntimeData(). Very messy, but it worked.
      • DEVS: Second issue, is that I can't configure the settings.json output file location or name. I want to build each asset set out for iOS and Android, and so when doing this, the settings.json and catalog.json files are overwritten.
    • After each platform build, I would upload the output files to S3, maintaining folder structure, and renaming the settings.json to settings_platform.json so we could fetch them separately.
      • The files would be output to Assets/StreamingAssets/com.unity.addressables
    Project 2 (the project to fetch the assets)
    • Here is the example script I used to pull the settings.json and then load an addressable by string key. This is obviously missing proper checks.
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.AddressableAssets;
    3. using UnityEngine.UI;
    5. public class RemoteAssetLoadTest : MonoBehaviour
    6. {
    7.     [SerializeField]
    8.     private RawImage m_Image;
    9.     [SerializeField]
    10.     private string m_ImageRef = "";
    12.     void Start ()
    13.     {
    14.         Addressables.Initialize().Completed += (initOp) =>
    15.         {
    16.             Addressables.LoadCatalogsFromRuntimeData("").Completed += (op) =>
    17.             {
    18.                 Addressables.LoadAsset<Texture2D>(m_ImageRef).Completed += (loadOp) =>
    19.                 {
    20.                     m_Image.texture = loadOp.Result;
    21.                 };
    22.             };
    23.         };
    24.     }
    26. }
    So this works however there is a lot of manual intervention involved so I'd love some feedback from the devs on whether this was the right way to approach it? Being able to output completely separate files that don't overwrite each other would be ideal. I understand that the BuildScript class is marked deprecated to be replaced soon, just wondering if there is an intended way to do this in the current version.

    My next part is to understand how to create custom ResourceProviders. I found the AssetBundleProviderRemoteWebRequest example in the source, however there isn't an example of how to actually implement it in code. Any hints on how to do this?

    Thanks in advance.
    MNNoxMortem and EirikWahl like this.
  3. AustynPember


    Mar 14, 2018
    I appreciate you posting what you've done so far with this. I am trying to do mostly the same thing (with Azure instead.) What kind of progress have you made since?
  4. Murcho


    Mar 2, 2009
    I've put this on the back burner until the Addressables system matures a bit more. We are heading into production and I couldn't rely on an incomplete system.
  5. AustynPember


    Mar 14, 2018
    Thanks - I am still going to try to build out something similar to what you did. We only have to build out to UWP so it should cut down on some of the complexity or issues you had.
    I was wondering for the above part - what exactly goes into the cloud?
    Do you just push the App folder (the build) onto the repository? I dont think so because you're referencing Assets/StreamingAssets which isn't in the build, it's in the project?
    Also - where is m_ImageRef being set? It's just an empty string?
    Last edited: Dec 7, 2018
  6. deepakguptaPR


    Aug 20, 2019
    @Murcho : Any progress on loading Project B addressable assets in Project A? If Yes, could you please let us know the process.
    @unity_bill : Any update on Addressable Asset documentation? Our requirement is to have Project independent of main project where addressable assets would be built and uploaded to our CDN servers.
  7. Murcho


    Mar 2, 2009
    @deepakguptaPR Unfortunately I haven't been able to return to this system to try again.
  8. deepakguptaPR


    Aug 20, 2019
    @Murcho : We have successfully implemented the Addressable Assets to be independent of Main project. As of now we don't have blockers. If you need any help regarding that Please drop me a DM. Thanks
  9. Shii


    Nov 23, 2014
    @deepakguptaPR can you share details? I considering switching from classic assetbundles to addressable system and not sure how to properly generate bundles in one project and load in another. In my project I also planning to have 2 projects - one for asset packing (SDK) and another for asset loading (Main game). Idea is to store game resouces in the asset packs and later distribute them as addons/DLC so people can make their own packs and load into the game by just placing needed files into compiled game StreamingAssets folder.
  10. deepakguptaPR


    Aug 20, 2019

    In Asset packing project, Have AddressableAssetSettings's Remote Catalog Build path to RemoteBuildPath and Remote Catalog Load path to RemoteLoadPath. RemoteBuildPath will have default value as ServerData/[Build Target].

    For the AssetGroups, BuildPath has to be RemoteBuildPath and Load Path has to be RemoteLoadPath.

    Build Player Content in the Addressables Window. After Building, you will get files in [Project folder]/ServerData/[Build Target] folder. Upload all the files in the CDN server(We are using MicroSoft Azure blobs Storage].

    Code in main project to load catalog, assets. Code is not refactored as it was only for testing.

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.AddressableAssets;
    4. using UnityEngine.AddressableAssets.ResourceLocators;
    5. using UnityEngine.ResourceManagement.AsyncOperations;
    6. using UnityEngine.ResourceManagement.ResourceLocations;
    8. public class AssetLoader : MonoBehaviour
    9. {
    10.     public string catalogPath;
    11.     public List<IResourceLocation> locations;
    13.     void Start()
    14.     {
    15.         initAddressables();
    16.     }
    19.     void initAddressables()
    20.     {
    21.         Debug.Log("initAddressables");
    22.         AsyncOperationHandle<IResourceLocator> handle = Addressables.InitializeAsync();
    23.         handle.Completed += initDone;
    24.     }
    25.     private void initDone(AsyncOperationHandle<IResourceLocator> obj)
    26.     {
    27.         Debug.Log("Initialization Complete ==> " + obj.Status);
    28.         if (obj.Status == AsyncOperationStatus.Succeeded)
    29.         {
    30.             loadCatalog();
    31.         }
    32.     }
    34.     void loadCatalog()
    35.     {
    36.         Debug.Log("loadCatalog");
    37.         AsyncOperationHandle<IResourceLocator>  handle = Addressables.LoadContentCatalogAsync(catalogPath);
    38.         handle.Completed += loadCatalogsCompleted;
    39.     }
    40.     void loadCatalogsCompleted(AsyncOperationHandle<IResourceLocator> obj)
    41.     {
    42.         Debug.Log("loadCatalogsCompleted ==> " + obj.Status);
    43.         if (obj.Status == AsyncOperationStatus.Succeeded)
    44.         {
    45.             loadResourceLocation();
    46.         }
    47.         else
    48.         {
    49.             Debug.LogError("LoadCatalogsCompleted is failed");
    50.         }
    51.     }
    53.     void loadResourceLocation()
    54.     {
    55.         Debug.Log("loadResourceLocation");
    56.         AsyncOperationHandle<IList<IResourceLocation>> handle = Addressables.LoadResourceLocationsAsync([groupname]);
    57.         handle.Completed += locationsLoaded;
    58.     }
    59.     void locationsLoaded(AsyncOperationHandle<IList<IResourceLocation>> obj)
    60.     {
    61.         Debug.Log("locationsLoaded ==> " + obj.Status);
    62.         if (obj.Status == AsyncOperationStatus.Succeeded)
    63.         {
    64.             locations = new List<IResourceLocation>(obj.Result);
    65.             loadDependency();
    66.         }
    67.         else
    68.         {
    69.             Debug.LogError("locationsLoaded is failed");
    70.         }
    71.     }
    73.     void loadDependency()
    74.     {
    75.         Debug.Log("loadDependency");
    76.         AsyncOperationHandle handle = Addressables.DownloadDependenciesAsync("decor");
    77.         handle.Completed += dependencyLoaded;
    78.     }
    79.     void dependencyLoaded(AsyncOperationHandle obj)
    80.     {
    81.         Debug.Log("dependencyLoaded ==> " + obj.Status);
    82.         if (obj.Status == AsyncOperationStatus.Succeeded)
    83.         {
    84.             loadAssets();
    85.         }
    86.         else
    87.         {
    88.             Debug.LogError("dependencyLoaded is Failed");
    89.         }
    90.     }
    92.     private void loadAssets()
    93.     {
    94.         AsyncOperationHandle<IList<GameObject>> handle = Addressables.LoadAssetsAsync<GameObject>(locations, onAssetsCategoryLoaded);
    95.         handle.Completed += onAssetsLoaded;
    96.     }
    97.     private void onAssetsCategoryLoaded(GameObject obj)
    98.     {
    99.         SpawnItem(;
    100.     }
    101.     private void onAssetsLoaded(AsyncOperationHandle<IList<GameObject>> obj)
    102.     {
    103.     }
    105.     void SpawnItem(string addressableKey)
    106.     {
    107.         Debug.Log("SpawnItem ==> " + addressableKey);
    108.         AsyncOperationHandle<GameObject> asyncLoad = Addressables.InstantiateAsync(addressableKey,, Quaternion.identity);
    109.         StartCoroutine(progressAsync(asyncLoad));
    110.         asyncLoad.Completed += assetSpawned;
    111.     }
    112.     void SpawnItem(GameObject addressableObj)
    113.     {
    114.         Debug.Log("SpawnItem ==> " + addressableObj);
    115.         AsyncOperationHandle<GameObject> asyncLoad = Addressables.InstantiateAsync(addressableObj);
    116.         StartCoroutine(progressAsync(asyncLoad));
    117.         asyncLoad.Completed += assetSpawned;
    118.     }
    119.     private System.Collections.IEnumerator progressAsync(AsyncOperationHandle<GameObject> asyncOperation)
    120.     {
    121.         float percentLoaded = asyncOperation.PercentComplete;
    122.         while (!asyncOperation.IsDone)
    123.         {
    124.             Debug.Log("Progress = " + percentLoaded + "%");
    125.             yield return 0;
    126.         }
    127.         Debug.Log("Progress Done= " + percentLoaded + "%");
    128.     }
    129.     void assetSpawned(AsyncOperationHandle<GameObject> obj)
    130.     {
    131.         Debug.Log("Instantiate completed ==> " + obj.Status);
    132.     }
    133. }
    Let me know if you need more clarification
    Tayfe, mbussidk and jeremedia like this.
  11. Shii


    Nov 23, 2014
    Thank you! Very useful information. Instructions are clear. =) Sorry for late response, did not get notification about new thread message.
    deepakguptaPR likes this.
  12. jeremedia


    Apr 21, 2015
    @deepakguptaPR I'm having trouble implementing the code you've shared.

    Is the "catalogPath" string pointing at a local catalog file produced by the asset building project?

    What is the "[groupname]" referenced on line 56?

    Thanks for sharing your solution.
  13. deepakguptaPR


    Aug 20, 2019
    @jeremedia : Sorry for the late response, Did not get notification :(

    catalogPath is the url of the catalog file. For example, if you are using amazon S3 bucket as CDN, you will upload the catalog file generated from Asset building project to the S3 bucket. CatalogPath is the url of the CDN(S3 bucket url).

    GroupName is the label name which you used while building asset bundle. Though it is not mandatory to provide label.

    Hope this helps :)
  14. chrisk


    Jan 23, 2009
    Hi guys, I have one question.
    What happens when you have multiple packing projects?
    The reason I have multiple packing projects is that 1. my project is huge so for efficient, I like to divide them. 2. multiple people working on different projects, for example, I have people working only on characters and others working only on maps. It makes sense to have separate projects for each group.
    I really hope there is way to handle it.
  15. deepakguptaPR


    Aug 20, 2019
    @chrisk : You can load multiple catalog files.
  16. chrisk


    Jan 23, 2009
    Yup, I know you can load multiple catalogs but the problem is conflict management.
    If you build addressable from multiple projects and they use some shared assets such as shaders and textures, you will have a conflict.
    It's very common to have such conflicts but it's not very clear how to manage them.
    I think we need a global addressable system that manages the addressable and it handles the conflict because each individual project cannot know the conflict ahead of time.
    pahe4retro likes this.
  17. Pdpalma


    Aug 14, 2014
    What's refers the catalogPath variable? The cloud url?
  18. deepakguptaPR


    Aug 20, 2019
    Yes, catalogPath refers to cloud URL(catalog json file)
  19. deepakguptaPR


    Aug 20, 2019
    We have not tried packing from multiple projects. Not sure how to handle!