Search Unity

API for getting the Address of a prefab or gameObject by instance?

Discussion in 'Addressables' started by RecursiveFrog, Jan 14, 2019.

  1. RecursiveFrog

    RecursiveFrog

    Joined:
    Mar 7, 2011
    Posts:
    350
    I have a scriptable object in which I associate Prefabs with their locations in the Resources folder. I do this by creating an editor script that lets me drag the prefab into an Object selector, at which point I obtain the prefab's location.

    Is there a way to do this for and Addressable's address? Can we call something like

    Code (CSharp):
    1. string address = Addressables.GetAddress(ofPrefab);
    And then store the address so we can obtain an instance of the prefab using the Addressables API?
     
    chanon81 likes this.
  2. MNNoxMortem

    MNNoxMortem

    Joined:
    Sep 11, 2016
    Posts:
    723
    Persistently? I don't think so (yet). You can however use `AssetReference` and pass it around.

    You could use `AssetReference` as a variable in a `ScriptableObject` and I'd assume Unity correctly serializes it.
     
  3. chanon81

    chanon81

    Joined:
    Oct 6, 2015
    Posts:
    168
  4. Ben-BearFish

    Ben-BearFish

    Joined:
    Sep 6, 2011
    Posts:
    1,204
    Does anyone know if there's an official API call for this? (Get a prefab's address/AssetReference) in the editor.
     
  5. Ben-BearFish

    Ben-BearFish

    Joined:
    Sep 6, 2011
    Posts:
    1,204
    I found the answer for my needs.
    Code (CSharp):
    1. // Create AssetRef:
    2. UnityEngine.Object asset;
    3. AssetReference assetRef = new AssetReference();
    4. assetRef.SetEditorAsset(asset);
    5.  
    6. // Then store assetRef somewhere.
    7.  
    8. // Get AssetReference Location
    9. string guid = assetRef.ToString();
    10. string path = AssetDatabase.GUIDToAssetPath(guid);
    11.  
    12. // Store the path somewhere. In ScriptableObject
    13. // or in class/struct that has asset path and assetRef together.
    14.  
    15. // OR:
    16. public static class AddressablesHelper
    17. {
    18. #if UNITY_EDITOR
    19.     public static string GetAddressablesAddress(this UnityEngine.Object asset)
    20.     {
    21.         AssetReference assetRef = new AssetReference();
    22.         assetRef.SetEditorAsset(asset);
    23.         string guid = assetRef.ToString();
    24.         string path = AssetDatabase.GUIDToAssetPath(guid);
    25.         return path;
    26.     }
    27. #endif
    28. }
     
    Last edited: Feb 20, 2020
  6. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    586
    @Ben-BearFish That doesn't look like it returns the address, it looks like it returns the path to the asset. Can you confirm?
     
  7. diesoftgames

    diesoftgames

    Joined:
    Nov 27, 2018
    Posts:
    122
    I'm also looking for something like this (ultimately because I want to be able to create a list of Addressables so I can convert them to indexes for a blittable way to refer to them in ECS), but the solution above seems to rely on the fact that the default address happens to be the asset path, which I'm thinking might not always be the case for me because I might want to simplify them. I'd love a way to grab the non-runtime key from a prefab or instance of the prefab so that I could: 1) drag an addressable prefab onto my GameObject with custom behaviour, 2) get that prefab's address, 3) look it up in my list of all addressables to get an index, 4) store that index for what can serve as a blittable reference to the addressable.

    EDIT: For now I can use a string value property instead of an AssetReference and just paste the address directly in there, it would just be nice to have the editor niceties of the AssetReference property.
     
  8. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    586
    This is my solution for grabbing multiple addresses from Objects/vice-versa.
    BuildAddressablesDictionary()
    needs to be called before getting any address. Of course this also assumes no duplicate addresses, so you will need to adjust that or remove if you have duplicate addresses.

    Code (CSharp):
    1. private static Dictionary<string, Object> addressesToPrefabs;
    2. private static Dictionary<Object, string> prefabsToAddresses;
    3.  
    4. public static void BuildAddressablesDictionary()
    5. {
    6.     addressesToPrefabs = new Dictionary<string, Object>();
    7.     prefabsToAddresses = new Dictionary<Object, string>();
    8.  
    9.     foreach (var group in UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings.groups)
    10.     {
    11.         foreach (var entry in group.entries)
    12.         {
    13.             var address = entry.address;
    14.             var prefab = entry.TargetAsset;
    15.             addressesToPrefabs.Add(address, prefab);
    16.             prefabsToAddresses.Add(prefab, address);
    17.         }
    18.     }
    19. }
    20.  
    21. public static string GetAddressFromPrefab(Object target)
    22. {
    23.     prefabsToAddresses.TryGetValue(target, out var address);
    24.     return address;
    25. }
    26.  
    27. public static Object GetPrefabFromAddress(string address)
    28. {
    29.     addressesToPrefabs.TryGetValue(address, out var target);
    30.     return target;
    31. }

    It also looks like they do have an API already to find an asset from a GUID, so you can use that if you only need to grab 1 address:

    Code (CSharp):
    1. public static string GetAddressFromPrefab(Object target)
    2. {
    3.     string path = AssetDatabase.GetAssetPath(target);
    4.     string guid = AssetDatabase.AssetPathToGUID(path);
    5.     var assetEntry = UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings.FindAssetEntry(guid);
    6.     return assetEntry.address;
    7. }
     
  9. diesoftgames

    diesoftgames

    Joined:
    Nov 27, 2018
    Posts:
    122
    I found a solution for my particular use case where I thought I need something similar, although it's maybe getting a little off-topic, but thought I'd recap it:

    I want to be able to essentially store AssetReferences in non-managed IComponentData. What I do is have a property field for my AssetReference (say in my authoring component), then what I actually serialize is a UnityEngine.Hash128, which I get by doing:
    Code (CSharp):
    1. var assetHash = Hash128.Parse(myAssetReference.AssetGUID);
    That can get stored in my component data and then when I need to instantiate it, that looks like:
    Code (CSharp):
    1. var assetRef = new AssetReference(assetHash.ToString());
    2. Addressables.InstantiateAsync(assetRef);
    EDIT: I ran into some trouble with Hash128 not being serializable, but as long as that's not a requirement this should work.
     
    Last edited: Apr 11, 2020
  10. wh0am15533

    wh0am15533

    Joined:
    Aug 14, 2019
    Posts:
    1
    You can also find an asset during runtime as so (You'll have to wait till the game loads the asset database first):

    Code (CSharp):
    1.  
    2. public static Tuple<string, string, string> FindAddresableAsset(string name)
    3. {
    4.     StringBuilder sb = new StringBuilder();
    5.     Tuple<string, string, string> res = new Tuple<string, string, string>("", "", "");
    6.     IEnumerable<UnityEngine.AddressableAssets.ResourceLocators.IResourceLocator> locators = UnityEngine.AddressableAssets.Addressables.ResourceLocators;
    7.     //sb.AppendLine("Locator CNT: " + locators.Count().ToString());
    8.     foreach (UnityEngine.AddressableAssets.ResourceLocators.IResourceLocator loc in locators)
    9.     {
    10.         //sb.AppendLine("Locator: " + loc.LocatorId);
    11.         foreach (object k in loc.Keys)
    12.         {
    13.             //sb.AppendLine("   Key: " + k.ToString());
    14.             if (k.ToString() == name)
    15.             {
    16.                 try
    17.                 {
    18.                     int middleIndex = loc.Keys.ToList().IndexOf(k);
    19.                     object bundle = loc.Keys.ElementAt(middleIndex - 1);
    20.                     object guid = loc.Keys.ElementAt(middleIndex + 1);
    21.                     res = new Tuple<string, string, string>(k.ToString(), bundle.ToString(), guid.ToString());
    22.                     //sb.AppendLine("Name: " + k.ToString() + " Bundle: " + bundle.ToString() + " GUID: " + guid.ToString());
    23.                     break;
    24.                 }
    25.                 catch (Exception e) { console.WriteLine("ERROR: " + e.Message); }
    26.             }                      
    27.         }
    28.     }
    29.     //File.WriteAllText(baseDirectory + "\\ASSETS.txt", sb.ToString());
    30.     return res;
    31. }
    32.  
    33.  
    Then use the reference to get Load your asset once you got the bundle and GUID. You may have several occurances so you may need to adjust the function for that.