Search Unity

  1. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  2. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  3. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  4. Participate with students all over the world and build projects to teach people. Join now!
    Dismiss Notice
  5. Build games and experiences that can load instantly and without install. Explore the Project Tiny Preview today!
    Dismiss Notice
  6. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  7. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

My Addressables Feedback

Discussion in 'Addressables' started by Peter77, Jun 24, 2018.

  1. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    2,746
    Hi,

    I looked at the AddressableAssetSystem_Demo and Addressable System 0.1.2 and would like to provide some feedback, because I wrote a system that seems to be similar in a few ways, which we used several years and thus have a few learnings that I would like to share with you.

    First, I would like to describe our "AssetReference" approach, what was bad about it and what seems to be superior to the current Addressable implementation. I called it WeakAssetReference, which I also use in the following text to make a distinction between your AssetReference and the one from our system.

    AssetReference

    The implementation looks like:
    Code (CSharp):
    1. struct WeakAssetReference
    2. {
    3.    public string assetGUID;
    4.    #if UNITY_EDITOR || DEBUG
    5.    public string editorAssetPath;
    6.    #endif
    7.  
    8.    UnityEngine.Object cachedAsset;
    9.  
    10.    public T Load<T>() where T : UnityEngine.Object
    11.    {
    12.        if (cachedAsset != null) return cachedAsset as T;
    13.  
    14.        // load asset in blocking fashion. if preloaded already,
    15.        // it's just an table lookup for the loaded asset and fast.
    16.        cachedAsset = ContentManager.Load(this);
    17.        return cachedAsset;
    18.    }
    19. }
    The assetGUID was the actual guid the editor uses to reference assets (AssetDatabase.GUIDFromAssetPath). The editorAssetPath was available in the editor and debug builds, it represents the last known path of the asset in the project. This was useful, because if an asset could not be loaded (anymore), the assetGUID wasn't of great help, but looking at the editorAssetPath gave us an idea what happened to the asset.

    WeakAssetReference was implemented as a ValueType, to avoid additional null-reference checks.

    I implemented a custom Editor to mimic the look and feel of a standard EditorGUI.ObjectField for a WeakAssetReference. A WeakAssetReference feels the same from an usage point of view, whether you use a direct reference or a WeakAssetReference. The only difference it makes for the user is a tiny icon next to the "ObjectField picker" that indicates it's a WeakAssetReference.

    It was important to me to make it feel like a standard reference, to make it easier for everybody on the team to adopt. Everybody knew already how to drag&drop assets in an EditorGUI.ObjectField or how to use the Object Picker.

    Using a WeakAssetReference looks like this:
    Code (CSharp):
    1. class Player : MonoBehaviour
    2. {
    3.    [WeakAssetReferenceAttribute(typeof(GameObject))]
    4.    public WeakAssetReference explosion;
    5.  
    6.    void Awake()
    7.    {
    8.        // async (pre)load the asset
    9.        ContentManager.Preload(explosion);
    10.    }
    11.  
    12.    void Update()
    13.    {
    14.        if (Input.GetButton("Fire")
    15.        {
    16.            // prefab preloaded already, otherwise blocks until loaded
    17.            var prefab = explosion.Load<GameObject>();
    18.  
    19.            // do something with the prefab
    20.        }
    21.    }
    22. }
    The [WeakAssetReferenceAttribute] provided additional information for the custom WeakAssetReference editor. It allowed to specify what asset type the WeakAssetReference expects. This was also very helpful, to avoid that someone perhaps adds a reference to a Mesh, where the code actually expects it to be a GameObject.

    We used Awake() to trigger async-load calls of the specific object during scene loading. The loading-screen is kept alive until all requested assets have been preloaded.

    In order to access the actual asset, one would use WeakAssetRefeerence.Load<Type>() which was a blocking call. If the asset was preloaded already, it was just a value lookup, otherwise the system would load the asset in a blocking fashion.

    The blocking Load call is important to keep the game code simple. The outer-loading-screen makes sure all required assets are loaded already, we didn't want to add more complexity to the game code that is using these assets.


    Addressables

    I'm now going to explain how we achieved something similar as Addressables, I named it "ContentManager".

    The ContentManager is more limited than your Addressables system. We assume assets are always provided via asset bundles and they are located on disk. Downloading asset bundles would be an entirely different code path for us, that is not part of the ContentManager.

    All assets in the project are addressable by default.

    This was very important to us, because we want people without a strong technical background to be able to add and wire assets in the project. For example, if a GameDesigner created a new enemy configuration asset (ScriptableObject), s/he doesn't have to mark that configuration asset to be addressable first, it just works.

    I tested the AddressableAssetSystem_Demo what happens if I create a new prefab in "Assets/SpaceShooter/Prefabs". The Addressable System does not seem to provide the new prefab, it seems I have to add it in the "Addressables" window first, before the AssetReference GUI allows me to select that prefab. I imagine this workflow would not have worked in our team.

    How did we achieve addressables by default?

    We assume to load assets by their "Assets" directory location. If I have a prefab in "Assets/Prefabs/Player.prefab", this asset can be loaded using that path on all platforms, be it in the editor itself, iOS or Windows Standalone.

    With the WeakAssetReference you don't even have to keep that path around. You only need it if you want to by-pass the WeakAssetReference system and it does make sense in a few cases to by-pass it.

    Our addressables system (ContentManager) made this possible by using what I named an "Asset File Table". The AssetFileTable is a ScriptableObject that contains all paths and guids of the project.

    The AssetFileTable was generated by a custom build step. If I remember correctly, I used the generated assetbundle manifest files to build the AssetFileTable.

    When running the game in the editor, it would just contain AssetDatabase.GetAllAssetPaths and their corresponding guids.

    Here is a simplified example of the AssetFileTableEntry:
    Code (CSharp):
    1. struct AssetFileTableEntry
    2. {
    3.    public string assetGUID;
    4.    public string assetPath;
    5.    public string assetBundle;
    6. }
    If an asset is requested, it would call something like this on the ContentManager:
    (this is heavily simplified)
    Code (CSharp):
    1. class ContentManager
    2. {
    3.    AssetFileTable m_FileTable;
    4.  
    5.    T Load<T>(WeakAssetReference addr) where T : UnityEngine.Object
    6.    {
    7.        AssetFileTableEntry entry = m_FileTable.FindEntryByGUID(addr.assetGUID);
    8.        AssetBundle bundle = GetOrOpenAssetBundle(entry.assetBundle);
    9.        T asset = bundle.LoadAsset(entry.assetPath, typeof(T)) as T;
    10.        return asset;
    11.    }
    12.  
    13.    T Load<T>(string assetPath) where T : UnityEngine.Object
    14.    {
    15.        AssetFileTableEntry entry = m_FileTable.FindEntryByPath(assetPath);
    16.        AssetBundle bundle = GetOrOpenAssetBundle(entry.assetBundle);
    17.        T asset = bundle.LoadAsset(entry.assetPath, typeof(T)) as T;
    18.        return asset;
    19.    }
    20. }
    Sometimes it's useful to construct the location in code and request something like "give me all assets in 'Assets/Prefabs/Weapons'" for example. This was also possible via ContentManager:

    Code (CSharp):
    1. class ContentManager
    2. {
    3.    WeakAssetReference[] FindAssets(string directory, string assetType, bool includeSubDirectories)
    4.    {
    5.     // Find all assets that are part of 'directory' in the asset file table
    6.    }
    7.  
    8.    WeakAssetReference[] FindAssets(WeakAssetReference directory, string assetType, bool includeSubDirectories)
    9.    {
    10.        AssetFileTableEntry entry = m_FileTable.FindEntryByPath(assetPath);
    11.        // entry.assetPath would be the directory such as "Assets/Prefabs/Weapons" for example
    12.        return FindAssets(entry.assetPath, assetType, includeSubDirectories);
    13.    }
    14. }
    Game code uses FindAssets like this:
    Code (CSharp):
    1. void LoadAllWeapons()
    2. {
    3.    WeakAssetReference[] weaponAddresses = ContentManager.FindAssets("Assets/Prefabs/Weapons", "prefab", false);
    4.    foreach(var addr in weaponAddresses)
    5.    {
    6.        GameObject prefab = ContentManager.Load<GameObject>(addr);
    7.    }
    8. }
    We tried to avoid hard-coded paths, the preferred approach was to use a WeakAssetReference that represents a directory:
    Code (CSharp):
    1. class Something
    2. {
    3.    // drag&drop "Assets/Prefabs/Weapons" in this field
    4.    WeakAssetReference m_WeaponDirectory;
    5.  
    6.    void LoadAllWeapons()
    7.    {
    8.        WeakAssetReference[] weaponAddresses = ContentManager.FindAssets(m_WeaponDirectory, "prefab", false);
    9.        foreach(var addr in weaponAddresses)
    10.        {
    11.            GameObject prefab = ContentManager.Load<GameObject>(addr);
    12.        }
    13.    }
    14. }
    What was good about our system

    1) Using this system was easy to use and understand for everybody on the team. This was made possible, because it felt no different than working with normal object references. Drag&Drop assets in WeakAssetReference slots felt no other than using standard hard references.

    2) We don't have to maintain an extra addressables list. This was important, because we have tens of thousands of assets. There is no way to maintain such list by hand. Many team members would also not have the technical knowledge to maintain such list, nor do we actually want people spend time on that. We want them to do what they can best, be it art or sounds, but not adding assets to a specific list.

    3) On the programmer side, it was also useful to have as well. The code to load assets was unified, not matter whether the game runs in the editor or on ios. You write loading code once and the same code works everywhere.

    4) Because we use the asset guid's the editor uses itself, even if you move assets around or rename, our system still works.


    What was bad about our system

    1) WeakAssetReference being "type less" was sometimes causing issues. A programmer added a new field, such as:
    Code (CSharp):
    1. WeakAssetReference explosion;
    This was not very descriptive. Is it a sound, prefab, or whatever? We solved this via an attribute:
    Code (CSharp):
    1. [WeakAssetReferenceAttribute(typeof(GameObject))]
    2. WeakAssetReference explosion;
    But sometimes we also missed to use that attribute. Or the type changed during a refactor. So it would have been way better if we could write something like this:
    Code (CSharp):
    1. WeakAssetReference<GameObject> explosion;
    Unity's serialization system wasn't able to handle this, even though Unity can serialize List<string> for example. It would have been the correct solution for us and I believe it's from advantage for this system if you improve Unity's serialize system to support that.


    2) The AssetFileTable was a very memory inefficient beast. Imagine you have only 10000 assets in that table. That's a lot of paths and guids already, which eat up a memory and put further pressure on the GC. The first implementation had about 14 MB just string's in the AssetFileTable. If I would write such system again, I would avoid string for the assetGUID and use Hash128 for example.

    assetGUID string = 32 chars = 64 bytes memory
    Hash128 = 4 ints = 16 bytes memory

    My optimization for assetPaths was to split the paths into directory, filename and file extension:
    • Assets/Weapons/Prefabs/Lasergun.prefab
    would become:
    • Directory = Assets/Weapons/Prefabs/
    • Filename = Lasergun
    • Fileextension = prefab

    The directory and file-extension would be stored in an array and the AssetFileTableEntry does not use a string, but the integer index of that directory in the array. This made the AssetFileTable more memory efficient, but caused its code to get quite complex and indirect. It still was the same from an usage point of few.

    It was also possible to use "keywords" as file extension. I could use "texture" as file extension to get all textures, no matter whether they were png or tga.



    Unity Addressable and ResourceManager

    If you allow me to transfer this knowledge to the new Addressables system, here is what I think should be revised:

    1) Make all assets addressable by default or allow us to do this, like a "Assets/*" rule in the Addressable window.

    2) Use the Addressable window to add exceptions to the "addressable by default" rule.

    3) Don't use a custom AssetReference UI. Mimic the EditorGUI.ObjectField UI and allow to use the standard Object Picker window. The current implementation of the list might work with 20 assets, but if you have that flat list containing 10000 and more assets, that stops being useful, even with the search field. Also, Unity users love drag&drop, don't take it away from us.

    4) Replace string assetGUID in the AssetReference with Hash128 to save memory
    For debug purposes, in development mode builds and editor, keep a string of the last know path of the asset around in the AssetReference. This helped us so many times.

    5) Add functionality to query addressables for its content "give me all prefabs at this address". I guess it's probably supported already, but I didn't find it.

    6) Having only async Instantiate methods for AssetReference will make it difficult to use. You either have to add a lot of support code that loads the stuff at the beginning and then caches the result, or write more complicated game code that is able to handle async instantiation. Both cases not ideal. I recommend to add a blocking Load and Instantiate method. If the asset isn't loaded, it blocks until it's loaded. If the asset does not exist, return null.

    7) If the "catalog_4.json" file is something like our AssetFileTable, I imagine if this file is kept in memory, it's also going to be a huge memory waste in a large project, just like AssetFileTable was in ours.

    8) Get rid of the additional "adressable" indirection, but use the guid instead.

    9) Support AssetReference<GameObject>
     
    Last edited: Jun 29, 2018
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    3,467
    I agree with most of the points. Note that drag-and-drop is supported - in the demos, they dragged assets onto the drop-down, and that assigned the asset. It's not intuitive at all that that's how the field works, and since it's indistinguishable from other drop-downs, it's a bit confusing.

    For "making everything addressable by default"... I don't know. Having things turn addressable on-assign means that all the things you've ever wanted to be addressable are now addressable, and nothing else is. I guess it depends on workflow - if you generate game content based on the contents of folders rather than by explicitly assigning assets to prefabs or scriptableobjects or scenes or whatnot, it'll have to be automatic in some fashion.
     
    laurentlavigne and Peter77 like this.
  3. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    284
    I noticed that you can mark a folder asset as Addressable. don't know if/how you can load the contents
     
    Peter77 likes this.
  4. rigidbuddy

    rigidbuddy

    Joined:
    Feb 25, 2014
    Posts:
    33
    Generic drawer & serialization f AssetReferenceT<> is highly needed. Because it gives object filter and protects from runtime type errors.

    Agree that Editor UI could mimic ObjectField UI.

    Actually I've seen similar editor in ExposedReference<> (used in Playables) which was super OK for users and already handled by Unity serializer.
     
    Peter77 likes this.
  5. ScottPeal

    ScottPeal

    Joined:
    Jan 14, 2013
    Posts:
    10
    So the title of this forum is "Addressables Feedback" and I provided feedback that it is not working with 2018.2.0b10/11. Then the moderator deleted my post. How is this not feedback on addressables?

    "Your post in the thread Addressables Feedback was deleted. Reason: Please create a new thread rather than derailing an existing topic."
     
    Deeeds likes this.
  6. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    2,746
    The title of this forum thread (which I created) is "Addressables Feedback", it's not the title of the forum. It's a thread with my very specific feedback regarding the Addressables System targeted to Unity Technologies.

    If you want to join the discussion and add something to my feedback, this it the place to be. If your question/feedback is not related to my post, it's better to create a new forum thread. This helps to keep the forum clean and organized. One thread per topic/issue, rather than everything in one thread, is normally preferred.

    BTW, if you found a bug, it's also from advantage to submit a bug-report and post the bug-report Case number in your forum thread, rather than posting in the forum only. Unity Technologies often asks for a project/example to reproduce the issue and to avoid discussion ping-pong, it's normally easier to submit that bug-report first and then create the forum thread.

    Hope it helps!

    PS: I renamed the thread title to "My Addressables Feedback", to avoid future confusion.
     
  7. _Daniel_

    _Daniel_

    Joined:
    Feb 28, 2007
    Posts:
    2,615
    I agree with the idea of blocking for loading. Async loading code is quite a bit more complex to write in my experience. Being able to load an asset immediately allows for easy to understand code.
     
  8. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    276
    I don't like the idea of synchronous loading at all due to screen freezes.
    If you have a few assets you want to load in order you can easily create a coroutine where you call the Addressable system to load the asset then yield return them all until they are loaded. Super easy and not even complex code required. You can actually hang a loading bar UI on it that way and it won't freeze your screen while loading the assets.
    Who would want a screen freeze from a synchronous call?...
     
  9. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    2,746
    The point I was trying to make here is to use async loading to preload all assets, for example during a loading screen.

    Then later during gameplay, to access those pre-loaded assets, I don't want to handle async loading and instantiation anymore, because I know I did preload those assets. I just want to use Load/Instantiate, which would block if the asset would not have been pre-loaded, but because it was pre-loaded, it's really just a table lookup to return the asset in question.

    This allows to reduce the game code complexity a fair amount, because you don't have to deal with coroutines/async handling at all. That's the reason why I want additional Load/Instantiate methods that are non-async.
     
    SugoiDev and scvnathan like this.
  10. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    276
    I still don't understand your case completely with the information you gave, but wouldn't want to devote the attention to that either by continue asking for more info. Also code complexity is debatable, I don't find coroutines or asynchronous systems complex.

    But if I were to build a simple async pre-loading system I'd just let all systems(asset containers) pre-load their required assets. Wait for them all to tell me they have completed loading (simple event) and then remove the loading screen if all were to be complete. Whenever an asset is then required to instantiate it has already been loaded, only would need to call the Instantiate method.

    i.e show Loading screen -> Send Event for Loading Systems -> Systems announce themselves to a list -> start loading the linked addressable asset -> On Complete loading -> save asset in a variable for later use -> Send Event that the system has completed loading -> if all systems that were announced have completed loading, remove loading screen.

    No coroutine required. An Awake / Start would suffice to send the event and an event receiver method that checks on complete if the loading queue is empty or not.
     
  11. rastlin

    rastlin

    Joined:
    Jun 5, 2017
    Posts:
    70
    Your approach basically doubles the asset-lookup table size, which already is within addressables core.

    Async code is infinitely more complex than synchronous fetch of the asset, that's not up for debate. With asynchronous fetch you need to handle cancellation and possibility of assets arriving in different order they ware requested, which may or may not be an issue. If you did not had to handle such cases it does not means that more advanced project can function properly without them.

    By the looks of it the Addressables is great... on paper. Based on Berlin talk there is still so much work required for this approach to be production ready I think it will not be in usable state by the end of 2018. Lack of proper variant support (labelling? c'mon...), no possibility to override the asset, all the valid points Peter77 mentioned. I also think single reference-table asset will be a merge-hell, I liked the old approach of per-asset address much better. The blocking fetch is a must before I can start thinking of switching my project to this package.

    They basically laid out nice API and an answer for most of the questions was "You need to code that yourself" ... which we kind of already do with asset bundles. On the one hand they resolved some of the bigger annoyances of the old system, and on the other they took away some of the core strengths.

    Looking forward for Addressables team to prove me wrong in next few months :) .
     
    Peter77 and _Daniel_ like this.
  12. MaskedMouse

    MaskedMouse

    Joined:
    Jul 8, 2014
    Posts:
    276
    Doesn’t rlly, a few scripts that just listen to an event, announce themselves, load their assets which is addressables.loadasset and on complete do an event back that it is ready.

    Thats just a modular way of handling multiple asset loads that need to be done.
    I.e you want your UI system to load, a list of monster prefabs, environment prefabs.
    just a loading script that keeps track if everything has been loaded or not.
    Its dead simple and doesn’t need a synchronous call, just a few events...
    So thats not rlly double asset lookups.

    I rather not have my game freeze over loading assets, in my case they need to be downloaded from server wouldn’t want to let the game freeze while it may be downloading 50-100mb of assets. Ppl will just kill the game thinking it doesn’t respond due to freezing download+loading calls.

    If thats all it has to do it is pretty much no complexity. All you need is a class with a queue, some events on complete / on error handling. If you want to cancel it you call off the queue... so simple. If you find that infinitely more complex then idk.
    You may have a completely different view on the matter at hand than I have or we’re on 2 completely different skill levels.
     
  13. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    265
    @Peter77 so we have pretty much written the exact same solution our end about 6 months ago. Though we made one major improvement - We were able to make our ResourceRef<T> be typed, so no need for attributes or whatever and we wrote custom serialization against our own kind of prefab that held the references. We also used our own UI drawer system to completely replicate the functionality of the editor experience. Works really well. The only more recent improvement we made was we used the Odin Inspector plugin to do all our UI drawer rendering.

    I haven't yet had time to dive into the Addressables system but I'm not yet sure if it is better or worse than our solution - Ours has a non-perfect perf profile, but it is still only 2 hashed Dict lookups per asset per session (We cache).
     
  14. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    2,746
    If I provide feedback, I expect someone from Unity Technologies to respond that my feedback has been seen. This did not happen yet.
     
  15. hadmaerd

    hadmaerd

    Joined:
    Sep 6, 2014
    Posts:
    7
    There is no way that "addressables by default" will work as memory efficient as current addressables system. Either there will be some crazy restrictions on usage / code writing or you won't be able to track only used assets so you will have to add everything in bundles (RIP small updates).
     
  16. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    197
    Hi Peter,
    Thank you for your feedback. Let me reply to some of your concerns in chunks....

    AssetReference UI - I know this one needs work. Our intention is to make it look more like a direct reference, but not exactly like a direct reference. We want to make it clearly a drag target, but also clearly a unique thing. We haven't yet come up with a good solution for that, but it is very important to us to solve.

    all assets addressable by default - You can put a folder into the addressable system, which will make all the assets within that be addressable. The thing to keep in mind is that this will cause all of those assets to be in bundles and come with your build. If this is what you want, then that should satisfy your workflow. This is only needed though, if you are loading things by address (string name) in code or if you want the assets to show up in the dropdown. If you are loading via AssetReferences, then dragging onto the asset reference will mark that asset as addressable. If you do mark a folder as addressable, and give it an address such as "x", then everything inside of it will be loadable by "x/<rest of path>".

    types and attributes on AssetReference - we already have the attribute as you described. AssetReferenceTypeRestriction. We also have an attribute for AssetReferenceLabelRestriction should you want to restrict to a label. In addition, we've created a class AssetReference<TObject> and specific child classes of that such as AssetReferenceGameObject. Using these specific child classes, we are able to get around Unity's serialization issues and provide a proper inspector for forcibly type-restricted classes.

    non-async - We are working on a solution to this as well, but just haven't landed on something that solves things in a clean way. One of the fundamental tenets of our system is that you could change an asset from being local to being remote without your game code changing or caring. A synchronous UnityWebRequest isn't quite viable right now. We are still trying to come up with a solution, but again don't have an all-purpose solution yet. One solution that you can easily do, that we have not (yet) made into a general solution, is you can call load an asset, then just hold on to it and instantiate it yourself. Our system will only know of the one load, only have one ref count, and unload once you call one Release, but that's fine. It's difficult for us to implement a general purpose (supports online stuff) system like this, but if you only want to use Addressables to load, not to instantiate, that is a perfectly fine workflow.

    use GUID not address - everything that is addressable is also currently loadable via the Hash128 of it's GUID. Also, the AssetReference uses the GUID so that you can have a reference to something, and it can either move on-disk, or change it's address, without loosing the connection. We will definitely not get rid of addresses though. One key use-case of this system is to be able to load "Tank" and not care that the address can point to different things throughout development.


    All of your comments that we don't already have a fix for, I've noted in our internal task system to make sure we keep on top of. Both comments that I've replied to above, and some of the smaller ones I haven't (memory management, etc.).

    Thanks again for your thorough and thought out feedback,
    Bill
     
  17. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    265
    @unity_bill thanks for the reply.

    But why? As far as I can tell this system is a backend system - One that the "coders" implement and manage. Exposing artefacts into the editor UX means that everyone in the team needs to have some awareness of it. Why should a designer care about Addressables?
     
  18. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    197
    It's very possible that we'll end up with it looking exactly like a direct reference. We just haven't had time to iterate on that. There are several "why's" slowing us down, but the most important one is that dragging an asset on the an AssetReference marks that asset as addressable. Changing or removing that asset from the reference does not un-mark it as addressable, because it could be used elsewhere. A standard object reference has no such external affect when adding an asset to it.

    Again, we may end up looking exactly like an object reference, but need to make sure we understand the implications with whatever we land on.

    Thanks for trying this thing out!
    -Bill
     
  19. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    2,746
    Hi Bill, thanks for your reply!
     
  20. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    3,086
    My 2cents:

    - Async Loading only. Yes, it's more code complexity, but if you just assume everything happens async you'll be better off in the long run. Once you have both, you run into a ton of refactoring every time anything changes, which is worse than just having a single code path to work with.
    - I can go either way on marking assets- our system built on asset bundles does this, and we have minimal issues with it because it's coders writing all the loading code and we handle dependancies automatically. If I was to write an entirely new system from the ground up, I would have gone with something closer to a torrent file- where all data in the tree is packed into blocks for downloading, and requesting any asset would effectively request and verify that certain blocks of data are up to date or not. Options for preloading this data or fetching it ahead of time, streaming it from multiple sources and other clients, etc. There would be some tradeoff's between block size, packet size, padding, download size, etc, but seems manageable. Server side hard drive space is cheap.
     
  21. zornor90

    zornor90

    Joined:
    Sep 16, 2015
    Posts:
    97
    Out of curiosity, how did you pull the typing off - did you require the ResourceRef to always be stored in a specially serializable prefab? I'm trying to ensure that I can add my AssetReference<T> class to any script and have it get serialized, considering cheating by using Odin
     
  22. strich

    strich

    Joined:
    Aug 14, 2012
    Posts:
    265
    Ah. Yeah we're cheating here - We've built our own ECS system and use our own ScriptableObject-based 'blueprints' that store data and references to prefabs.

    You should definitely get Odin though. Should solve the problem for you.
     
    zornor90 likes this.
  23. Paul_Bronowski

    Paul_Bronowski

    Joined:
    Sep 3, 2014
    Posts:
    40
    Liking it so far, but +1 to missing synchronous API as a formidable barrier to entry.

    Please tell me there's a better way...

    Code (CSharp):
    1.     /// <summary>
    2.     /// ServicesManagerService AssetReference
    3.     /// </summary>
    4.     [System.Serializable]
    5.     public class AssetReferenceServicesManagerService : AssetReferenceT<ServicesManagerService> { }
    6.  
    7.  
    8.  
    9.         [SerializeField]
    10.         AssetReferenceServicesManagerService _ServicesManagerServiceAssetRef;
    11.  
    12.         private Coroutine _AwakeCoroutine;
    13.  
    14.         private Coroutine _OnEnableCoroutine;
    15.  
    16.  
    17.         /// <summary>
    18.         /// Awake this instance
    19.         /// </summary>
    20.         public void Awake()
    21.         {
    22.             // We only care about runtime
    23.             if (!Application.isPlaying)
    24.                 return;
    25.             this._AwakeCoroutine = this.StartCoroutine(this.AwakeAsync());
    26.             Debug.Log("TEST: Awake(): Completed");
    27.         }
    28.  
    29.         /// <summary>
    30.         /// Awake this instance (async)
    31.         /// </summary>
    32.         public IEnumerator AwakeAsync()
    33.         {
    34.             // We only care about runtime
    35.             if (!Application.isPlaying)
    36.                 return;
    37.             // Load the ServicesManagerService asset (not instance)
    38.             if (this._ServicesManagerServiceAssetRef.editorAsset != null)
    39.             {
    40.                 IAsyncOperation<ServicesManagerService> asyncOp = this._ServicesManagerServiceAssetRef.LoadAsset();
    41.                 yield return asyncOp;
    42.                 ServicesManagerService instance = asyncOp.Result;
    43.                 asyncOp.Release();
    44.                 // Note: LoadAsset won't call Awake (which is good), but we want runtime properties, in this case.
    45.                 yield return this.StartCoroutine(instance.AwakeAsync());
    46.             }
    47.             Debug.Log("TEST: AsyncAwake(): Completed");
    48.            this._AwakeCoroutine = null;
    49.         }
    50.  
    51.         /// <summary>
    52.         /// Enable this instance
    53.         /// </summary>
    54.         public void OnEnable()
    55.         {
    56.             // We only care about runtime
    57.             if (!Application.isPlaying)
    58.                 return;
    59.             this._OnEnableCoroutine = this.StartCoroutine(this.OnEnableAsync());
    60.             Debug.Log("TEST: OnEnable(): Completed");
    61.         }
    62.  
    63.         /// <summary>
    64.         /// Enable this instance (async)
    65.         /// </summary>
    66.         /// <returns></returns>
    67.         public IEnumerator OnEnableAsync()
    68.         {
    69.             // We only care about runtime
    70.             if (!Application.isPlaying)
    71.                 yield break;
    72.             yield return this._AwakeCoroutine;
    73.             // TODO: Things that depend on Awake() previously having been called
    74.             Debug.Log("TEST: OnEnableAsync(): Completed");
    75.             this._OnEnableCoroutine = null;
    76.         }
    77.  
    78.         /// <summary>
    79.         /// Start this instance (async)
    80.         /// </summary>
    81.         /// <returns></returns>
    82.         public IEnumerator Start()
    83.         {
    84.             yield return this._OnEnableCoroutine;
    85.             // TODO: Things that depend on OnEnable() or Awake() previously having been called
    86.             Debug.Log("TEST: Start(): Completed");
    87.         }
    88.  
     
    mcurtiss and zhuxianzhi like this.