Search Unity

[Serializable] not working on inherited script

Discussion in 'Scripting' started by slkjdfv, Aug 9, 2013.

  1. slkjdfv

    slkjdfv

    Joined:
    Oct 23, 2010
    Posts:
    435
    I have this editor I'm working on and it uses a list that contains an interface class. I have different classes meant for different purposes that inherit from this class. But when I try to serilize them it wont save my changes when I enter and exit playmode. See code examples below.

    using UnityEngine;
    using System;
    using System.Collections;

    Interface Class :
    Code (csharp):
    1. public interface MyInterface
    2. {
    3.     string name
    4.     {
    5.         get;
    6.         set;
    7.     }
    8.     Rect rect
    9.     {
    10.         get;
    11.         set;
    12.     }
    13.  
    14.     void Init ( );
    15.     void Init ( Rect r );
    16. }
    Abstract Class That Inherites from MyInterface :
    (This class is abstract as I want it easily modifiable)
    Code (csharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections;
    4.  
    5. [Serializable]
    6. public abstract class MyAbstract : MyInterface
    7. {
    8.     [SerializeField]
    9.     private Rect _rect;
    10.     [SerializeField]
    11.     private string _name;
    12.  
    13.     [SerializeField]
    14.     public virtual Rect rect
    15.     {
    16.         get
    17.         {
    18.             return _rect;
    19.         }
    20.         set
    21.         {
    22.             _rect = value;
    23.         }
    24.     }
    25.  
    26.     [SerializeField]
    27.     public virtual string name
    28.     {
    29.         get
    30.         {
    31.             return _name;
    32.         }
    33.         set
    34.         {
    35.             _name = value;
    36.         }
    37.     }
    38.  
    39.     [SerializeField]
    40.     public virtual int intA
    41.     {
    42.         get;
    43.         set;
    44.     }
    45.  
    46.     [SerializeField]
    47.     public virtual int intB
    48.     {
    49.         get;
    50.         set;
    51.     }
    52.  
    53.     public virtual void Init ( )
    54.     {
    55.         rect = new Rect ( );
    56.     }
    57.  
    58.     public virtual void Init (Rect r )
    59.     {
    60.         rect = r;
    61.     }
    62. }
    This Class Interits from MyAbstact :
    Code (csharp):
    1.  
    2. using System;
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. [Serializable]
    7. public class MyClass : MyAbstract
    8. {
    9.     [SerializeField]
    10.     public override int intA
    11.     {
    12.         get;
    13.         set;
    14.     }
    15.     [SerializeField]
    16.     public override int intB
    17.     {
    18.         get;
    19.         set;
    20.     }
    21.     [SerializeField]
    22.     public override Rect rect
    23.     {
    24.         get;
    25.         set;
    26.     }
    27.     [SerializeField]
    28.     public override string name
    29.     {
    30.         get;
    31.         set;
    32.     }
    33.  
    34.     public override void Init ( )
    35.     {
    36.         base.Init ( );
    37.     }
    38.  
    39.     public override void Init (Rect r )
    40.     {
    41.         base.Init ( r );
    42.     }
    43. }
    44.  
    This Class Holds all my interfaces in a list :
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. [Serializable]
    8. public class InterfaceWindowContainer : MonoBehaviour
    9. {
    10.     [SerializeField]
    11.     public List<MyInterface> interfaces;
    12.         [SerializeField]
    13.         public int TestInt = 0;
    14. }
    15.  
    All of this code works fine when I'm editing it but as soon as i hit play and go back all changes mode to the interfaces are gone, and I tested EditorGUIUtility.SetDirty On a test integer (the int is in the last script called TestInt) and that saved but not the List :(. What am I doing wrong? Can interfaces not be serilized? If not how can I fix this? Thanks for reading.
     
  2. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
  3. Jacksendary

    Jacksendary

    Joined:
    Jan 31, 2012
    Posts:
    408
    Try this; Just add it in your parent class/super class above or below the [Serializable]

    Code (csharp):
    1.  
    2. [System.Xml.Serialization.XmlInclude(typeof(TypeHere))]
    3.  
    Not sure if it works with interfaces but give it a try.
     
  4. slkjdfv

    slkjdfv

    Joined:
    Oct 23, 2010
    Posts:
    435
    at jackie0100 - that didn't work it said interface can't be serilized. I even put that in a class that inherits from it and no luck. This is very annoying.
     
  5. Jacksendary

    Jacksendary

    Joined:
    Jan 31, 2012
    Posts:
    408
    Yup as I said... Interfaces simply CAN'T be serialized then, that is one of the prizes for using them :)
     
  6. slkjdfv

    slkjdfv

    Joined:
    Oct 23, 2010
    Posts:
    435
    So just making it a class and nothing else worked and fixed my serializing issue... but now I have a new issue it wont save it as the inherited class when I enter play mode, It will revert back to it's base class... When I use SetDirty and debug the class it's saved as it shows the correct class, however when I press and debug showing the same variable it's saying that it's the base class now. How can that happen???
     
  7. slkjdfv

    slkjdfv

    Joined:
    Oct 23, 2010
    Posts:
    435
    Bump. I still need help fixing my issue please :).
     
  8. Deleted User

    Deleted User

    Guest

    This is an unfortunate truth. And it's not just interfaces. Any form of dynamic polymorphism will be broken by Unity's serialization system.
    The usual way around this is to have your inheritance path include UnityEngine.ScriptableObject at some level as the serialization system recognizes objects of this type and properly serializes them to the instance type rather than the variable type.

    Unfortunatley, interfaces can't derive from classes, so they can't inherit from UnityEngine.ScriptableObject. This means that variables with interface types are unusable for persistent data.

    This thread goes into more depth on this.
     
    Last edited by a moderator: Jan 19, 2014
  9. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Good grief! The false stuff written in here! It's mind bogging!

    Interface are just a class contract. Class that implement one can be fully serialized properly.

    First;

    Code (csharp):
    1.  
    2.     [SerializeField]
    3.     public override string name
    4.     {
    5.         get;
    6.         set;
    7.     }
    8.  
    You're trying to serialize a property, not a field! Please, apply SerializeField only to field, not property.

    Second, if you want to keep polymorphism intact between serialization, your class MUST derive from ScriptableObject or MonoBehaviour. For some reason Unity is unable to properly serialized otherwise.

    Code (csharp):
    1.  
    2. public abstract class MyAbstract : ScriptableObject, MyInterface
    3.  
    Should do it.

    A class and an interface can implement any number of different interface.
     
  10. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    this package can serialize interfaces.
     
  11. Deleted User

    Deleted User

    Guest

    @LightStriker: I'm not talking about issues with properties. I just tried it again and the variable with interface type isn't getting serialized properly, even when the instance is of a type that subclasses ScritableObject.

    Here's the code.

    Layout.cs - The interface
    Code (csharp):
    1.  
    2. public interface Layout
    3. {
    4.     int cellTotal { get; }
    5.     Rect getCell(int index, float rotation);
    6.     Vector2 getCellScaling(int index, float rotation, Texture atlas);
    7. }
    8.  
    Layout_Grid.cs - The Concrete class
    Code (csharp):
    1.  
    2. [System.Serializable]
    3. public class Layout_Grid : ScriptableObject, Layout     // ScriptableObject is a parent class
    4. {
    5.     public Vector2 cellCount, offset, size;
    6.     public int rotationOffset;
    7.     public bool verticalIndexing;
    8.  
    9.     public int cellTotal
    10.     {
    11.         get
    12.         {
    13.             return (int)(cellCount.x * cellCount.y);
    14.         }
    15.     }
    16.     public Rect getCell(int index, float rotation)
    17.     {
    18.         // ...
    19.     }
    20.     public Vector2 getCellScaling(int index, float rotation, Texture atlas)
    21.     {
    22.         // ...
    23.     }
    24. }
    25.  
    Sprite.cs - The Class who's instances are being serialized
    Code (csharp):
    1.  
    2. public class Sprite : MonoBehaviour
    3. {
    4.     [SerializeField]        // Shouldn't be necessary, but doesn't change the situation either way
    5.     public Layout layout = null;    // This is instantiated with a Layout_Grid at Sprite creation
    6.     // ...
    7. }
    8.  
    The layout member of Sprite references a Layout_Grid instance when the Sprite object is created. This is done through ScriptableObject.CreateInstance. However layout is null after serialization.

    Am I missing something? I'd love it if variables of interface type were properly serialized, but it doesn't seem to be the case.
     
    Last edited by a moderator: Jan 19, 2014
  12. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Ah! I get what you meant, and no, you can't have a serializable field's type as an interface. Unity can't know what's inside is actually deriving from one of its own serializable class, since an interface can be implemented by anything.

    Let's go back to what interface are made for... Contracts between classes that have nothing in common. In Unity's cases, since everything serializable derive from ScriptableObject (Or MonoBehaviour), why isn't your field a ScriptableObject?

    Code (csharp):
    1.  
    2. [SerializeField]
    3. private ScriptableObject myInstance;
    4.  
    5. public MyInterface MyInstance
    6. {
    7.     get { return myInstance as MyInterface; }
    8.     set
    9.     {
    10.         ScriptableObject obj = value as ScriptableObject;
    11.         if (obj == null)
    12.             return;
    13.  
    14.         myInstance = obj;
    15.     }
    16. }
    17.  
    One example where properties can be handy. This way, you enforce that whatever implement the interface is also deriving from a valid seriablizable class.
     
    Last edited: Jan 19, 2014