Search Unity

Why are there no synchronously LoadAsset APIs?

Discussion in 'Addressables' started by zhuxianzhi, Jul 11, 2018.

  1. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    Another question, what if the resource does not call Addressables.ReleaseAsset?
     
  2. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    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.
     
  3. PaulBurslem

    PaulBurslem

    Unity Technologies

    Joined:
    Oct 7, 2016
    Posts:
    79
    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.
     
    RecursiveFrog and karl_jones like this.
  4. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    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)
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    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.
     
  6. Kastenfrosch2

    Kastenfrosch2

    Joined:
    Mar 19, 2017
    Posts:
    10
    @Baste
    Code (CSharp):
    1. ResourceManagement.IAsyncOperation<GameObject> loading = Addressables.LoadAsset<GameObject>("Foo");
    2. while (!loading.IsDone) {}
    3.  
    Unfortunately the loop never finishs when being called from the Main Thread.
    I guess resource loading is ticked from within the MainThread itself :(.
     
    vexe likes this.
  7. MNNoxMortem

    MNNoxMortem

    Joined:
    Sep 11, 2016
    Posts:
    723
  8. Kastenfrosch2

    Kastenfrosch2

    Joined:
    Mar 19, 2017
    Posts:
    10
    @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.
     
  9. jayatubi

    jayatubi

    Joined:
    Dec 9, 2013
    Posts:
    143
    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.
     
  10. zyc_dc

    zyc_dc

    Joined:
    May 11, 2018
    Posts:
    42
    Yeah. I also have the problem when using Lua. We really need sync load.
     
  11. gsantosgeneragames

    gsantosgeneragames

    Joined:
    Nov 21, 2018
    Posts:
    7
  12. NestorAlgieri

    NestorAlgieri

    Joined:
    Sep 11, 2011
    Posts:
    299
    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 :(
     
  13. Kirsche

    Kirsche

    Joined:
    Apr 14, 2015
    Posts:
    121
    Synchronous API is also needed if you want to create GUI programmatically like this:

    Code (CSharp):
    1.  
    2. private void CreateGUI()
    3. {
    4.     Window window = new Window("Options");
    5.     window.SetSize(400, 300);
    6.     window.SetAlignment(HAlign.Left, VAlign.Top);
    7.     window.SetPosition(50, 250);
    8.  
    9.     Panel panel = new Panel(window);
    10.     panel.SetBackgroundColor(255, 0, 0, 155);
    11.     panel.SetStretch(true, true);
    12.  
    13.     TabPanel tabpanel = new TabPanel(panel);
    14.     tabpanel.SetStretch(true, true);
    15.  
    16.     var tab = tabpanel.AddTab("Graphics");
    17.     var list = tab.AddComponent<VerticalLayoutGroup>();
    18.     list.childForceExpandHeight = false;
    19.     list.spacing = 5;
    20.  
    21.     // ...
    22. }
     
  14. unity_bill

    unity_bill

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    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.
     
    won-gyu, 5argon and MNNoxMortem like this.
  15. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    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.
     
  16. MNNoxMortem

    MNNoxMortem

    Joined:
    Sep 11, 2016
    Posts:
    723
    (@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.
    Code (CSharp):
    1. Addressables are not available in edit mode
     
  17. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    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.

    Code (CSharp):
    1. public static class AddressablesExtension
    2. {
    3.     public static bool IsNullOrEmpty(this AssetReference aref)
    4.     {
    5.         return aref == null || aref.RuntimeKey == Hash128.Parse("");
    6.     }
    7.  
    8.     /// <summary>
    9.     /// Use the Addressables system if in real play, use `editorAsset` if in edit mode.
    10.     /// </summary>
    11.     public static async UniTask<T> LoadAssetX<T>(this AssetReference aref)
    12.     where T : UnityEngine.Object
    13.     {
    14. #if UNITY_EDITOR
    15.         if (!Application.isPlaying)
    16.         {
    17.             return (T)aref.editorAsset;
    18.         }
    19. #endif
    20.         var op = aref.LoadAsset<T>();
    21.         var result = await op;
    22.         //Debug.Log($"{op.Status} {object.ReferenceEquals(null, op.Result)} {op.IsDone} {op.IsValid} {op.OperationException}");
    23.         return result;
    24.     }
    25.  
    26.     /// <summary>
    27.     /// Use the Addressables system if in real play, use `AssetDatabase` if in edit mode.
    28.     /// </summary>
    29.     /// <param name="key">Addressable key</param>
    30.     /// <param name="pathForEditor">This starts with "Assets/..." and you need the file extension as well.</param>
    31.     public static async UniTask<T> LoadAssetX<T>(string key, string pathForEditor)
    32.     where T : UnityEngine.Object
    33.     {
    34. #if UNITY_EDITOR
    35.         if (!Application.isPlaying)
    36.         {
    37.             return AssetDatabase.LoadAssetAtPath<T>(pathForEditor);
    38.         }
    39. #endif
    40.         return await Addressables.LoadAsset<T>(key);
    41.     }
    42. }
    The better way is probably we have to ask the addressable config scriptable object file for the current mapping.
     
    MNNoxMortem likes this.
  18. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,746
    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.
     
  19. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    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.
     
    Last edited: Mar 3, 2019
  20. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,746
    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)
     
  21. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Unity team just says it has to be asynchronous and ignores real-world cases there people need synchronous calls. And there are a lot of these cases.
    So many developers use Resources load and this system can not replace it without significant code and design changes!
    Just give as separate instant load functions.
    If we now that resources are on device why do we need to change everything to be asynchronous!
     
  22. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    How AAS was designed is that we don't know if the resource are on device or not. Everything are represented by a key/label (or GUID) so you don't know where or even what it is. For example, my label may ended up asking Facebook for some credential then use that with Firebase to get my things. That thing returned from my LoadAssetAsync may not be a literal asset file but some data generated from the internet.

    The Resources API on the other hand make an assumption that we refer to each things as path, and this path is a file system path (slash formatted, etc.) and by that extension, everything is strictly "a file", one path specify which file you want. Clear unit, has tangible form and is right there in the folder. AAS is not really Resources replacement/direct upgrade, but I like to view it as it could achieve something similar to Resources, and others.

    Also how about you look at https://github.com/Unity-Technologies/Addressables-Sample/tree/master/Advanced/Sync Addressables/Assets/SyncAddressables how to adapt a custom provider for synchronous API? The underlying of async API ended up using a chain of old API like those with the asset bundle or scenes, which still contains a sync. counterpart or being adapted to complete immediately on request in these custom providers.
     
    unity_bill likes this.
  23. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Thanks for describing the difference.

    I will check that out. But if sync load is not natively supported by unity team I and many other not very experienced programmers most likely will not risk using this in production( So I hope this will be officially added.
     
  24. VictorQianUnity

    VictorQianUnity

    Joined:
    Nov 13, 2015
    Posts:
    30
    Unity should really check what the devs want before making such "Design decision", not everyone is making a mobile game that load resource from remote.

    I don't know if the "design decision" is pure made by those big mobile client or not, but just use some basic logic then you will know a sync function is a necessity, unless Unity is planing never to get rid of the Resources.Load api. Stop letting your marketing team designing your engine, ask some real devs.
     
    Last edited: Aug 17, 2019
    MegamaDev likes this.
  25. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Are you implying that Unity is planning to get rid of Resources.Load API? If so, could you link me to that?
     
  26. VictorQianUnity

    VictorQianUnity

    Joined:
    Nov 13, 2015
    Posts:
    30
    No, I'm just saying if they can have a addressable system with synced load, they can get rid of resource.load in the future so we don't need to deal with 3 different system in one engine.

    And i will not be surprised if they do get rid of the Resource.Load, just look at their official article about resource management. https://learn.unity.com/tutorial/assets-resources-and-assetbundles#5c7f8528edbc2a002053b5a6

    Quote:"
    2.1. Best Practices for the Resources System
    Don't use it."
     
    Last edited: Aug 17, 2019
  27. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    I would vote for that)
     
    PhilSA likes this.
  28. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    We should have official sync AAS that's for sure. So it is a supercharged Resources.Load with label not forced to physical slash separated path.

    But as a side note about official words, I don't trust all of them. At least I remembered that "don't use it" segment was copied from the old docs from since 4.6 era when I started using Unity and likely outdated / not maintained.

    "Use of the Resources folder makes fine-grained memory management more difficult" I think it is actually the inverse, since with Resources folder you could fine-grain manage memory as opposed to Unity loading everything it touches on starting the scene, that made many game unscalable and have that characteristic hiccup on every scene change. (save for audio clip with disabled auto load, and missing SpriteAtlas with static callback set up) There is no where else to hide from Unity finding them, other than moving things to Resources folder to dynamically load them. The game is now scalable performance wise, but now not maintainable because of path problem. And it is now not clear where to call Resources.UnloadAsset to.

    And then this is precisely the point of AAS. Combining manual load with saner custom label + loading by remembering GUID instead (AssetReference). It is because being able to load manually and release manually is more fine grained, that it is good and AAS was made based on that. The statement about Resources make memory management difficult because of its manual property then doesn't make sense because we are now going this way.

    One other example is from the Legacy Animation system. There are full of conflicting information. And I am not sure which one they intended.

    https://docs.unity3d.com/Manual/Animations.html

    Screenshot 2019-08-18 00.38.59.png

    Here it is not recommended anymore.

    https://docs.unity3d.com/Manual/MecanimPeformanceandOptimization.html

    Screenshot 2019-08-18 00.41.09.png

    Here it says it could be an optimization.



    Here it is a valid best practice over Animator in many cases.
     
    Last edited: Aug 17, 2019
  29. Endahs

    Endahs

    Joined:
    Sep 16, 2014
    Posts:
    94
    +1 for sync methods! I do like the async methods but there are many use cases for a simple sync method to use.
     
  30. wtksana_unity

    wtksana_unity

    Joined:
    Nov 1, 2018
    Posts:
    4
    yes we need sync api, please
     
  31. sinjimonkey

    sinjimonkey

    Joined:
    Jul 31, 2013
    Posts:
    72
    Damn. I really need a sync load. I'm struggling to figure out how I can create a monobehaviour for unit testing to run coroutines off of. Was really hoping I could load a prefab an instantiate it and then use that to call coroutines from.
     
    TextusGames likes this.
  32. atr0phy

    atr0phy

    Joined:
    Nov 5, 2014
    Posts:
    43
    The tone of the AAS description in the manual suggested to me that Unity is gently nudging developers in this direction over using resource folders and Asset Bundles. Perhaps I assume incorrectly, but I for one would be perfectly content if they deprecated those in favor of a singular asset management system that doesn't suffer from the flaws of those systems.

    To be blunt, this seems a bizarre design choice. Don't get me wrong, I'm a massive fan of asynchronous operations, but it seems illogical to abandon synchronous operations altogether here. I've always loved Unity for its flexibility. So forcing design patterns on developers (or forcing them to use older, more flawed forms of asset management) is not something I can support.
     
  33. WaaghMan

    WaaghMan

    Joined:
    Jan 27, 2014
    Posts:
    245
    +1 to synchronous loading. While async is nice when loading big assets (even if they're not being downloaded), a synchronous API would also be very welcome when loading small prefabs that we know will load real quick, and simplify working with them.
     
  34. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    I have found no use case to use synchronous addressables so far.
    You can simply use a coroutine or async task and wait for assets to be loaded before continueing with other code.
    Even for unit tests, if you have the asset guid you can just use new AssetReference(guid) and try load that asset.


    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3. using UnityEngine.AddressableAssets;
    4. using UnityEngine.ResourceManagement.AsyncOperations;
    5. using UnityEngine.TestTools;
    6.  
    7. namespace Tests
    8. {
    9.     public class LoadAddressableTest
    10.     {
    11.         // A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use
    12.         // `yield return null;` to skip a frame.
    13.         [UnityTest]
    14.         public IEnumerator LoadAddressableTestWithEnumeratorPasses()
    15.         {
    16.             var assetGuid = "36af56e333754a34f88765fc866e3300";
    17.             var key = new AssetReference(assetGuid);
    18.  
    19.             var loadAssetAsync = key.LoadAssetAsync<GameObject>();
    20.             loadAssetAsync.Completed += OnCompleted;
    21.             yield return loadAssetAsync;
    22.         }
    23.  
    24.         private void OnCompleted(AsyncOperationHandle<GameObject> obj)
    25.         {
    26.             Debug.Log(obj.Result);
    27.             Addressables.ReleaseInstance(obj.Result);
    28.         }
    29.     }
    30. }
    Even for things like GUI's I create a method CreateAsync() and make sure it is awaited.
    If the GUI prefab is not yet loaded from addressables I wait it off, after it's loaded -> instantiate it -> use it

    I don't see any code not being able to run async. Synchronous calls just block the main thread why would you want that at all?
    Sure it can make transition from synchronous code to asynchronous calls easier if there was a synchronous call but thats about it.
     
    MarcSpraragen and phobos2077 like this.
  35. WaaghMan

    WaaghMan

    Joined:
    Jan 27, 2014
    Posts:
    245
    Actually it's the other way around for me. I haven't found many use cases where you want to instantiate something and you don't care how much it will take to spawn (specially if we're talking about that ideal "remote bundle" thing they're justifying).

    Enemy bullets, small gameobjects for effects, level information, UI, general resources... we need most of these things to be available in a timely manner, and making it async is just (potentially) generating additional delay to user's inputs, and complicating code/requiring helper functions to simplify it.

    Of course, the solution to these problems is to preload things in advance, but this was already possible before, without the need to force all methods to be asynchronous.

    Asynchronous is powerful. Synchronous is simple.
     
    atr0phy likes this.
  36. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    I'd prefer to preload assets. I don't find it pleasing when a game freezes to load its assets. Mostly those kind of things are hidden behind a loading screen.
    It doesn't matter whether you load it synchronously or asynchronously at that point. You want assets which means you have to wait for them to be loaded in. With synchronous loading you're allowing your game to freeze up, preventing any input at all. With asynchronous loading you can load the assets in the background, if you really require some essential assets you can check if those are cached. if they're not, you can use a loading screen.
    As for actually using the assets, you can simply load them in an async awake method and instantiate them.

    When a game boots up -> check for essential remote assets -> not cached? -> download them async. (in case of iOS/Android mobile, prompt the user to download additional assets). Let the player choose / setup whatever is possible without remote assets. In the meanwhile those essential assets are already cached and usable. If not then just put a loading screen when the game is about to start.

    Preventing a game from freezing is something I consider a high priority. So I am extremely happy with asynchronous loading. I don't see the point of wanting to freeze your game just to load some asset. If you struggle with loading order then that's more a design flaw.
    By using asynchronous designs it doesn't matter anymore whether you get it them from local or remote. Code can easily be used for either location without any additional code changes or workarounds. Also if you're loading them from streaming assets then don't worry too much about loading time as it is pretty much instantly. Same goes for cached content. Unless they're huge prefabs either way it has to be loaded into memory so you're going to wait for that load time either by freezing or let the game update continue. imho I rather see a game play fluidly and maybe with some loading screens before starting than constant stuttering / freezes from asset loading. It all depends on when you require assets.

    Like I said I don't see a use case to use synchronous loading.
    Anything you named can be preloaded / cached. If you're then still loading the AA then it is pretty much almost loaded instantly.

    If things were already loading synchronously then loading asynchronously isn't any more difficult either because you can simply await until all assets are loaded. Though with asynchronous calls you can call the loading of assets in parallel and then await them instead of having to wait until every asset is loaded individually. Not sure if that would speed up the process but if it does then that's even more of a reason to use async.

    For a good UX you don't want a game freezing. You'll lose users that are impatient. When nothing happens and the game freezes they tend to close it and re-start it.
    When something is on screen moving, they tend to wait a certain amount of time.

    For clarity sake I'm not trying to be stubborn or anything, but I honestly don't see any use case where it is a necessity to use synchronous loading over asynchronous loading.
     
    Last edited: Oct 2, 2019
    phobos2077 and Overing like this.
  37. WaaghMan

    WaaghMan

    Joined:
    Jan 27, 2014
    Posts:
    245
    I think we're talking about different things. By synchronous instantiation/loading I mean for things that, at most, would take 50-100ms to load and spawn (so the first time you encounter a new enemy or item, you may notice a small hiccup). Asynchronous works better with coroutines or an async (possibly fire-and-forget) method, but those aren't always the best to integrate in the general structure of a game's logic.

    Of course synchronous loading anything big or that requires a download is a no-no, but let the programmer be the one to decide which method to use.

    Not to mention in our (PC and Consoles) games, the Awake() method is usually the one causing freezes on the loading screen, not loading/instancing the asset, so the freeze is usually unavoidable (can't be entirely sure, but I guess the async methods of Addressable will still call Awake() on the main thread).

    Also I'm not defending synch-only. When I said preload things in advance was possible, I meant doing it async... There are important benefits in async preload (you can do it even at the game's main menu, the loading screen can be animated, you can show progress if it's going to take a while...), but for instancing things that could be done instantly (for example, a prefab with a single gameobject and no geometry)? Not that much.
     
  38. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    And my point was anything that is so small where synchronous loading doesn't cause a stutter / freeze then loading it asynchronous shouldn't even be a problem either. It is pretty much the same but you can do different things in the meantime or await it if you have to execute code afterwards.

    The only difference between synchronous calls and asynchronous calls are that synchronous calls cause stutters / freezing the main thread and asynchronous doesn't.

    Need to convert an synchronous method to asynchronous. Just use async.
    If you need to do things in order make it an async Task and await that Task.
    It is just as simple as that. It gives the same results as if it were synchronous but it won't stutter or freeze.

    I'm kind of having a difficulty understanding what the real issue is synchronous vs asynchronous. Cause any synchronous process can be made asynchronous.

    Maybe if you'd have an actual code example where you'd wish to use synchronous loading. It might clear things up.
     
    phobos2077 likes this.
  39. atr0phy

    atr0phy

    Joined:
    Nov 5, 2014
    Posts:
    43
    And that's perfectly cool, but what's good for the goose isn't necessarily good for the gander, as they say. Off the top of my head, here are a few general reasons why I would avoid asynchronous design patterns where they aren't strictly necessary in this context:
    • Increased complexity. The K.I.S.S. principle applies here. There's no reason to add additional code complexity when it provides no benefit. Indeed, it can be counterproductive. It increases the potential for bugs and can lead to less predictable execution, which subsequently makes debugging more of a chore.
    • Increased overhead. I'm not sure what AAS is doing under the hood, but running a Task in C# inherently incurs more overhead than a single method call. That's just the nature of the beast. In many situations, it may not matter. But some of us devs are bound to run into situations where it does.
    • Increased refactoring difficulty. Is it reasonable to expect devs to either restructure existing code to adhere to this rigid design choice or be forced to use legacy asset management systems that are more flawed than AAS?
    So I think the real question here is this: is there any good reason not to include synchronous methods? Because I don't see how including them would break the original design-intent pointed out by Paul.
     
  40. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    The good reason to not include it that keeps being brought up is "we need to support you suddenly moving the content to the cloud".

    When I'm shipping things for console, I'm not going to do that. I still need Addressables, because I need Asset Bundles and managing asset bundles manually is garbage. I need asset bundles as otherwise the patch sizes will break the consoles patch limits.

    So the whole core design principle of Addressables (support loading from wherever, lol) is completely orthagonal to what I need from it (make asset bundles not be bad).

    That doesn't mean that I find this to be a very large obstacle, but I prefer to take on the complexity cost of async when I need it for performance reasons, rather than paying it all the time because the toolset was made with the assumption that I might be streaming S*** from the cloud.
     
  41. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    Increased complexity? It depends on your current code structure where you require to load something. If it is already done in an awake the only thing you have to do is add the async keyword and put await in front of the asset load task.
    I don't see how that can lead to unpredictable execution. As well as converting several synchronous processes when they're converted to asynchronous processes and then awaited in order. It is merely only adding async to the method and putting await before the method call. Simple as that.

    If you have systems that are depending on that loading order well thats more of a core structural issue than a problem between loading synchronous or asynchronous.

    Overhead of Task is negnectable. It's loading assets. I don't expect to load an asset every frame.
    I usually use Task<T> to return me the item I want. Because thats something coroutines cannot do. Whenever theres no need to return something i.e. in an awake method then IEnumerator can be used instead and yield return the operation.

    Increased refactoring difficulty... I personally find it hardly difficult to convert synchronous code into asynchronous code.

    I don't see enforcing asynchronous loading as a bad thing.
    I see it as preventing a bad user experience. I've seen many synchronous code problems when I started developing. Where Resources.Load was used and that it would freeze the game time and time again. When Resources.LoadAsync arrived people were still using Resources.Load and would still complain that the game freezes. Blaming Unity in the process. Which at that point was just ignorance of the developer to not check documentation whether there is an asynchronous way of loading (some don't even know what asynchronously means). And yes for those there is a slight learning curve to go from synchronous programming to asynchronous programming when you're just starting with it. But for any intermediate developer it shouldn't be a problem converting synchronous systems to asynchronous systems.
    If the current structure doesn't allow it, then thats rather a problem with the structure than with the API.
    So imho enforcing asynchronous loading I see it as an improvement. I compare it with a certain case with artists. If you're going to give artists parameters to change of which you know will drop FPS with certain values. You mostly want to restrict those values to keep them within boundaries.
    Making certain API's for a developer applies the same thing. By giving the possibility to load synchronously, some developers will use it and blame Unity for it for bad performance.
     
    ArthurAulicino and phobos2077 like this.
  42. RecursiveFrog

    RecursiveFrog

    Joined:
    Mar 7, 2011
    Posts:
    350
    Bullets appearing in a timely manner usually implies you’ve pooled them, because if you synchronously instaintiate as needed you’re going to trigger a garbage collector run at some point. Nothing in that description is advocacy for synchronous loading, so much as it’s an advocacy for preloading enough assets to recycle.
     
    phobos2077 and iamarugin like this.
  43. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    I have tried to make inventory system there each item definition is addressable scriptable object. Inventory slot contains int amount and AssetReference ItemDefinition fields. In asynchronous way it was a unmanagable nightmare to implement and maintain, and I ended by creating custom database with ability to synchronously get item object by guid.

    I just want to use persistence address of addressable object and then access it at any time I want, because I know this object exists and is very lightweight. I do not even need loading and unloading functionality I just need to get an object by persistent address( assetReference is serialized by persistent guid in oppose to object reference which is serialized by non persistent instanceId).

    The name "Addressable" asset implies I can access asset by it's address, but instead it gives false hope because I have no synchronous api.
     
  44. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    I find it annoying to use scriptable objects together with addressables in that kind of way.
    For an item definition I'd use pure C# classes. They can be serialized and deserialized using newtonsoft JSON because unity's JsonUtility doesn't support polymorphism (hopefully that'll ever change).
    When you have classes extending
    ItemDefinition
    you can use scriptable objects use those to serialize the definition you want. To load these definitions you can use a static class
    Items
    which contains an array of
    ItemDefinition
    base pure C# class. While loading the game you just load every item definition there is as addressable but extract the
    ItemDefinition
    data and add it to the array / list / dictionary whatever you want (I'd use Dictionary cause there is not going to be an item with duplicate ID's, then you can just fetch the item definition with ID).
    So you wrap
    ItemDefinition
    extended class into a scriptable object so you can serialize the data. Generate an item ID so they're not duplicate with any other item definition. Then use
    Items.GetItemByID(int id)
    to get the item definition you want at runtime.

    For loading in the player inventory you can just use newtonsoft json serialize / deserialize to load/save the data (as BSON maybe for protection of editting). It will contain all the information if you use it with settings that store the type of the object. It will polymorph them back as they were. So if you have an Inventory class with a
    List<Item>
    and the Item class contains a
    int Amount
    field and a
    ItemDefinition Definition
    field. Then Newtonsoft Json will deserialize all that data you had easily. And you'll have all definitions in place as you had before due to the magic of polymorphism that newtonsoft json is able to do.

    In your case thats more like a serialization nightmare than a synchronous / asynchronous nightmare
     
  45. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    You loose drag and drop ability and descriptive renamable name if items in favour of item ID.

    Addressable asset reference class fits perfectly for me. It is serialized by guides in JsonUtility and I can easealy retrieve itemDefenition by that guid from my custom database which I automatically collect in editor and ensure it is loaded at game start. I can rename or move itemScriptable object and my saves will not break. It also gives ability to easily select items from list in asset reference class.

    In inventory I use ItemInstance class which contains
    AssetReference ItemDefenition;
    int ItemAmount;
    ItemInstanceState ItemState.
    This class is easily serialized.

    This approach has many benefits and only trouble I experience is the lack of built-in synchronous get asset api.
     
  46. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    Trying to understand your system that you have. But I don't know anymore where you require to load something.
    You just said that you load your database at game start. If you have a asset reference to the scriptable object you can just use the asset GUID to lookup the ItemDefinition and return it. You don't need any loading for that. Or do I understand your current setup wrong?
     
  47. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    Yes. For now I load my custom database at start once and that is it. Then i am getting my itemDefenitions by looking by guid in that asset database. Yes now i do not need any additional loading due to my custom solution.

    But then I used default api i need to load(get) ItemDefenition directly from asset reference via build in asynchronous call. And it was nightmare then i tried to accessing ItemDefinition from AssetReference field in asynchronous maner from multiple places and multiple layers sequentially and in parallel! I spended so much time refactoring my code and ended up with idia that is not managable for me.

    Then i spent another shunk of time
    by further understanding addressables and creating custom solution with database to get itemDefenitions from assetReference synchronously by guid.

    I spended about 3 weeks to get to the point of working custom data base solution. And there may be many people like me, i do not want them to loose they time almost for nothing, it is not right.

    If only there were a synchronously load (or get) api for assetReference class i would just use that api and spended zero time and nerves. And that is how it should be. That is why I need synchronous api.
     
  48. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    1,092
    [Bit off-topic]
    Well I spent 2 days (± 16 hours) and this is what I got a prototype with a lot of features still lacking but still could continue to build on this.
    https://maskedmous.stackstorage.com/s/geNkeh48pVORB1Z

    Some things could've probably done easier but this is me programming without really thinking much ahead.
    If it were a real project I'd probably setup a diagram first with atleast 5+ days of designing (as it is a huge system).
    Would need to collect all use cases and take that into account.

    I really love making an inventory system (tried to do it for about 6 ~ 7 times now from scratch).
    Unity's UI Elements runtime package in 2019.3 would make a few things easier like bindings. Now I've made an awkward Event handler for it.
    ECS would make it a bit easier as well. Tried to separate a lot of data into components.
    Things that I haven't really thought yet of is saving to json and restoring from json. But that would probably be something I'd do with an interface that gives me the json and restoring from json.
    But yeah it's a prototype and probably not going to be the end result. (If I would even continue it, it's fun but I'd probably want to do this in ECS)

    I don't think someone has to spend 3 weeks for it to work. You just have to know your way around it, If the project heavily relies on synchronized workflows then yes I can imagine it requires some additional work. It is not impossible but takes longer. Though heavily synchronized workflows sounds more like a project fundamental issue to begin with.

    imho, it's easy to shout-out for a synchronized workflow but I'd say it just promotes bad structured projects (no offence but some people manage to create really terrible structures. The structure I made is probably terrible as well, but atleast it's not too bad for loading things). As well as promoting people for blaming the engine constantly freezing whilst loading, even though there is an async API. Bit same discussion with using full class names vs var. I use var everywhere I can, because it enforces better variable names and it looks more readable to me.
    That could be an endless discussion of whether to use or not to use var. The discussion whether to make synchronized API available or not feels a bit the same.

    [pros]
    - With asynchronous API it doesn't matter where it comes from, it'll just load it. No additional changes required to the loading API when it goes from streaming assets to remote or the other way around.
    - No more freezing whilst loading assets
    - Pretty much as fast as synchronous loading.
    - Lightweight references to assets. Helps larger projects where you don't want to load in everything at once for memory constraints.
    - Profiling loaded assets
    - No more asset bundle handling manually as it was one big chore to get it done correctly...
    - Task property they gave the asset references. I wish they did that with more API in the Unity Engine.
    - Probably more pros...

    [cons]

    - Loading scriptable objects that are used for events aren't that compatible due to it creating a new instance and breaking the events that are serialized in the scene as well. Scriptable Objects containing shared references to game objects or other data would break as well. It sounded like a fun structure to work with but after experimenting a lot I've stepped down from it and went to static classes for sharing singleton data.
    - Might require restructuring due to asynchronous API's vs Synchronous API's such as Resources.Load or serialized references.
    - Requires extra managing of loading & unloading in some cases.
     
  49. PNordlund

    PNordlund

    Joined:
    Nov 20, 2013
    Posts:
    48
    The lack of synchronous loading makes the simplest use cases impossible to implement. For example, what we do a lot is initialize a UI screen with some icons that are loaded from Resources based on a name of a sprite that we get from game config data. Now these sprites in the Resources folder don't get packed into sprite sheets so I'd like to move them out of it and just use addressables to load the sprites which I definitely know are in the build.

    Even if I'd pack them into a bundle, I'd download the bundle first (mobile games do that you know) and when the game is running, I know I have the asset available locally.

    Maybe there's a need for another, simpler addressables system that's developed from the perspective of the most common use case for this feature?
     
    chanon81 and TextusGames like this.
  50. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Watch the Unite Copenhagen talk :


    Screenshot 2019-10-24 15.54.31.png
     
    frank231love and Favo-Yang like this.