Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Generating AssetBundles in a separate project for multiple platforms.

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

  1. Murcho

    Murcho

    Joined:
    Mar 2, 2009
    Posts:
    309
    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 : https://forum.unity.com/threads/can...te-from-an-other-project.547369/#post-3637720

    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

    Murcho

    Joined:
    Mar 2, 2009
    Posts:
    309
    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;
    4.  
    5. public class RemoteAssetLoadTest : MonoBehaviour
    6. {
    7.     [SerializeField]
    8.     private RawImage m_Image;
    9.     [SerializeField]
    10.     private string m_ImageRef = "";
    11.  
    12.     void Start ()
    13.     {
    14.         Addressables.Initialize().Completed += (initOp) =>
    15.         {
    16.             Addressables.LoadCatalogsFromRuntimeData("https://s3-us-west-2.amazonaws.com/my-s3-bucket-name/Addressables/settings.json").Completed += (op) =>
    17.             {
    18.                 Addressables.LoadAsset<Texture2D>(m_ImageRef).Completed += (loadOp) =>
    19.                 {
    20.                     m_Image.texture = loadOp.Result;
    21.                 };
    22.             };
    23.         };
    24.     }
    25.  
    26. }
    27.  
    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

    AustynPember

    Joined:
    Mar 14, 2018
    Posts:
    17
    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

    Murcho

    Joined:
    Mar 2, 2009
    Posts:
    309
    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

    AustynPember

    Joined:
    Mar 14, 2018
    Posts:
    17
    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?
    *Edit*
    Also - where is m_ImageRef being set? It's just an empty string?
     
    Last edited: Dec 7, 2018
  6. deepakguptaPR

    deepakguptaPR

    Joined:
    Aug 20, 2019
    Posts:
    8
    @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

    Murcho

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

    deepakguptaPR

    Joined:
    Aug 20, 2019
    Posts:
    8
    @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

    Shii

    Joined:
    Nov 23, 2014
    Posts:
    11
    @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

    deepakguptaPR

    Joined:
    Aug 20, 2019
    Posts:
    8

    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;
    7.  
    8. public class AssetLoader : MonoBehaviour
    9. {
    10.     public string catalogPath;
    11.     public List<IResourceLocation> locations;
    12.  
    13.     void Start()
    14.     {
    15.         initAddressables();
    16.     }
    17.  
    18.  
    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.     }
    33.  
    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.     }
    52.  
    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.     }
    72.  
    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.     }
    91.  
    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(obj.name);
    100.     }
    101.     private void onAssetsLoaded(AsyncOperationHandle<IList<GameObject>> obj)
    102.     {
    103.     }
    104.  
    105.     void SpawnItem(string addressableKey)
    106.     {
    107.         Debug.Log("SpawnItem ==> " + addressableKey);
    108.         AsyncOperationHandle<GameObject> asyncLoad = Addressables.InstantiateAsync(addressableKey, Vector3.zero, 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
     
  11. Shii

    Shii

    Joined:
    Nov 23, 2014
    Posts:
    11
    Thank you! Very useful information. Instructions are clear. =) Sorry for late response, did not get notification about new thread message.