4

I was following a tutorial where I need to save the sprite that belongs to an object. I am creating a database of items and would of course want the correct image to come with the item. This is how I built my Item database:

ItemObject

[System.Serializable]
public class ItemObject{

    public int id;
    public string title;
    public int value;
    public string toolTip;
    public bool stackable;
    public string category;
    public string slug;
    public Sprite sprite;


    public ItemObject(int id, string title, int value, string toolTip, bool stackable, string category, string slug)
    {
        this.id = id;
        this.title = title;
        this.value = value;
        this.toolTip = toolTip;
        this.stackable = stackable;
        this.category = category;
        this.slug = slug;
        sprite = Resources.Load<Sprite>("items/" + slug);
    }

ItemData

[System.Serializable]
public class ItemData {

    public List<ItemObject> itemList;

}

I then Save/Load ItemData.itemList. That works great. As you can see I use "slug" to load the sprite of my Item, slug is basically a code-friendly name (ex: golden_hat), then I have the same name for my Sprite.

This also works great, but Unity saves the Sprite InstanceID, which always changes on start, so if I exit Unity and load my data, it will get the wrong Image.

My Json:

{
    "itemList": [
        {
            "id": 0,
            "title": "Golden Sword",
            "value": 111,
            "toolTip": "This is a golden sword",
            "stackable": false,
            "category": "weapon",
            "slug": "golden_sword",
            "sprite": {
                "instanceID": 13238
            }
        },
        {
            "id": 1,
            "title": "Steel Gloves",
            "value": 222,
            "toolTip": "This is steel gloves",
            "stackable": true,
            "category": "weapon",
            "slug": "steel_gloves",
            "sprite": {
                "instanceID": 13342
            }
        }
    ]
}

So I'm guessing it wont work to do it this way, is there some other way to save a Sprite in Json? Or do I have to load the correct Sprite at runtime every time using my "slug"?

Programmer
  • 121,791
  • 22
  • 236
  • 328
Majs
  • 607
  • 2
  • 7
  • 20
  • You **should not** save sprites in your game. You should use AssetBundles or the Resources API. As for saving it, save the **path** of the AssetBundles or [Resources](https://stackoverflow.com/a/41326276/3785314) folder – Programmer Jun 02 '17 at 12:05
  • @Programmer Thanks, Ill save the path to Resources folder for now. And start looking into AssetBundles. – Majs Jun 02 '17 at 15:43

2 Answers2

3

If you're using JsonUtility for serialization, you can implement ISerializationCallbackReceiver in order to receive serialization callbacks. That way you can load the correct sprite based on the stored path after the object has been deserialized. You'll also avoid resource duplication.

[Serializable]
public class ItemObject : ISerializationCallbackReceiver
{
    public void OnAfterDeserialize()
    {
        sprite = Resources.Load<Sprite>("items/" + slug);
        Debug.Log(String.Format("Loaded {0} from {1}", sprite, slug));
    }

    public void OnBeforeSerialize() { }

    (...)
}

If you really want to store the sprites directly in the JSON, consider serializing the raw texture data taken from Sprite.texture.GetRawTextureData() (probably store the binary data in a base64 string) and recreating it at runtime using Sprite.Create(). The ISerializationCallbackReceiver trick will apply here too.

As an additional note, if it fits your requirements, definitely consider dropping the JSON-based object database in favour of using Scriptable Objects. This way you can easily reference UnityEngine objects without having to resort to using the Resources folder (which is not recommended unless necessary).

apk
  • 1,550
  • 1
  • 20
  • 28
  • 1
    Thanks, +1 for mentioning ScriptableObjects. But as I understand those are saved internally. And I would like players to be able to mod my game without too much issue. Which Json fits very well for – Majs Jun 02 '17 at 16:10
  • This was the method I was attempting to use, but unfortunately, you can't make calls to Resources.Load during OnAfterDeserialize or OnBeforeSerialize. – Robin Neal Apr 05 '18 at 13:54
1

A more appropriate way would be to save the texture as byte [] then your json contain the name or url of the byte [].

    {
        "id": 1,
        "title": "Steel Gloves",
        "value": 222,
        "toolTip": "This is steel gloves",
        "stackable": true,
        "category": "weapon",
        "slug": "steel_gloves",
        "sprite": "steelgloves"
    }

Then when you grab the json, you convert to C# and look for the the byte array. Once you have the byte array, you can reconstruct the sprite:

byte [] bytes = File.ReadAllBytes(path);
Texture2d tex = new Texture2D(4,4);
tex.LoadImage(bytes);
Sprite sp = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f,0.5f));
Everts
  • 10,408
  • 2
  • 34
  • 45
  • Accepted cus it answers my question. However am going to just save the path to the Resources asset instead :) – Majs Jun 02 '17 at 16:10