Search Unity

Odin Serializer problem

Discussion in 'Scripting' started by Flynn_Prime, Aug 24, 2018.

  1. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Hi, I hope someone can help me here as Odin still isn't completely documented, and i'm a serialization noob. I am trying to serialize a polymorphic list, but I am getting a warning from Unity as I am trying to use Odin to serialize a Sprite, but Unity warns to let it handle its own "weird objects". Full warning below:

    "A formatter has been created for the UnityEngine.Object type Sprite - this is *strongly* discouraged. Unity should be allowed to handle serialization and deserialization of its own weird objects. Remember to serialize with a UnityReferenceResolver as the external index reference resolver in the serialization context."

    This is the base class that contains the Sprite reference the warning refers to:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [System.Serializable]
    4. public class Item
    5. {
    6.     public string itemName;
    7.     public Sprite icon;
    8.  
    9.     public Item(string itemName, Sprite icon)
    10.     {
    11.         this.itemName = itemName;
    12.         this.icon = icon;
    13.     }
    14. }
    and the Serialization/Deserialization:

    Code (CSharp):
    1. using System.IO;
    2. using System.Collections.Generic;
    3. using OdinSerializer;
    4. using UnityEngine;
    5.  
    6. public class SaveData
    7. {
    8.     public static void SaveInventory(List<Item> inventoryItems)
    9.     {
    10.         byte[] bytes = SerializationUtility.SerializeValue(inventoryItems, DataFormat.Binary);
    11.         File.WriteAllBytes(Application.persistentDataPath + "/SaveFile.dat", bytes);
    12.     }
    13.  
    14.     public static List<Item> LoadInventory()
    15.     {
    16.         var filePath = Application.persistentDataPath + "/SaveFile.dat";
    17.  
    18.         if (File.Exists(filePath))
    19.         {
    20.             byte[] bytes = File.ReadAllBytes(filePath);
    21.             return SerializationUtility.DeserializeValue<List<Item>>(bytes, DataFormat.Binary);
    22.         }
    23.         else
    24.         {
    25.             Debug.Log("No inventory save file found");
    26.             return new List<Item>();
    27.         }
    28.     }
    29. }
    30.  
    How can I get around this? My items are procedurally generated, and therefore the Sprite icon is assigned at run time. I don't have prefabs of my items etc. I need this reference, and I need it to be serialized in this class. But I can't use Unities serialization because it does not support polymorphism.
     
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    Perhaps you get warning, as you do it wrong way. You dont want to serialize sprites as same as you dont want to serialize rigid bodies etc. You just serialize for storing what is important. For example position, angle rotation. health, maybe velocity. ALso own and/or indexes references to other objects. And encase it in class.
     
  3. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Does that mean redesigning my Item classes not to include Unity Objects at all? If not, how could I serialize a list of items, when they and their derived classes may have unity objects such as sprites in them?
     
  4. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    You should avoid wherever possible saving whole Objects to file. I mean, you can have classes, storing relevant information about given object. Or class with list of the type, of your another class. If you read file, you can simply load data to existing objects, or if you don't have them, instantiate from prefab. This way, you don't save lot of unnecessary data, which you don't need.

    For example, you may don't need storing information about the scale, if scale for each sprite object is always the same, as is driven by game logic. You may want to store name to texture, or color rgb. But you don't need store whole material, as textures are always in the same directory (relative to game/project). So again, you save on not needed data.

    You may be already familiar, but I mention in case, there is UtilityJson, to handle serialization to from files aswell.

    Code (CSharp):
    1. public class DataStorage
    2. {
    3.     public int saveTime ;
    4.  
    5.     public in id ;
    6.     public float position ;
    7.     public string textureName ;
    8.  
    9.     public List <SomeOtherProperties> myOtherProperties ;
    10. }
    11.  
    12. public class SomeOtherProperties
    13. {
    14.     public int joinedFactionTime ;
    15.     public int friendlyFaction ;
    16. }
     
    CodeSmile likes this.
  5. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,890
    What Antypodish said. Plus from my experience I would stay away from using Odin to serialize anything, or generally relying on it or any other „low-level“ plugin for anything non-editor specific. You will save yourself from incompatibilities with newer Unity versions and issues with how Odin serializes things that weren‘t supposed to be serialized (ie it may seem to work but breaks under unforeseeable circumstances). Don‘t go there as your code will be tightly coupled to things that turn out to break or cause issues you cannot resolve without a major refactoring work. If you can avoid it don‘t design your code architecture around plugins that you cannot wrap in such a way that enables you to remove it or replace it with your own (partial) implementation.
     
  6. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    I would much rather use Unity's built in serialization but I lose data when serializing a list of polymorphic data.

    I agree that it would be best to not design my code around plugin, but again, what about situations Unity can not handle?
     
  7. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,890
    I know. I would advise to design code so it works (best) with default Unity behaviour. For save/load specifically, I would design all classes so that the instances can be instatiated passing in a struct of data. Ie Item becomes a generic container with item type, number of items, item name, current usage percent and what not being simple primitive value fields in a struct, which you can the easily serialize with JsonUtility or Json.Net.

    In other words, assuming your current design implements Item with subclasses ArrowItem, ArmorItem, PotionItem etc. then that would not be a good design in general and you should consider refactoring it to be more like how ECS components are designed.
     
    Antypodish likes this.
  8. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    I think I am going to swing away from procedurally generated loot for the time being until I am more savvy with serialization itself.

    Thanks for the input. Glad I asked the question before I got too far along the design process.