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

Can I ask Addressables if a specified key (addressable name) is valid?

Discussion in 'Addressables' started by chanon81, Oct 25, 2018.

  1. chanon81

    chanon81

    Joined:
    Oct 6, 2015
    Posts:
    159
    Hello,

    I'm porting a game from another game engine to Unity.

    In the previous engine, I would check the file system and ask it whether certain textures exist at a specified file path, and if they don't, then fall back to another one.

    So what I would like is something like:
    Code (CSharp):
    1. Addressables.IsKeyValid("Powers/PowerLevel_2");
    and then if it isn't valid and Powers/PowerLevel_1 is, then I'd fallback to using Powers/PowerLevel_1.

    Is something like this possible?
     
  2. chanon81

    chanon81

    Joined:
    Oct 6, 2015
    Posts:
    159
    OK, from looking at the Addressables source code, I see that I can use
    Addressables.ResourceLocators
    for this.

    This is what I got:
    Code (CSharp):
    1.  
    2. public bool AddressableResourceExists(object key) {
    3.     foreach (var l in Addressables.ResourceLocators) {
    4.         IList<IResourceLocation> locs;
    5.         if (l.Locate(key, out locs))
    6.             return true;
    7.     }
    8.     return false;
    9. }
    10.  
    Then when using I need to make sure that Addressables has been initialized first. Eg.

    Code (CSharp):
    1.  
    2. Addressables.Initialize().Completed += (op) => {
    3.     string key;
    4.     key = "My/Texture.png";
    5.     Debug.Log("Resource exists for " + key + " = " + AddressableResourceExists(key));
    6. };
    7.  
     
    QantasVR likes this.
  3. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    1,023
    What you have above probably could work. But the thing I would actually recommend is to just do Addressables.LoadAsset<IResourceLocation>("key");

    This will not load your asset, but will load the location that points to your asset. If your key is valid, then you'll get a valid location back. Otherwise you will not. You can keep and use that location (doing LoadAsset<GameObject>(myNewLocation)) or you can just discard it and continue to load with key once needed.

    -Bill
     
    FlightOfOne, Mikael-H and chanon81 like this.
  4. Rafarel

    Rafarel

    Joined:
    Jul 21, 2017
    Posts:
    183
    @unity_bill how can I do that please ?


    I've done this but it isn't working :

    Code (CSharp):
    1.  
    2. var asyncKeyExists = Addressables.LoadAsset<IResourceLocation>(key);
    3.  
    4. if (asyncKeyExists.Status == AsyncOperationStatus.Failed)
    5. {
    6.     Debug.LogError("TurretsManager can't preload " + key + " this key does not exists!");
    7. }
    8.  
    9.  
    Thanks for your help!
     
  5. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    1,023
    In the soon-to-be-released 0.7.x, you can use a dedicated interface for this: `LoadResourceLocations`. I don't particularly like the name. It should probably be GetResourceLocations, but haven't had time to change that. so fair warning, it may get renamed going into 0.8.x
     
    marcianosd and Rafarel like this.
  6. d_grass

    d_grass

    Joined:
    Mar 6, 2018
    Posts:
    26
    @unity_bill
    Hello,
    I am trying right now to check if the location that points to the asset is available.
    Unfortunatly Addressables.LoadAsset<IResourceLocation>("key"); does not work anymore in 7.5.

    Error Log Shows:
    Exception encountered in operation Resource<IResourceLocation>(2019-001-020749.prefab)

    I also tried Addressables.LoadResourceLocations(key).Status == UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationStatus.Succeeded but it is always false.

    Could it be possible that it isnt checked synchronusly and if its the case, how can i archive a check that an key is available?

    I would be grateful to you if you have an solution.
     
  7. senfield

    senfield

    Joined:
    Apr 1, 2019
    Posts:
    31
    If you get a reference to a catalog (an IResourceLocator) then you can iterate through its .Keys and check for a match. I believe you can get to the active set of IResourceLocator(s) from Addressables.ResourceLocators.
     
  8. d_grass

    d_grass

    Joined:
    Mar 6, 2018
    Posts:
    26
    The Amount of ResourceLocators in Addressables.ResourceLocators is 0. But i can load Adressables how is this possible?
     
    Last edited: May 10, 2019
  9. senfield

    senfield

    Joined:
    Apr 1, 2019
    Posts:
    31
    It sounds like the Addressables system might not be done initializing when you are asking for the length.
    If I ask for the Count after initialization has finished I do get 1.
     
  10. senfield

    senfield

    Joined:
    Apr 1, 2019
    Posts:
    31
    The loading routines for addressables are all setup to wait for initialization to complete.
    E.g.
    public AsyncOperationHandle<TObject> LoadAsset<TObject>(object key)
    {
    if (!InitializationOperation.IsDone)
    return LoadAssetWithChain<TObject>(key);

    That's why your loads work... they are simply waiting patiently for the initialize callback.
    You can manually initialize addressables with something like Addressables.Initialize().Completed += yourCallback;
     
  11. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    1,023
    This is an async operation. So you need to do Addressables.LoadResourceLocations(key).Completed += whatever. Then check status. If that doesn't work, please let me know.
     
  12. d_grass

    d_grass

    Joined:
    Mar 6, 2018
    Posts:
    26
    @unity_bill & @senfield Thank you for your support, that helped me a lot and cleared things up. In particular initialize missed for me. Sorry that I bothered you with that.
     
    unity_bill likes this.
  13. marcianosd

    marcianosd

    Joined:
    Nov 6, 2018
    Posts:
    4
    Hello @unity_bill.

    When Addressables.LoadResourceLocationsAsync(address) is called and the address is not found, is it supposed to throw an exception? Or is it supposed to complete with AsyncOperationStatus.Failed?

    Currently, it's throwing an exception. I remember that it was the other case back when it was still Addressables.LoadAsset<IResourceLocation>(address). Just wondering if that is the correct behavior moving forward.

    Thanks!
     
  14. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    1,023
    We're planning to remove that exception to make LoadResourceLocationsAsync be a safe & easy way to validate an address or label.
     
  15. marcianosd

    marcianosd

    Joined:
    Nov 6, 2018
    Posts:
    4
    Hi @unity_bill ,

    Thank you for your response.

    I've just updated to 1.1.4, and according to the changelogs for 1.1.3, there's this line:
    • Made LoadResourceLocations no longer throw an exception if given unknown keys. This method is the best way to check if an address exists.
    But upon testing (as of 1.1.4), LoadResourceLocationsAsync still throws an exception when:
    • passing a single unknown key
    • passing a list where all keys are unknown (lists work if there is at least 1 known key)
    Are there prior steps that need to be done for this to work?

    Thanks!
     
    NickZhao, DanielGamaga and chanon81 like this.
  16. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    1,023
    Is it the InvalidKeyException? Looking through the code I'm not sure how this could happen, and this is specifically what we fixed. Perhaps you could debug or add some logging to the code to see where this get's thrown? There are only a few spots where we throw it.

    If it's a different exception, then knowing which one would help.
     
    marcianosd likes this.
  17. marcianosd

    marcianosd

    Joined:
    Nov 6, 2018
    Posts:
    4
    my bad. they were Error Logs and i got Error Pause on.
     
    Last edited: Jul 18, 2019
    unity_bill likes this.
  18. funbites

    funbites

    Joined:
    Oct 9, 2015
    Posts:
    17
    I'm using Unity 2019.2.3f1 and Addressables 1.2.2 and I'm getting InvalidKeyException.

    Code (CSharp):
    1.  
    2. Addressables.LoadAssetAsync<GameObject>(lastLoadedSceneName).Completed += OnCompleteLoading;
    3.  
    Exception encountered in operation UnityEngine.ResourceManagement.ResourceManager+CompletedOperation`1[UnityEngine.GameObject], result='', status='Failed': Exception of type 'UnityEngine.AddressableAssets.InvalidKeyException' was thrown., Key=ALICE_01_A_MAOS_ALGEMADAS
    UnityEngine.AddressableAssets.Addressables:LoadAssetAsync(Object)
    Funbites.GudeInk.Core.LoadSceneFunction`1:OnCall(LineTag[]) (at Assets/_GUDE_CORE/Scripts/Story/ContextFunctions/LoadSceneFunction.cs:38)
     
  19. peeka

    peeka

    Joined:
    Dec 3, 2014
    Posts:
    93
    I don't want exception to Log into player.log when pack doesn't exist, I am using LoadResourceLocationsAsync, but it logs the file name and everything to player.log.

    in my addressableassetsettings I have Log Runtime Exceptions unchecked, but it still log the exception..

    I am on aa 1.2.4
     
    joaobsneto and funbites like this.
  20. joaobsneto

    joaobsneto

    Joined:
    Dec 10, 2009
    Posts:
    152
    Same here. No updates so far?
     
  21. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    1,023
    to be clear, the original post was about LoadResourceLocationsAsync, which will not throw an exception if the key is unknown.

    LoadAssetAsync (which is what your code is calling) will and should throw an InvalidKey exception if it does not recognize the key. So whatever you are passing in as "lastLoadedSceneName" is not actually an addressable thing.
     
  22. joaobsneto

    joaobsneto

    Joined:
    Dec 10, 2009
    Posts:
    152
    I understand, but a system that is based on string values, a typo or a missing address may happen in production. So, should I always ask for Location and then ask for the asset itself?
     
  23. MSFX

    MSFX

    Joined:
    Sep 3, 2009
    Posts:
    115
    @unity_bill Also getting this exception when using LoadAssetAsync and wondering what the correct pattern is, I assumed I could just request a load and the error could be dealt and handled with awaitable.Status..? Using 1.3.8 with Unity 2019.1
     
    Last edited: Nov 27, 2019
    Jaimi likes this.
  24. Gwom

    Gwom

    Joined:
    Aug 5, 2015
    Posts:
    14
    For anyone else that stumbled here, looking for a way to suppress the errors when things are missing, I have a manager singleton that I use for all things addressables, that Initializes the system in Start() - then for Images / Game Objects, I use the following:

    Code (CSharp):
    1.     // Used for setting sprites from the addressable system
    2.     public static void LoadImage(string reference, Image img)
    3.     {
    4.         // Gives us a list of locations, this tells us if the reference is valid
    5.         Addressables.LoadResourceLocationsAsync(reference).Completed += (loc) =>
    6.         {
    7.             // If it is valid, there will be a result
    8.             if(loc.Result.Count > 0)
    9.                 Addressables.LoadAssetAsync<Sprite>(reference).Completed += (obj) => SetImage(obj, reference, img);
    10.             else
    11.                 Debug.LogWarningFormat("Reference {0} not found in Addressable System", reference);
    12.         };
    13.     }
    14.  
    15.     // Here we set an image if it did succeed
    16.     public static void SetImage(AsyncOperationHandle<Sprite> obj, string reference, Image img)
    17.     {
    18.         if(obj.Status == AsyncOperationStatus.Succeeded)
    19.         {
    20.             var sprite = obj.Result;
    21.  
    22.             // Could be the image has gone by this point
    23.             if(sprite != null && img != null)
    24.                 img.sprite = sprite;
    25.         }
    26.         else if(obj.Status == AsyncOperationStatus.Failed)
    27.             Debug.LogFormat("Cant get sprite from Addressable {0}", reference);
    28.     }
    29.  
    30.     // Used for loading a game object from the addressable system
    31.     public static void LoadGO(string reference, Action<GameObject> callback, Transform handle)
    32.     {
    33.         // Gives us a list of locations, this tells us if the reference is valid
    34.         Addressables.LoadResourceLocationsAsync(reference).Completed += (loc) =>
    35.         {
    36.             // If it is valid, there will be a result
    37.             if(loc.Result.Count > 0)
    38.             {
    39.                 if(callback != null)
    40.                     Addressables.InstantiateAsync(reference, handle).Completed += (obj) => SetGO(obj, reference, callback);
    41.                 else
    42.                     Addressables.InstantiateAsync(reference, handle);
    43.             }
    44.             else
    45.                 Debug.LogWarningFormat("Reference {0} not found in Addressable System", reference);
    46.         };
    47.     }
    48.  
    49.     public static void SetGO(AsyncOperationHandle<GameObject> obj, string reference, Action<GameObject> callback)
    50.     {
    51.         if(obj.Status == AsyncOperationStatus.Succeeded)
    52.         {
    53.             var gObj = obj.Result;
    54.             //var sprite = obj.Result.GetComponent<Sprite>();
    55.  
    56.             if(gObj != null)
    57.                 callback.Invoke(gObj);
    58.         }
    59.         else if(obj.Status == AsyncOperationStatus.Failed)
    60.         {
    61.             Debug.LogFormat("Cant get gameObject from Addressable {0}", reference);
    62.         }
    63.     }
    64.  
     
  25. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    459
    This is a snippet of the code I use in my AssetManager. When Addressables is initialized, it provides a resourcelocator that contains all the valid keys which I use to build a hashset for quick lookup. Then I expose an IsValidAddress function and let the normal load throw like normal if the caller fails to check their key.


    Code (CSharp):
    1. // Addressables uses this to set the remote asset path at runtime when AssetManager is initialized.
    2. public static string AssetPath { get; private set; }
    3.  
    4. private static HashSet<object> _keys;
    5.  
    6. /// <summary>
    7. /// Initialize the AssetManager to download assets from the specified <paramref name="assetPath"/>.
    8. /// </summary>
    9. public static Promise Initialize(string assetPath)
    10. {
    11.     if (string.IsNullOrWhiteSpace(assetPath))
    12.     {
    13.         throw new System.ArgumentException("assetPath cannot be null or whitespace.", nameof(assetPath));
    14.     }
    15.     if (AssetPath != null)
    16.     {
    17.         throw new InitializationException("AssetManager is already initialized.");
    18.     }
    19.  
    20.     AssetPath = assetPath;
    21.  
    22.     return Addressables.InitializeAsync().ToPromise() // ToPromise is an extension I wrote
    23.         .Then(resourceLocator =>
    24.         {
    25.             // Cache the keys so we can check if any key exists.
    26.             _keys = new HashSet<object>(resourceLocator.Keys);
    27.         });
    28. }
    29.  
    30. /// <summary>
    31. /// Is the address valid to load an asset?
    32. /// </summary>
    33. public static bool IsValidAddress(string address)
    34. {
    35.     ValidateInitialized();
    36.  
    37.     return _keys.Contains(address);
    38. }
    39.  
    40. private static void ValidateInitialized()
    41. {
    42.     if (AssetPath == null)
    43.     {
    44.         throw new InitializationException("AssetManager must be initialized before accessing any other functions.");
    45.     }
    46. }
     
    FlightOfOne, chanon81 and Thermos like this.
  26. cathalmcd

    cathalmcd

    Joined:
    Jul 15, 2019
    Posts:
    5
    Can you please tell us how to load all the label names from a group, so that we can create UI elements dynamically
     
  27. neoangelique

    neoangelique

    Joined:
    Nov 19, 2014
    Posts:
    9
    Is there a way to use async and await to fulfill the checking instead of using a callback? I've been trying this approach but addressables always returns me IsValid = true and Status = completed even if the asset and address does not exist.... this is incredibly frustrating as this is such a basic requirement yet isn't as straightforward.....

    Code (CSharp):
    1. await Addressables.LoadResourceLocationsAsync(address).Task;
    2. if (Addressables.LoadResourceLocationsAsync(address).Status == Completed)
     
  28. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    459
    Try this:

    Code (CSharp):
    1. var op = Addressables.LoadResourceLocationsAsync(address);
    2. await op.Task;
    3. if (op.Status == Completed)
     
    neoangelique likes this.
  29. cathalmcd

    cathalmcd

    Joined:
    Jul 15, 2019
    Posts:
    5
    Can anyone tell me how to load all the label names from a group, so that we can create UI elements dynamically
     
  30. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    459
    Groups don't exist at runtime. You can build a lookup dictionary for that purpose similar to what I did here (just use the group's name instead of asset entry's address).
     
unityunity