Search Unity

AsyncOperations don't complete in Editor context

Discussion in 'Addressables' started by johnseghersmsft, May 22, 2019.

  1. johnseghersmsft

    johnseghersmsft

    Joined:
    May 18, 2015
    Posts:
    28
    I've been working with Addressables for a while now. When I moved to using 2019.1 beta, I had to switch to the staging preview version 1.0.2. Seeing that is now quite old, and 2019.1 has been released for a while, I upgraded to 0.8.6.

    Of course a bunch of things changed, including IAsyncOperations->AsyncOperationHandle... and I think something has changed when in the Editor context (i.e. not Play mode).

    I have code that is marked [ExecuteAlways]. Because of the inability to use the Addressables code to load assets while not in Play mode, I created a simple method that would work in Editor, but also work at runtime:
    Code (CSharp):
    1.         public static IAsyncOperation<T> LoadAddressableAsset<T>(string keyName) where T : Object
    2.         {
    3. #if UNITY_EDITOR
    4.             var op = new CompletedOperation<T>();
    5.  
    6.             var guid = UnityEditor.AssetDatabase.AssetPathToGUID(keyName);
    7.             if (string.IsNullOrEmpty(guid))
    8.             {
    9.                 keyName += ".asset";
    10.                 guid = UnityEditor.AssetDatabase.AssetPathToGUID(keyName);
    11.             }
    12.             Debug.Log($"KeyName: {keyName} GUID: {guid}");
    13.  
    14.  
    15.             var asset = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(keyName);
    16.             Debug.Log($"Asset Loaded from {keyName}: {asset?.name ?? "(null)"}");
    17.  
    18.             return op.Start(null, keyName, asset);
    19. #else
    20.             return Addressables.LoadAsset<T>(keyName);
    21. #endif
    22.         }
    This code would be called like:
    Code (CSharp):
    1. var loadedAsset = await EditorHelpers.LoadAddressableAsset<SomeAssetType>("someAssetName").Task;
    2.  
    Of course, this needed to be changed. The equivalent code is as follows:
    Code (CSharp):
    1.         public static AsyncOperationHandle<T> LoadAddressableAsset<T>(string keyName) where T : Object
    2.         {
    3. #if UNITY_EDITOR
    4.             var guid = UnityEditor.AssetDatabase.AssetPathToGUID(keyName);
    5.             if (string.IsNullOrEmpty(guid))
    6.             {
    7.                 keyName += ".asset";
    8.                 guid = UnityEditor.AssetDatabase.AssetPathToGUID(keyName);
    9.             }
    10.             Debug.Log($"KeyName: {keyName} GUID: {guid}");
    11.  
    12.  
    13.             var asset = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(keyName);
    14.             Debug.Log($"Asset Loaded from {keyName}: {asset?.name ?? "(null)"}");
    15.  
    16.             return new ResourceManager().CreateCompletedOperation(asset, null);
    17. #else
    18.             return Addressables.LoadAssetAsync<T>(keyName);
    19. #endif
    20.         }
    Now, though, the await never completes--especially for code that is running in the context of an EditorWindow.

    Did something happen to the Editor's SynchronizationContext? Or did something else break with CompletedOperations? Or is there a better way to create a CompletedOperation since that is no longer something I can just construct?
     
  2. johnseghersmsft

    johnseghersmsft

    Joined:
    May 18, 2015
    Posts:
    28
    Digging more into this, I switched things around so that helper method returns a Task<T> instead of the AsyncOperationHandle<T>. The code is:
    Code (CSharp):
    1.         public static Task<T> LoadAddressableAsset<T>(string keyName) where T : Object
    2.         {
    3. #if UNITY_EDITOR
    4.             var guid = UnityEditor.AssetDatabase.AssetPathToGUID(keyName);
    5.             if (string.IsNullOrEmpty(guid))
    6.             {
    7.                 keyName += ".asset";
    8.                 guid = UnityEditor.AssetDatabase.AssetPathToGUID(keyName);
    9.             }
    10.             Debug.Log($"KeyName: {keyName} GUID: {guid}");
    11.  
    12.  
    13.             var asset = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(keyName);
    14.             Debug.Log($"Asset Loaded from {keyName}: {asset?.name ?? "(null)"}");
    15.  
    16.             return Task.FromResult(asset);
    17. #else
    18.             return Addressables.LoadAssetAsync<T>(keyName).Task;
    19. #endif
    20.         }
    21.  
    This works in the editor...I haven't yet built for standalone, but the #else clause should be correct.

    This definitely means that either my
     ResourceManager().CreateCompletedOperation(asset, null)
    was incorrect, or there's something wrong with the .Task property when running in editor code.

    To use this, you'd make the call as
    var asset = await EditorHelpers.LoadAddressableAsset<SomeType>("SomeAssetName");
     
  3. Whatever560

    Whatever560

    Joined:
    Jan 5, 2016
    Posts:
    519
    Would you happen to have a workaround for scenes ?
     
  4. phobos2077

    phobos2077

    Joined:
    Feb 10, 2018
    Posts:
    350
    Seriously this should work out of the box. I have a resource system built on my project that I've ported from Resources folder to Addressables. Because it was used for some Editor tools I had to implement a secondary backend to manually read asset locations from Asset Group scriptable objects. Not cool :)