Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question sprite serialization to disk

Discussion in 'Editor & General Support' started by supernameworld, Jan 9, 2024.

  1. supernameworld

    supernameworld

    Joined:
    Sep 14, 2015
    Posts:
    16
    I am using fullserializer to generate save files which are read and written to disk. One type of field I frequently need to save/load is an array of sprites. Serializing and deserializing these fields results in them reappearing as null.

    My workaround to this would be to put the sprites in a resources folder, and instead serialize the paths to the folder, then do resources.load after de-serializing. The problem is that I do not see a way of getting the path at runtime (I know you can do it in editor mode with AssetDatabase but this is not helpful).

    My question is: how do I get the path to an object that exists in the resources folder at runtime? What is the correct way of serializing a reference to a sprite to disk? The solution has to be flexible enough that a user can change the array at runtime to other arbitrary sprites in the resources folder, of which there could be many.
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,391
    Reference to Unity objects cannot be serialised and deserialised to disk. Nor can you look up the path or GUID of assets at runtime.

    Generally you need to introduce some kind of look-up table yourself (such as a scriptable object referencing all the sprites you may need), and convert the direct references to some kind of path or ID that be used to look up from the table. Some serialisers have ways to convert these references during serialisation as well.

    This is less straight forward with built in Unity object types as you can only really work off the name of the asset as a unique identifier, as their GUID is not something we can access at runtime. With custom assets, like scriptable objects, you can have it express unique ID's.

    Alternatively, you can work with indirect references. Ergo, rather than have direct references to the sprite, you instead have a wrapper class that provides an implementation for looking up and returning the sprite based on some ID it's saved internally.

    All-in-all, you may have to build a wrapper scriptable-object type to make this workable. Otherwise you'll have to work off the names of your sprite assets.

    Simply just loading all the sprites in Resources, and then checking each one is an option, too. Not a good one, but it's an option.
     
  3. supernameworld

    supernameworld

    Joined:
    Sep 14, 2015
    Posts:
    16
    I tried the following. The metaprogram generates a static method which can then be called at runtime. All the method should do is switch on the sprite name and match it with the resource path. This requires all sprites that are looked up be in a resource folder and have non-conflicting names, which is fine.

    An additional question: When I look up sprites which are set to spritemode:multiple, it only gives me the first one, not all of them (e.g. mysprite_0 and not mysprite_n)

    Code (CSharp):
    1. const string assets_folder = "Assets/";
    2.     static void generate_sprite_fetcher(MetaProgram m)
    3.     {
    4.         List<Sprite> sprites = new List<Sprite>();
    5.         string[] guid = AssetDatabase.FindAssets("t:Sprite", new string[1] { assets_folder });
    6.         foreach (string id in guid)
    7.         {
    8.             string asset_path = AssetDatabase.GUIDToAssetPath(id);
    9.             sprites.Add((Sprite)AssetDatabase.LoadAssetAtPath(asset_path, typeof(Sprite)));
    10.         }
    11.  
    12.         m.WriteLines
    13.         (
    14.             "public class SpriteFetcher",
    15.             "{",
    16.                 "public static Sprite get(string name)",
    17.                 "{",
    18.                     "switch(name)",
    19.                     "{"
    20.         );
    21.         for (int i = 0; i < sprites.Count; ++i) m.WriteLines
    22.         (
    23.                         $"case \"{sprites[i].name}\": return Resources.Load<Sprite>(\"{trim_to_resource_folder(AssetDatabase.GetAssetPath(sprites[i]))}\");"
    24.         );
    25.         m.WriteLines
    26.         (
    27.                         "default: Debug.LogError($\"tried loading resource {{name}} but was not found in a resource folder!\"); return null;",
    28.                     "}",
    29.                 "}",
    30.             "}"
    31.         );
    32.     }
    The code needs to grab all of the sprites, even the ones that come from a sliced image set to multiple. How do I grab all of them and not just the first one?
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,391
    I believe sprite mode - multiple is just an importer thing, which generates a bunch of sub-object sprites. I don't believe you can access sub-objects at runtime without having existing references to them.

    Notably with Addressables, sub-objects need to be specifically referenced via an asset reference. You're not able to derive sub-objects through the parent object. This is likely the same with the old Resources API.
     
  5. supernameworld

    supernameworld

    Joined:
    Sep 14, 2015
    Posts:
    16
    Does that not mean it is impossible to address objects in unity without having a reference to every sprite in my commercial project active in memory simultaneously? Its a large scale game so I cannot just keep live references to every sprite laying around all the time there are thousands of them.

    E: even if there was just a way to know _how_many_ subobjects there are, it would work, because I could just write $"{spritename}_{i}". Maybe there is a way to do that?
     
    Last edited: Jan 10, 2024
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,391
    The addressables system can allow you to have indirect references that can be loaded on an as-need basis. You will just need a middle-man system that handles this.
     
  7. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    8,391
    I'm like 99.99% sure you can only access sub-objects via the main object in the editor only. At runtime you need pre-established references.

    Again, sprite mode - multiple is an importer setting; it's not reflected in the actual sprite properties. It just generates a bunch of sub-object sprites, but the parent object has no way to access them directly or any knowledge of their existence. The setting is stored in the editor-only meta file as part of the importer.
     
  8. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    6,922
    You know these paths. You program to load resources from a Resources folder, so you know the paths. Neither the user nor your app is ever going to save files to a Resources folder. You may want to use StreamingAssets instead.


    The important question here is: WHY are you trying to do this?

    Sounds to me a lot like you try to do this to solve a problem you may not actually have, or where you could use a Unity built-in module or package that you aren't aware of (eg Addressables as mentioned).

    If you have many sprites, surely you are using Sprite Atlases? If not, look into them. You'd be wasting both performance and memory not using atlas sprites. And when it comes to memory usage, there are also options to use compressed textures or reduced color depth.