Search Unity

Loading SpriteSheet Sprites With AssetReference

Discussion in 'Addressables' started by andiPossess, Aug 29, 2018.

  1. andiPossess

    andiPossess

    Joined:
    Dec 2, 2017
    Posts:
    4
    There doesn't seem to be a way of referencing anything but the default sprite on a sprite sheet (in this case, a texture imported with Sprite Mode Multiple) using this system.

    Loading via LoadAsset<Object>() results in a reference to the texture. Loading via LoadAsset<Sprite>() results in a direct reference to the first sprite, it seems. Either way, I can't get to the data about the sprites in the texture.

    Additionally, there's no way to add addresses for each individual sprite packed into a sheet, so I can't do it that way.

    Am I missing something here?
     
  2. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    I don't know :)
    We haven't spent as much time as we'd like testing all the workflows, and sprite usage is one of them. I've added a ticket to our system to look into this, and hope to poke at it next week. I'll get back to you once I've been able to spend some time checking on this sprite case, as well as a few others.

    Thanks for testing out the system.
    Bill
     
  3. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    Hi, we've looked into this, and I've got a slight update. The short version is that sprite-sheets don't play nicely with asset bundles today, and Addressables is a wrapper around asset bundles. So sprite sheets don't play nice with addressables. We are looking into ways we could mask the problems by doing some under-the-covers magic, but we haven't worked it all out yet.

    If you broke your sprite sheet up, and then made it into a sprite atlas you could have a slightly better time. In this scenario, you could make the atlas addressable, and load it through our system. From there you'd need to load the specific frame you were after using https://docs.unity3d.com/ScriptReference/U2D.SpriteAtlas.GetSprites.html

    This is something we could more easily create wrappers around so you could do the entire thing through addressables. We are looking into that as well.
     
    MNNoxMortem likes this.
  4. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    214
    So am I understanding this right, that there is no way to access the sub-sprites of a Multiple mode Sprite via code?

    Like I get not having looked at every use case under the sun, but how is "Iterate through sub-sprites by code" not right at the top of the list? Do you guys really expect people to set up their 2d animation clips using the Mechanim System? In production and at scale!? Come on guys... thats a complete mindless time sink that is totally unnecessary, and completely unsuited to rapid iteration in a large-scale production.

    I just assumed I would be able to write a simple script, that iterates through a multi-mode Sprite, at some FPS. It's really hard to believe this use case is not supported...I have a reference directly to the multi-sprite in the inspector, but somehow can not access it's children? Yeesh
     
    Last edited: Oct 3, 2018
    vexe, Alex_May and 5argon like this.
  5. Shlemon

    Shlemon

    Joined:
    May 17, 2013
    Posts:
    8
    I also think this feature is very important. Please add it soon!
     
  6. PaulBurslem

    PaulBurslem

    Unity Technologies

    Joined:
    Oct 7, 2016
    Posts:
    48
    With the latest addressables (.5.x), you can load sub assets (such as Sprites from a spritesheet) by specifying an IList<Sprite> or Sprite[] for your load type. Here is a quick example:

    Addressables.LoadAsset<IList<Sprite>>("MySpriteSheetAddress");

    This also works for getting animations from an fbx asset.
     
  7. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    214
    Better late than never! Thanks Paul
     
  8. CapnCromulent

    CapnCromulent

    Joined:
    Sep 7, 2010
    Posts:
    43
    @PaulBurslem If I load a sprite sheet using IList, do I release each Sprite individually, or the entire IList?
     
  9. arima_moto

    arima_moto

    Joined:
    Nov 28, 2018
    Posts:
    5
    Hi!
    I am trying to load Multiple Sprite in various ways.
    (Addressables is 0.5.3)

    Code (CSharp):
    1. Addressables.LoadAsset<List<Sprite>>("Assets/Images/IconMaterial.png").Completed += op => { spritelist = op.Result; };
    2.  
    -> op.Result is null.

    Code (CSharp):
    1. Addressables.LoadAsset<Sprite[]>("Assets/Images/IconMaterial.png").Completed += op => { spritelist = op.Result; };
    2.  
    -> Error
    Assertion failed on expression: 'Thread::EqualsCurrentThreadIDForAssert(Thread::mainThreadId)'
    GetGfxDevice() should only be called from main thread

    Isn't there a good method for that?
     
  10. DigitalSalmon

    DigitalSalmon

    Joined:
    Jul 29, 2013
    Posts:
    42
    We're finding that op.Result is null when attempting to load Sprites

    Code (CSharp):
    1. yield return Addressables.LoadAssets<List<Sprite>>(keys, op => {
    2.     Debug.Log("Loaded: " + op.Result);
    3. }, Addressables.MergeMode.Intersection);
    Will continue to investigate by loading Texture2D/SpriteAtlas, but we're not familiar with SpriteAtlas, so it could be fiddly.
     
    arima_moto likes this.
  11. DigitalSalmon

    DigitalSalmon

    Joined:
    Jul 29, 2013
    Posts:
    42
    For further info. when op.Result is null, op.OperationException is as follows:

    UnityEngine.ResourceManagement.UnknownResourceProviderException: Exception of type 'UnityEngine.ResourceManagement.UnknownResourceProviderException' was thrown., Location=...

    Also - Unity 2018.3.3f1, Addressables 0.5.3
     
    arima_moto likes this.
  12. arima_moto

    arima_moto

    Joined:
    Nov 28, 2018
    Posts:
    5
    Hi,

    I decided to use SpriteAtlas after all.
    Since it seems that a new version will appear in February, I am waiting for it.
     
  13. Dawar

    Dawar

    Joined:
    Dec 7, 2014
    Posts:
    122
    Addressables.LoadAsset<Sprite>("character").Completed += op => { image.sprite = op.Result; };
     
  14. BrothaMan

    BrothaMan

    Joined:
    May 19, 2013
    Posts:
    47
    Not sure if this has worked for anyone else, but each solution did not work for me in 0.7.5 in 2018.3.12f

    The example below only works in "Fast" Mode.

    Addressables.LoadAsset<IList<Sprite>>("MySpriteSheetAddress")

    Otherwise, for example in "Virtual" mode, I receive the following two errors:

    Exception encountered in operation Resource<IList`1>(Mooba Pre Match - Red.png): Provider of type UnityEngine.ResourceManagement.ResourceProviders.Simulation.VirtualBundledAssetProvider with id UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider has provided a result of type System.Object which cannot be converted to requested type System.Collections.Generic.IList`1[UnityEngine.Sprite]. The operation will be marked as failed.
    UnityEngine.ResourceManagement.ResourceProviders.Simulation.InternalOp:RequestOperation_Completed(VBAsyncOperation`1)
    DelegateList`1:Invoke(VBAsyncOperation`1) (at Library/PackageCache/com.unity.addressables@0.7.5-preview/Runtime/ResourceManager/Util/DelegateList.cs:69)
    UnityEngine.ResourceManagement.ResourceManager:Update(Single)
    MonoBehaviourCallbackHooks:Update() (at Library/PackageCache/com.unity.addressables@0.7.5-preview/Runtime/ResourceManager/Util/MonoBehaviourCallbackHooks.cs:19)

    Exception: Provider of type UnityEngine.ResourceManagement.ResourceProviders.Simulation.VirtualBundledAssetProvider with id UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider has provided a result of type System.Object which cannot be converted to requested type System.Collections.Generic.IList`1[UnityEngine.Sprite]. The operation will be marked as failed.
    UnityEngine.ResourceManagement.AsyncOperations.ProviderOperation`1[TObject].ProviderCompleted[T] (T result, System.Boolean status, System.Exception e) (at Library/PackageCache/com.unity.addressables@0.7.5-preview/Runtime/ResourceManager/AsyncOperations/ProviderOperation.cs:118)
    UnityEngine.ResourceManagement.ResourceProviders.ProvideHandle.Complete[T] (T result, System.Boolean status, System.Exception exception) (at Library/PackageCache/com.unity.addressables@0.7.5-preview/Runtime/ResourceManager/ResourceProviders/IResourceProvider.cs:95)
    UnityEngine.ResourceManagement.ResourceProviders.Simulation.VirtualBundledAssetProvider+InternalOp.RequestOperation_Completed (UnityEngine.ResourceManagement.ResourceProviders.Simulation.VBAsyncOperation`1[TObject] obj) (at Library/PackageCache/com.unity.addressables@0.7.5-preview/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualBundledAssetProvider.cs:82)
    DelegateList`1[T].Invoke (T res) (at Library/PackageCache/com.unity.addressables@0.7.5-preview/Runtime/ResourceManager/Util/DelegateList.cs:69)
    UnityEngine.Debug:LogException(Exception)
    DelegateList`1:Invoke(VBAsyncOperation`1) (at Library/PackageCache/com.unity.addressables@0.7.5-preview/Runtime/ResourceManager/Util/DelegateList.cs:73)
    UnityEngine.ResourceManagement.ResourceManager:Update(Single)
    MonoBehaviourCallbackHooks:Update() (at Library/PackageCache/com.unity.addressables@0.7.5-preview/Runtime/ResourceManager/Util/MonoBehaviourCallbackHooks.cs:19)


    Trying to figure out what LoadAsset type I need to pass in order for the Resource Manager to know it should be loading a collection of Sprites and not a single Texture2D.

    Unfortunately, this is a big time blocker for me to use Addressables if there isn't away to load Sprites from a spritesheet. Too far into development to switch my spritesheets to SpriteAtlas

    Also tried the LoadAssets method, couldn't get that to work either.
     
  15. BrothaMan

    BrothaMan

    Joined:
    May 19, 2013
    Posts:
    47
    @unity_bill Any chance you can help with my question above about loading spritesheets through addressables?

    Basic Addressables.LoadAsset<Sprite> doesn't work on a single sprite png for me either. I tried to load by string pointing to the Asset and by AssetReferenceSprite, still only expects to load a Texture2D and not a Sprite
     
    Last edited: May 9, 2019
  16. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
  17. WillJey

    WillJey

    Joined:
    Feb 12, 2019
    Posts:
    3
    Hi, @unity_bill, @BrothaMan
    Addressables.LoadAsset<IList<Sprite>>("MySpriteSheetAddress") fail in the virual mode.
    There's bugs in VirtualAssetBundle.LoadAssetOp<TObject> : VBAsyncOperation<TObject>. Where TObject is ALWAYS System.Object. I Thinks TObject Should Be 'IList<Sprite>'.
    Sorry for my poor English.
    Check on Latest
    [0.8.6] - 2019-05-14
     
  18. BrothaMan

    BrothaMan

    Joined:
    May 19, 2013
    Posts:
    47
    Thanks @unity_bill

    So if I understand correctly, as of v0.8.6, loading sprites from a spritesheet is not supported, as it will throw the error that @WillJey and I have found. In a future version, this will be fixed and when it does, you will provide an example of how to use it in the samples area of github link you provided above.

    Is that correct?
     
  19. WillJey

    WillJey

    Joined:
    Feb 12, 2019
    Posts:
    3
    I Found that It Fixs on Latest 1.1.4.https://docs.unity3d.com/Packages/com.unity.addressables@1.1/changelog/CHANGELOG.html
     
  20. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
  21. JonathanHoward

    JonathanHoward

    Joined:
    May 23, 2017
    Posts:
    1
    @unity_bill Is there any word on the Sprite sample project you had mentioned back in May? It'd be a great reference because I am having issues with Sprites being unloaded from memory after loading the asset in via Addressables. It will remain in the memory footprint regardless of successfully calling Release.
     
  22. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    A few comments...
    1. the fix to the sprite sheet problem only got put into 19.3.0a9 (or maybe 0a8, i'm not exactly sure). Once I can confirm that, I'll bring the sprite sample back, but know parts of it will only work in the latest 19.3. I'll make it clear in the demo which is which.
    2. For unloading, keep in mind we can't unload part of a bundle. So if you load 5 things from a bundle, then release 4, all 5 are in memory until the last goes away. https://docs.unity3d.com/Packages/com.unity.addressables@1.1/manual/MemoryManagement.html
    3. we are working on a "asset reference to sub asset" system, targeting release of it in a 1.2.x sometime in September-ish. The idea would be that you could drag a SpriteAtlas, Texture, FBX, etc. onto an Asset reference. The single dropdown would switch to a double. The first showing that your asset is, say, "mySpriteAtlas", and the second pointing to something inside it, say "sprite_3". This isn't fully fleshed out yet, but should make the sprite/atlas/sheet/2d workflow cleaner.
     
  23. Ferazel

    Ferazel

    Joined:
    Apr 18, 2010
    Posts:
    349
    In regards to #2 in our custom asset management wrapper we store references to the object and references to the AssetBundle. The asset destroyed when the ref count reaches zero. So let's say you have a Texture loaded from an asset bundle (TextureA 1, AssetBundle 1) load another texture from the AB (TextureA 1, TextureB 1, AssetBundle 2). When the ref count reaches 0, we manually call Destroy() on the texture to remove the memory. This doesn't work with prefabs or indirectly loaded assets (referenced components), but it does for direct loads. Is that an option for the addressable system in the future so that we don't need to be so granular with our asset bundles and rely on that fully for unloading the assets? In particular textures or other large Unity objects?

    #3 sounds promising, but again but I'm cautious because the designer/artist workflow is to find the sprite image not the sprite that would be a sub-asset of the sprite atlas (I'm guessing?). If they could link to the sprite that is a sub-asset of the image and still reference the atlas, that I think would be ideal. At the very least, if you could have a nicer picker for sub-asset sprites to find the image that'd be OK too.
     
  24. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    We've explored it, but it can run into some scenarios where things break. For example, say you do this:
    • load textureX from bundleX
    • load prefabA from bundleA
      • this has a direct ref to textureX, so that gets an additional ref count
    • load prefabB from bundleA
    • release prefabA
    • release prefabB
    At this point, we'd have a ref count of 0 on textureX, but prefabA is still in memory (even though it also has a ref count of 0). If we destroy textureX, we would most likely be in trouble come time to load prefabA again. This may be a weird edge case, but the point is, we can't keep up with all the weird edge cases people may do in their games. directly destroying things is quite delicate.
    now, it should be possible for you to extend addressables to do that, assuming you can know what edge cases you may or may not hit. You could load a texture through addressables, release it through addressables, then manually destroy it. I think. I haven't tested this, but in theory it should work.

    The idea would be that an artist could drag a sprite (from the source image) onto an asset reference, and if that sprite was in a SpriteAtlas that we already knew was addressable, we'd link it. The place this breaks down compared to the old sprite workflow, is if you didn't have a thing in a sprite atlas, then dragged it to an asset reference, then later added it to an atlas, we wouldn't have a way to know that. Though we are exploring if there are ways we could detect this (or at least error check for it in Analyze).
     
  25. mcteapot

    mcteapot

    Joined:
    Aug 26, 2013
    Posts:
    7
    I am trying to load Sprite or even a number of SpriteAtlas via AssetLabelReference. I keep running into errors when loading via the Addressable.

    Code (CSharp):
    1.  
    2. static public void LoadAssets(AssetLabelReference EnvironmentPortraitLableName) {
    3.  
    4.             Addressables.LoadAssetsAsync<SpriteAtlas>(EnvironmentPortraitLableName.labelString, null).Completed += OnLoadPortraits;
    5.  
    6. }
    7.  
    8. static private void OnLoadPortraits(AsyncOperationHandle<IList<SpriteAtlas>> obj) {
    9.             List<SpriteAtlas> list = new List<SpriteAtlas>();
    10.             if (obj.Result != null)
    11.             {
    12.                 list = new List<SpriteAtlas>(obj.Result);
    13.                 Debug.Log($"loaded Portraits assets count : {list.Count}");
    14.             }
    15.         }
    16.  
    17.  
    18.  
    The Other way I am trying to load Sprites is

    Code (CSharp):
    1.        
    2. static public IEnumerator LoadPortraits(AssetLabelReference EnvironmentPortraitsLableName)
    3.         {
    4.             if (EnvironmentPortraitsLableName != null)
    5.             {
    6.                 m_EnvironmentPortraitLableName = EnvironmentPortraitsLableName;
    7.                 if (m_EnvironmentDictionary == null)
    8.                 {
    9.                     m_EnvironmentPortraitDictionary = new Dictionary<string, Sprite>();
    10.  
    11.                     yield return Addressables.LoadAssetsAsync<Sprite>(m_EnvironmentPortraitLableName.labelString, op =>
    12.                     {
    13.                         Sprite ep = op;
    14.                         if (ep != null)
    15.                         {
    16.                             m_EnvironmentPortraitDictionary.Add(ep.name, ep);
    17.                         }
    18.                     });
    19.  
    20.                     m_IsPortraitLoaded = true;
    21.                 }
    22.             }
    23.         }
    24.  
    The only things that seem to load are GameObject either way. I am looking at the Addressables-Sample sample project and there is not code on loading Sprites or any object with AssetLabelReference.
     
  26. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    954
    What are the errors? And what exactly is being marked as addressable? You are using a label, so I assume you mark a bunch of things as addressable, then try to load them here.

    In the first code snippet, things should work, as long as the thing that is addressable is the SpriteAtlas itself. The second code snippet should work if you were marking the sprite assets as addressable.

    I don't really understand how you could load a GameObject with either snippet.

    The sprite sample is back online. It requires 2019.3.0a11+ and is being backported to 2019.2 and 2018.4. Note that parts of the demo can work on older Unity versions. I think all atlas stuff should be fine on older versions. It's the sprite-from-spritesheet that was broken.
    https://github.com/Unity-Technologies/Addressables-Sample
     
  27. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    1,478
    I am trying to load all assets at once using object type as below :

    var asyncOperation = Addressables.LoadAssetsAsync<Object>(addresses, null, Addressables.MergeMode.Union);

    however, the sprite assets are loaded as texture2D instead of sprite type.

    Is there no way to load sprite as sprite type without having to explicitly using Sprite type on loading? casting result to Sprite doesn't work.
     
  28. Favo-Yang

    Favo-Yang

    Joined:
    Apr 4, 2011
    Posts:
    302
    If you do not specify the type (in your example it was null), then you will get both texture2d and sprite in the results. Then you can test result.GetType() == typeof(Sprite) to figure out.

    However, it seems a bad idea to waste the memory load both types. If your intention is to simplify loading logic so you can load all assets without knowing its type in advance. Then you may want to break down it to two steps.
    1) Load LoadResourceLocations first, which gives you a loc.ResourceType for inspecting.
    2) Load assets based on the ResourceType, and ignore the unnecessaries.
     
    unity_bill likes this.
  29. KB73

    KB73

    Joined:
    Feb 7, 2013
    Posts:
    185

    If we pass in a <IList<Sprite>> it works but we're trying to abstract out and batch load stuff via just strings..
    We store off the IResourceLocator stuff and even that returns a Texture2D in resourceLocator array of 0 and Sprite in array of 1......

    So is it safe to assume whether it is a single sprite or a multiple sprite we always cast to a type IList<Sprite>? There doesn't appear to be a way to tell whether it is a single sprite or multi sprite until after we've loaded it
     
    Last edited: Oct 8, 2019
  30. Favo-Yang

    Favo-Yang

    Joined:
    Apr 4, 2011
    Posts:
    302
    Well, I would say it's a design issue for sprite asset. As long as the system wants to keep the ability to load both sprite and texture2d. It gonna return two entries for the same address. Unless you specify TObject.

    Well, if texture2d is not useful for you, you can just ignore it in the IResourceLocator stuff. It's not perfect, but good enough for your application.

    Yes. It always return collection it TObject is a collection.
     
    unity_bill likes this.