Hi.
I need to load all assets with label. Then I need to create dictionary of that asset and its guid in order to get asset i need from that dictionary by its guid (AssetReference.RuntimeKey is a guid).
I know how to load assets or its locations with lable but i do not know how to find the key in form of guid not in form of name.
AssetReference class has a runtime key in form of guid, which is further used to load corresponding asset.
How do i load and get matched pairs of assets and it’s guids knowing only lable?
Or how do i convert guid based address of AssetReference into name based adress that I see in addressable window?
I found out that resource locator contains both name based and guid based location key.
Currently addressables.LoadResourceLocations( myLable) returns only locations with name based address…
How do i get guid based locations?
I dont know answer for your exact question, but if you want to create cache of your assets to prevent loading delay - you dont need to actually store them.
If you just load them into memory and do not release them - following calls to the same asset seems to get already loaded copy of said asset.
In our project when we preload - we just get assets we want async and store handle (to release later). When we need to actually use the asset - we send another LoadAssetAsync that returns same asset we already loaded instantly (or with 1 frame unnoticeable delay, not sure). Just need to keep track and release all the handles when you don`t need the assets.
Sorry if this is not the answer you were looking for
Thanks for answer.
But it still returns assets in asynchronous way with 1 frame delay.
I need instant synchronous access after i load all this assets and its dependendensies asynchronously at start of the game.
I actually found the way and will post my solution after I test it.
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using Object = UnityEngine.Object;
public class SyncAddressableDatabase : MonoBehaviour
{
public static SyncAddressableDatabase Instance;
public static readonly string SynchronousLabel = "Synchronous";
private readonly Dictionary<string, Object> m_AssetKeyDatabase = new Dictionary<string, Object>();
private void Awake()
{
if(Instance != null)
Debug.LogError("There is multiple instances of single class " + nameof(SyncAddressableDatabase));
Instance = this;
DontDestroyOnLoad(this);
}
public async Task Initialize()
{
// Loading locations by label
var labelOperation = Addressables.LoadResourceLocationsAsync(SynchronousLabel);
var syncLocations = await labelOperation.Task;
// Loading assets and making dictionary of location and corresponding asset
var tempLocationIdDatabase = new Dictionary<string, Object>();
var operationHandles = new List<AsyncOperationHandle<Object>>();
foreach (var syncLocation in syncLocations)
{
var asyncOperationHandle = Addressables.LoadAssetAsync<Object>(syncLocation.PrimaryKey);
operationHandles.Add(asyncOperationHandle);
asyncOperationHandle.Completed += handle =>
tempLocationIdDatabase.Add(syncLocation.InternalId, handle.Result);
}
// Awaiting for all asset loadings to be completed
var curTime = Time.time;
foreach (var asyncOperationHandle in operationHandles)
await asyncOperationHandle.Task;
Debug.Log(Time.time - curTime);
// Iterate for each resource locators and it's keys
// In order to find matched locations and add key into database
m_AssetKeyDatabase.Clear();
foreach (var resourceLocator in Addressables.ResourceLocators)
{
foreach (var key in resourceLocator.Keys)
{
var hasLocation = resourceLocator.Locate(key, typeof(Object), out var keyLocations);
if (!hasLocation)
continue;
if(keyLocations.Count == 0)
continue;
if(keyLocations.Count > 1)
continue;
var keyLocationId = keyLocations[0].InternalId;
var locationMatched = tempLocationIdDatabase.TryGetValue(keyLocationId, out var asset);
if (!locationMatched)
continue;
m_AssetKeyDatabase.Add(key.ToString(), asset);
}
}
}
public static Object GetAssetSync(string key)
{
Instance.m_AssetKeyDatabase.TryGetValue(key, out var asset);
return asset;
}
}
public static class SynchronousAssetKeyDatabaseExtension
{
public static Object GetAssetSync(this IKeyEvaluator assetReference) =>
SyncAddressableDatabase.GetAssetSync(assetReference.RuntimeKey.ToString());
}
In short i load locations by lable. Than i load assets and make pairs of location and loaded asset into temporary dictionary. Than I iterate for all RessourceLocators and their Keys and try to locate and if location matches i add key and previously loaded asset into final dictionary. I now able to retrieve Loaded asset from that dictionary by key ( both name based and guid based) via simple extention method AssetReferenceField.GetAssetSync().
From my understanding Addressable system has some form of built-in cache and will not actually load asset second time if it is in memory. And this system appears to duplicate that behavior.
I may be wrong, though, because so far my understanding of Addressables is limited.
If there is the chacke may be there is a way to simpli iterate through it without creating custom database, i need to inverstigate more on that subject.
I turned your code into an AsyncOperation using the Addressables AsyncOperationBase class. It allows you to yield return the operation in a coroutine and generally fits the Addressables theme. Heres the code:
public class LoadAssetsByLabelOperation : AsyncOperationBase<List<AsyncOperationHandle<Object>>>
{
string _label;
Dictionary<object, AsyncOperationHandle> _dictionary;
public LoadAssetsByLabelOperation(Dictionary<object, AsyncOperationHandle> dictionary, string label)
{
_dictionary = dictionary;
if(_dictionary == null)
_dictionary = new Dictionary<object, AsyncOperationHandle>();
_label = label;
}
protected override void Execute()
{
//Task.Factory.StartNew(new Action(LoadAssetsByLabelAsync);
DoTask();
}
public async Task DoTask()
{
var locationsHandle = Addressables.LoadResourceLocationsAsync(_label);
var locations = await locationsHandle.Task;
var locationInternalIdDictionary = new Dictionary<string, AsyncOperationHandle<Object>>();
var operationHandles = new List<AsyncOperationHandle<Object>>();
foreach (var resourceLocation in locations)
{
AsyncOperationHandle<Object> assethandle = Addressables.LoadAssetAsync<Object>(resourceLocation.PrimaryKey);
operationHandles.Add(assethandle);
assethandle.Completed += assetOp =>
{
if (!locationInternalIdDictionary.ContainsKey(resourceLocation.InternalId))
locationInternalIdDictionary.Add(resourceLocation.InternalId, assetOp);
};
}
foreach (var handle in operationHandles)
await handle.Task;
foreach (var locator in Addressables.ResourceLocators)
{
foreach (var key in locator.Keys)
{
var hasLocation = locator.Locate(key, typeof(Object), out var keyLocations);
if (!hasLocation)
continue;
if (keyLocations.Count == 0)
continue;
if (keyLocations.Count > 1)
continue;
var keyLocationID = keyLocations[0].InternalId;
var locationMatched = locationInternalIdDictionary.TryGetValue(keyLocationID, out var loadedHandle);
if (!locationMatched)
continue;
if(!_dictionary.ContainsKey(key))
_dictionary.Add(key, loadedHandle);
}
}
Complete(operationHandles, true, string.Empty);
}
}
An example method that would call this is:
public static AsyncOperationHandle<List<AsyncOperationHandle<Object>>> LoadAssetsByLabelAsync(string label)
{
var handle = Addressables.ResourceManager.StartOperation(new LoadAssetsByLabelOperation(_loadedAssets, label), default);
return handle;
}
It also allows you the chain it along with other AsyncOperationHandles.