Search Unity

How to Load a list or ienumerable of AssetReference with standart asynchronous operation handler?

Discussion in 'Addressables' started by TextusGames, Aug 9, 2019.

  1. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Is there a built-in way to load list of AssetReferences or List Of AssetAddress with standart operation handler?

    I can write something like

    Code (CSharp):
    1.  
    2.     public IEnumerator LoadAll( List<AssetReference> assets , Action completed)
    3.     {
    4.        
    5.         Debug.Log("Started");
    6.         var tStart = Time.realtimeSinceStartup;
    7.        
    8.         var completionindex = 0;
    9.         foreach (var assetReference in assets)
    10.         {
    11.             assetReference.LoadAssetAsync<Object>().Completed +=
    12.                   handle => completionindex += 1;
    13.         }
    14.  
    15.         yield return new WaitUntil(()=> completionindex == assets.Count);
    16.        
    17.         //completed?.Invoke();
    18.         var tEnd = Time.realtimeSinceStartup;
    19.  
    20.         Debug.Log("Completed");
    21.         Debug.Log(tEnd - tStart);
    22.        
    23.     }
    But this do not have standart operation handler.
     
  2. danilonishimura

    danilonishimura

    Joined:
    Jul 13, 2010
    Posts:
    70
    AFAIK, AssetReference is a synthetic sugar. Behind the scenes it calls the same kind of Addressables.Instantiate(key), so in theory iterating through the ContentCatalog would be the way to do what you want.

    Code (CSharp):
    1. var asyncCatalogLoad = Addressables.LoadContentCatalogAsync(catalogPath);
    2. while (!asyncCatalogLoad.IsDone)
    3. {
    4.     yield return new WaitForEndOfFrame();
    5. }
    6. foreach (var k in asyncCatalogLoad.Result.Keys)
    7. {
    8.       // your code
    9. }
     
    Last edited: Aug 9, 2019
  3. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Most likely it will not assign Asset field of AssetReferences presented in a list ( AssetReferenceField.LoadAssetAsync<Object>() will assign its Asset variable, and I am looking for similar methods but for collection of asset references).
    I have a field which is a List<AssetReference> Assets some there in the script and the content of that list could be changed at runtime. You are referring to some "catalogPath" where do I find that?
     
  4. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    LoadAssetsAsync ?
     
  5. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    I have tried Addressable.LoadAssetAsynchroous(assetsAsKeyList, assetAction, mergeAssets) but that did not assign Asset variable of Each AssetReferencesPresented in List (because it is a list of keys).


    For example, I have a list of possible assetsReferences as possible itemDefenitions.
    Then I randomly generate let say 10 of them a copy them into chest inventory (also list of assetReferences). Now I need to load them all at once and make that so Asset variable of assetReferences in Chest is assigned by whose loaded objects.
    Then I need to draw the content of that chest by accessing chests inventory and getting data out of Asset variable of AssetReference ((ItemInChest.Asset as ItemClass).Image/Name so on).

    Looks like some function like AddressableAssets.LoadAssetsAsync should be added. I think it is common to load a collection of assets. (not bundle).
     
  6. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    I meant, this method is already there. It returns a single async op that gives a result of a list of object, so after yield returning the async op then you can access its .Result that contains all objects. I believe you have to use the merge mode Union.

    Screenshot 2019-08-10 02.36.26.png
     
  7. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    I have additionaly tested that function by below code.

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEngine;
    5. using UnityEngine.AddressableAssets;
    6. using Object = UnityEngine.Object;
    7.  
    8. public class TestSerialise : MonoBehaviour
    9. {
    10.    
    11.     public AssetReference Single;
    12.     public List<AssetReference> Multiple;
    13.  
    14.     public void Start()
    15.     {
    16.         // Loading AssetReference Directly will asign "Asset" field of Single reference
    17.         Single.LoadAssetAsync<Object>().Completed += handle => Debug.Log(Single.Asset);
    18.        
    19.         // Loading AssetReferences will not assign "Asset" fields
    20.         var keys = Multiple.Select(e => e.RuntimeKey).ToList();
    21.         var operation = Addressables.LoadAssetsAsync(
    22.             keys,
    23.             (Action<Object>) Debug.Log,
    24.             Addressables.MergeMode.Union);
    25.        
    26.         operation.Completed += handle =>
    27.         {
    28.             Debug.Log("COMPLETED");
    29.             foreach (var o in Multiple)
    30.                 Debug.Log(o.Asset);
    31.         };
    32.     }
    33.  
    34.  
    35. }
    36.  
    Example.
    I have a list List<AssetReference> ITEMS which may contain same assigned values.
    I need to asynchronously load them at ones preferably with AsyncOperationHandle.
    Also, it needs either to assign Asset field of each ITEM (ITEMS[x].Asset = LoadedObject)
    or perform as many actions as ITEMS.Count.
    Because I need to iterate through ITEMS and access each corresponded loaded object.

    With Addressables.LoadAssetsAsync I can not match each ITEM and the loadedObject
    because if Items contain duplicated assets then this method will return only unique loaded objects.

    The solution I provided above loads each ITEM and Assigns ITEM.Asset object field automatically but I do not have benefits of AsyncOperationHandle return type. Then I can iterate through the original list and use corresponded loaded object. (even if that would not happen automatically I could just add objects in the temp object list and return it on completion and it would be the same size so I could match original and loaded lists)

    Something like Addressables.LoadAssetsAsync would be usable in that case but only if it would return a list of objects of the same size or perform as much actions.

    Is there a way?


     
  8. hanniiel

    hanniiel

    Joined:
    Jun 14, 2013
    Posts:
    39
    If you want to iterate per single object you might use Task so result is awaited and you can have control over it.
    if you want to use a list of certain assets you might first set the base like relationships and set labels (categories).

    In my code I have labels(categories) that represent a list of objects and per each object there is a relationship with certain asset.

    the name of the sprite matches the name of the prefab so if I need to load the object I can do it with the name of the sprite following a specific path.

    i.e.
    Group Models
    Models/{category}/prefab(s) . each folder with label model and other label with the specific category.
    Group modelicons
    Modelicon/{category}/sprite(s) each folder with label modelicons and other label with the specific category.

    I hope this can help you as a reference.

    Code (CSharp):
    1.   List<string> GetActivityCategories3D()
    2.     {
    3.    
    4.         List<string> categories = new List<string>();
    5.  
    6.         if (categories.Count == 0)
    7.             categories.Add("Recycle");
    8.  
    9.         return categories;
    10.     }
    11.  
    12.     void SetCatsAddressables3d()
    13.     {
    14.         var activity_cats = GetActivityCategories3D();//get list of allowed cats
    15.  
    16.         for (int i = 0; i < activity_cats.Count; i++)
    17.         {
    18.    
    19.             var cats = new List<object> { "selectable", "category", $"{activity_cats[i]}" };
    20.             int valor = i;
    21.             Addressables.LoadAssetsAsync<Texture2D>(cats, null, Addressables.MergeMode.Intersection).Completed += (obj) =>
    22.             {
    23.                 HandlerCatsLoaded(obj, valor==(activity_cats.Count-1));
    24.             };
    25.         }
    26.        
    27.        
    28.     }
    29.     Cat3dInventory inventory;
    30.  
    31.     async Task HandlerCatsLoaded(AsyncOperationHandle<IList<Texture2D>> obj, bool isLast)
    32.     {
    33.         try
    34.         {
    35.             var catsAllowed = GetActivityCategories3D();//get list of allowed
    36.             for (var i = 0; i < obj.Result?.Count; i++)
    37.             {
    38.                 var sp = Imagine.GetSprite(obj.Result[i]);
    39.              
    40.                 //filter cats from activity or project
    41.                 if (catsAllowed.Contains(sp.name))
    42.                 {
    43.                     Cat3dInventoryCategory category = new Cat3dInventoryCategory(sp.name, sp);
    44.  
    45.                     var result = await Addressables.LoadAssetsAsync<Texture2D>(
    46.                         new List<object> { "modelicon", sp.name.ToLower() },
    47.                         null,
    48.                         Addressables.MergeMode.Intersection).Task;
    49.  
    50.  
    51.                     for (int d = 0; d < result?.Count; d++)
    52.                     {
    53.                         var modelicon = Imagine.GetSprite(result[d]);
    54.                         Debug.Log("model icon " + modelicon.name);
    55.  
    56.                         var prefab = await Addressables.LoadAssetAsync<GameObject>($"Models/{sp.name}/{modelicon.name}.prefab").Task;//load models from modelicons from category
    57.                         //iterate over object like prefab.layer = 1
    58.  
    59.  
    60.  
    61.                         category.Items.Add(new Cat3dInventoryItem(modelicon.name, modelicon, prefab));
    62.                     }
    63.                     if(result?.Count>0)
    64.                         Addressables.Release(result);
    65.  
    66.  
    67.                     inventory.Categories.Add(category);
    68.  
    69.                     if (isLast)
    70.                     {
    71.                  
    72.                         BundleLoader._Instance.LoadBundleStreaming("ToolEditor3d", LoadSceneMode.Additive, OnCat3DInitialized);
    73.                     }
    74.                 }
    75.  
    76.             }
    77.             Addressables.Release(obj.Result);
    78.             Cat3DManager.SetInventory(inventory);
    79.         }
    80.         catch (System.Exception ex)
    81.         {
    82.             Debug.Log($"couldn't load icon models  {ex.Message}");
    83.         }
    84.     }