Another question, what if the resource does not call Addressables.ReleaseAsset?
My understanding is that the system needs to work equally well with remote and local assets. If they did a synchronous load on a remote asset, it could be a noticeable performance issue depending on how long it takes to load. I’m not sure why there is no synchronous load for assets that are placed in a local group, though.
Correct - this was a design decision to allow for a seamless transition when moving your assets from a local location (Resource folder, StreamingAssets, etc) to a remote location without having to change code. The returned IAsyncOperation is yieldable so you can use it in coroutines.
Not all projects need to load remote assets, there is no synchronous LoadAsset API, and you can’t quickly replace the original asset loading system (Resources API)
If you really need the asset to be finished loading before you continue the main thread, I guess you could just do a while(!loading.isDone);
on the IAsyncOperation. That can also be extracted into a helper method pretty easily.
ResourceManagement.IAsyncOperation<GameObject> loading = Addressables.LoadAsset<GameObject>("Foo");
while (!loading.IsDone) {}
Unfortunately the loop never finishs when being called from the Main Thread.
I guess resource loading is ticked from within the MainThread itself :(.
@Kastenfrosch2 check the loop from a coroutine and yield.
@MNNoxMortem The problem with this approach is, that it’s not really sync. So I cannot guarantee that after I called my Load() function, I can immediately acces the loaded object in the code line afterwards.
That takes away some simplicity of the old Resources.Load() approach.
Some third party library need synchronous IO interfaces for example the lua script integration. To implement the lua require
function the program should return content of the requried script immediately. The lua vm was written in c which is not yieldable thus the asynchronous IO can’t work for it. For now we have preload all the lua scripts into memory on the startup.
Yeah. I also have the problem when using Lua. We really need sync load.
+1 to the synch load
Yes we need sync load. It’s a real pain for an existing large project that relies heavily on sync load to convert to the new system
Synchronous API is also needed if you want to create GUI programmatically like this:
private void CreateGUI()
{
Window window = new Window("Options");
window.SetSize(400, 300);
window.SetAlignment(HAlign.Left, VAlign.Top);
window.SetPosition(50, 250);
Panel panel = new Panel(window);
panel.SetBackgroundColor(255, 0, 0, 155);
panel.SetStretch(true, true);
TabPanel tabpanel = new TabPanel(panel);
tabpanel.SetStretch(true, true);
var tab = tabpanel.AddTab("Graphics");
var list = tab.AddComponent<VerticalLayoutGroup>();
list.childForceExpandHeight = false;
list.spacing = 5;
// ...
}
Is that an editor window? or are you making a GUI window inside your game’s runtime?
Though it’s on the list, Addressables currently can’t be utilized at edit time.
Glad it is on the list! It is crucial when unit tests ran into them and I hate having to do if Application.isPlayMode then switch to AssetDatabase on all Addressables so that they are unit-test compatible.
( @unity_bill ,) @5argon also just ran into this problem. Did you find any suitable workaround to write unit tests when Addressables are involved? How do you map addressable keys to AssetDatabase path? I assume you just don’t and write all tests directly using AssetDatabase.
Addressables are not available in edit mode
I didn’t map, I just provide 2 separate paths manually… There should be a way to ask the catalog for the mapping to AssetDatabase compatible path but I remembered catalog doesn’t build immediately (and that’s why AAS is not edit mode compatible in the first place) and things may change for the upcoming v1.0.
public static class AddressablesExtension
{
public static bool IsNullOrEmpty(this AssetReference aref)
{
return aref == null || aref.RuntimeKey == Hash128.Parse("");
}
/// <summary>
/// Use the Addressables system if in real play, use `editorAsset` if in edit mode.
/// </summary>
public static async UniTask<T> LoadAssetX<T>(this AssetReference aref)
where T : UnityEngine.Object
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
return (T)aref.editorAsset;
}
#endif
var op = aref.LoadAsset<T>();
var result = await op;
//Debug.Log($"{op.Status} {object.ReferenceEquals(null, op.Result)} {op.IsDone} {op.IsValid} {op.OperationException}");
return result;
}
/// <summary>
/// Use the Addressables system if in real play, use `AssetDatabase` if in edit mode.
/// </summary>
/// <param name="key">Addressable key</param>
/// <param name="pathForEditor">This starts with "Assets/..." and you need the file extension as well.</param>
public static async UniTask<T> LoadAssetX<T>(string key, string pathForEditor)
where T : UnityEngine.Object
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
return AssetDatabase.LoadAssetAtPath<T>(pathForEditor);
}
#endif
return await Addressables.LoadAsset<T>(key);
}
}
The better way is probably we have to ask the addressable config scriptable object file for the current mapping.
So once again, because of Unity’s obsession with remote loading stuff, this is still not a proper replacement for the Resources folder. Exactly like the Asset Bundles before it.
Cool.
I look forward to a new solution in a few years, that will replace this one, that will also fail to replace the Resources folder for a 3rd time.
That’s exactly right, since the provider for the bundle is calling AssetBundle.LoadFromFileAsync inside. (UnityWebRequestAssetBundle.GetAssetBundle in the case of remote) And each group is essentially an asset bundle. It is not an entirely new architecture.
If you want synchronous function asap without waiting a few years, maybe it is faster to hack up your own provider based on AssetBundleProvider.cs that uses AssetBundle.LoadFromFile instead + assign null request operation + call complete immediately. (try to do it like the “invalid path” case in that file may work?) Make it so that the IAsyncOperation’s .Result is immediately available after the LoadAsset line and go by this assumption when you code it.
(This assumption would look a bit ugly as you don’t know at glance which IAsync is immediately done and holding result or not, being coming from the same Addressables.LoadAsset that take account of async load. Otherwise you would now have to hack up an another Addessables.LoadAssetSync which returns the result or null. I think I can feel why UT didn’t go this way.)
But I can imagine that it would not work if you have dependencies since IAO would ask and wait for those deps in chain and make your .Result still null even with synchronous load.
*Edit : I tried, but I forgot the first step of all the catalog .json file loading is also part of the dependency chain. (Providers are : JsonAssetProvider and ContentCatalogProvider) You will have to hack those into synchronous as well. However, the place where we could interchange those two are not exposed to be easily changable like AssetBundleProvider and BundledAssetProvider (which you can see the drop down on the group). You will have to go great length to make it work. (Json catalog loading is also taking in account in the case that catalog is online). At this point I think it is not worth it to go against the flow of API. Async hassle is for the best of ubiquitous use.
*Also the AAS docs should edit out the part where it says migration from Resource.Load is as easy as string replace. That gave me false hope even though now I have conformed to the async way.
Thanks for the reply, but I’m just going to keep using the Resources folder until Unity removes the functionality.
(or releases something to replace it, which they haven’t and aren’t planning to from what I can tell)