Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by G4merSylver · Aug 21, 2016 at 08:20 PM · c#serializationscriptableobjectinheritance

Calling a derived class (Serialized in a Scriptable Object) returns the super class.

Greetings!

There is a large chunk of source code below, maybe its to much and someone who potentially has a useful input can be scared away so: My problem is the following, when I use the TestX(:MonoBehaviour) script everything works fine, I get the correct types back - but the EditorWindow fails to do so and returns IDatabaseType (Base Class) for the derived classes! I suspect that it is related to that TestX.cs calls everything from a monobehaviour while my database is using an ScriptableObject its base.

Below you see the class which the database types will be derived from, the classes have been marked as serializable for a reason (Storing together with a Scriptable Object.)

 [Serializable]
 public class IDatabaseType
 {
     [SerializeField]
     private object value = new object();

     [SerializeField]
     public virtual object GetValue()
     {
         return null;
     }

     [SerializeField]
     public virtual void SetValue(object newValue)
     {
 
     }
 
 }
 

Here is one of the four derived classes (All four are identical ignoring the minor differences of variable types.)

     [Serializable]
     public class DBString : IDatabaseType
     {
         [SerializeField]
         private string value;
 
         public DBString(string value)
         {
             SetValue(value);
         }
 
         public override object GetValue()
         {
             return value;
         }
 
         public override void SetValue(object newValue)
         {
             value = (string)newValue;
         }
     }

Using these five (IDatabaseType + 4 Derivations) classes I have no problems, I can properly access to the derived classes by calling them like this.

 public class Testx : MonoBehaviour {
 
     // Use this for initialization
     void Start () {
         List<IDatabaseType> x = new List<IDatabaseType>();
         x.Add(new Unity.Database.DBBoolean(false));
         x.Add(new Unity.Database.DBFloat(0f));
         x.Add(new Unity.Database.DBInt(0));
         x.Add(new Unity.Database.DBString(""));
 
         IDatabaseType DBBoolean = new Unity.Database.DBBoolean(false);
         IDatabaseType DBFloat = new Unity.Database.DBFloat(0f);
 
         Debug.Log("(1) one by one: " + x[0].GetType());
         Debug.Log("(2) one by one: " + x[1].GetType());
         Debug.Log("(3) one by one: " + x[2].GetType());
         Debug.Log("(4) one by one: " + x[3].GetType());
 
         foreach(IDatabaseType IDT in x)
         {
             Debug.Log("foreach " + IDT.GetType());
         }
 
         Debug.Log("Unique: " + DBBoolean.GetType());
         Debug.Log("Unique: " + DBFloat.GetType());
     }
 
 }



However! it gets tricky when the Scriptable Object comes into the game, while the database gets properly serialized after the serialization it fails to properly identify the classes! It no longer returns the derived class but the base class!

The Database (I removed some of the method bodies to rise visibility.) here.

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using System;
 
 [Serializable]
 public class UnityDatabase : ScriptableObject {
 
     [SerializeField]
     private string DatabaseName;
     [SerializeField]
     private int DatabaseID;
     [SerializeField]
     public IDatabaseType[] Header;
     [SerializeField]
     public string[] HeaderLabel = new string[0];
     [SerializeField]
     public List<DatabaseRow> data = new List<DatabaseRow>();
     
     /// <summary>
     /// Setup new Database
     /// </summary>
     /// <param name="Header">Database Type Setup (Headline)</param>
     public UnityDatabase(IDatabaseType[] _Header, string[] _HeaderLabel)
     {
         Header = _Header;
         HeaderLabel = _HeaderLabel;
     }
 
     /// <summary>
     /// Create new Entry
     /// </summary>
     /// <param name="withValues">Values to use</param>
     public void CreateNewEntry(params IDatabaseType[] withValues)
     {
         // Verify given values
         if(!verifyRow(withValues))
         {
             return;
         }
 
         // Create Database Entry
         data.Add(new DatabaseRow(withValues));
 
         //Debug.Log("[SYSTEM]: Created new Row.");
     }
 
     /// <summary>
     /// Get Specified Entry
     /// </summary>
     /// <returns></returns>
     public IDatabaseType GetValue(int row, int column)
     {
         return data[row].data[column];
     }
 
     /// <summary>
     /// Get Specified Row
     /// </summary>
     /// <returns></returns>
     public DatabaseRow GetValue(int row)
     {
         return data[row];
     }
 
     /// <summary>
     /// Overwrite Targetted Field
     /// </summary>
     /// <param name="newValue">New Value</param>
     /// <param name="row">Row to target</param>
     /// <param name="column">Column to target</param>
     public void SetValue(IDatabaseType newValue, int row, int column)
     {
         // Verify if target point exists
         try
         {
             IDatabaseType x = data[row].data[column];
         } catch
         {
             Debug.Log("Outside Index Range.");
             return;
         }
 
         if(verifyEntry(newValue, data[row].data[column]))
         {
             data[row].data[column] = newValue;
         }
     }
     
     /// <summary>
     /// Verify if given data is compatible with this databases header
     /// </summary>
     /// <param name="needVerification">Data to Verify</param>
     /// <returns></returns>
     private bool verifyRow(IDatabaseType[] needVerification)
     {
         // Verify Length
         if (Header.Count() != needVerification.Count())
         {
             //Debug.Log("[SYSTEM]: Length Failure.");
             return false;
         }
 
         // Verify Types
         bool typeSuccess = true;
 
         for (int i = 0; i < Header.Length; i++)
         {
             // Check if TypeOf is equal
             if (!verifyEntry(needVerification[i], Header[i]))
             {
                 //Debug.Log("[SYSTEM]: Type Failure.");
                 typeSuccess = false;
             }
         }
 
         return typeSuccess;
     }
 
     private bool verifyEntry(IDatabaseType first, IDatabaseType second)
     {
         if(first.GetType() != second.GetType())
             Debug.Log("Warning: The given Entry is not compatible with the Signature!");
 
         return first.GetType() == second.GetType();
     }
 
 }
 
 [Serializable]
 public class DatabaseRow
 {
     [SerializeField]
     public List<IDatabaseType> data = new List<IDatabaseType>();
 
     public DatabaseRow(params IDatabaseType[] RowSetup)
     {
         data = RowSetup.ToList();
     }
 }

The Database is read from a EditorWindow.

 public class EditorDatabaseManager : EditorWindow
 {
 
     [MenuItem("Database/Database Editor")]
     public static void ShowWindow()
     {
         EditorWindow.GetWindow(typeof(EditorDatabaseManager));
     }
 
     private UnityDatabase db;
 
     void OnGUI()
     {
         db = (UnityDatabase)EditorGUILayout.ObjectField(db, typeof(UnityDatabase), true);
 
         if (db == null)
             return;
 
         if (GUILayout.Button("Create new Row"))
         {
             db.CreateNewEntry(
                 db.Header
             );
         }
     
         if (db.data == null)
         {
             EditorGUILayout.HelpBox("Data Null.", MessageType.Warning);
             return;
         }
 
         int x = 0;
   
         foreach (DatabaseRow DR in db.data)
         {
             EditorGUILayout.BeginHorizontal();
             EditorGUILayout.LabelField("" + x++, GUILayout.Width(50f));
 
             for (int i = 1; i < DR.data.Count; i++)
             {
                 Type t = db.Header[i - 1].GetType().GetElementType();
                 
                 IDatabaseType IDT = db.Header[i - 1];
                 EditorGUILayout.LabelField(IDT.GetType().ToString());
             }
 
             EditorGUILayout.EndHorizontal();
         }
 
     }
 }

To create the asset object you would also need this fellow here:

 public class CreateDatabaseManager : EditorWindow
 {
 
     [MenuItem("Database/Database Creator")]
     public static void ShowWindow()
     {
         EditorWindow.GetWindow(typeof(CreateDatabaseManager));
     }
 
     private Unity.Database.DBTypes[] header = new Unity.Database.DBTypes[0];
     private string[] labels = new string[0];
     private int headerLength = 0;
     private string dbname = "Undefined";
 
     void OnGUI()
     {
         // Verify header length
         if (header.Length != headerLength || labels.Length != headerLength) {
             header = new Unity.Database.DBTypes[headerLength];
             labels = new string[headerLength];
 
             for(int i = 0; i < headerLength; i++)
             {
                 labels[i] = "" + i;
             }
 
             OnGUI();
             return;
         }
 
         // Parameters
         EditorGUILayout.BeginHorizontal();
         headerLength = EditorGUILayout.IntField("Header Index Length: ", headerLength);
         dbname = EditorGUILayout.TextField(dbname);
         EditorGUILayout.EndHorizontal();
 
         EditorGUILayout.Space();
 
         // Label Definition
         EditorGUILayout.BeginHorizontal();
         for (int i = 0; i < labels.Length; i++)
         {
             labels[i] = EditorGUILayout.TextField(labels[i]);
         }
         EditorGUILayout.EndHorizontal();
 
         // Type Definition
         EditorGUILayout.BeginHorizontal();
         for (int i = 0; i < headerLength; i++)
         {
             header[i] = (Unity.Database.DBTypes)EditorGUILayout.EnumPopup(header[i]);
         }
         EditorGUILayout.EndHorizontal();
 
         EditorGUILayout.Space();
 
         // Create on Click
         if (GUILayout.Button("Create new Table (" + dbname + ")"))
         {
             // Prepare Object
             UnityDatabase obj = new UnityDatabase(ConvertToHeader(header), labels);
 
             dbname = (dbname.Equals("")) ? "Undefined" : dbname; 
 
             AssetDatabase.CreateAsset(obj, AssetDatabase.GenerateUniqueAssetPath("Assets/" + dbname + ".asset"));
             AssetDatabase.SaveAssets();
             AssetDatabase.Refresh();
             EditorUtility.FocusProjectWindow();
             Selection.activeObject = obj;
 
             header = new Unity.Database.DBTypes[0];
             headerLength = 0;
             dbname = "Undefined";
         }
     }
 
     IDatabaseType[] ConvertToHeader(Unity.Database.DBTypes[] target)
     {
         List<IDatabaseType> db = new List<IDatabaseType>();
 
         foreach(Unity.Database.DBTypes DBT in target)
         {
             switch(DBT)
             {
                 case Unity.Database.DBTypes.Bool:
                     db.Add(new Unity.Database.DBBoolean(false));
                     break;
                 case Unity.Database.DBTypes.Float:
                     db.Add(new Unity.Database.DBFloat(0f));
                     break;
                 case Unity.Database.DBTypes.Int:
                     db.Add(new Unity.Database.DBInt(0));
                     break;
                 case Unity.Database.DBTypes.String:
                     db.Add(new Unity.Database.DBString(""));
                     break;
             }
         }
 
         return db.ToArray();
     }
 }
 

Now my problem is the following, when I use the TestX.cs script everything works fine, I get the correct types back - but the editorwindow fails to do so and returns IDatabaseType for the derived classes! I suspect that it is related to that TestX.cs calls everything from a monobehaviour while my database is using an ScriptableObject as a base.

Edit: The namespaces you should use with this are the following:

 using UnityEngine;
 using UnityEditor;
 using System.Collections;
 using System;
 using System.Linq;
 using System.Collections.Generic;

Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image G4merSylver · Aug 21, 2016 at 08:28 PM 0
Share

a quick update, It isnt because of the DatabaseRow, a bit of experimenting around with the below attached source (Testx.cs) showed that the class itself works fine.

 public class Testx : $$anonymous$$onoBehaviour {
 
     void Start () {
         List<IDatabaseType> x = new List<IDatabaseType>();
         x.Add(new Unity.Database.DBBoolean(false));
         x.Add(new Unity.Database.DBFloat(0f));
         x.Add(new Unity.Database.DBInt(0));
         x.Add(new Unity.Database.DBString(""));
  
         List<DatabaseRow> y = new List<DatabaseRow>();
         y.Add(new DatabaseRow(x.ToArray()));

         foreach (DatabaseRow DR in y)
         {
             foreach(IDatabaseType IDT in DR.data)
             {
                 Debug.Log("Nested: " + IDT.GetType());
             }
         }
 
     }
 
 }
 
avatar image G4merSylver · Aug 21, 2016 at 08:38 PM 0
Share

Another little update, by creating a copy of UnityDatabase where it doesnt inherit from ScriptableObject and calling the entries again I got the proper types. I am really getting suspicious of that ScriptableObject fellow now...

         string[] s = new string[4];
         s[0] = "0";
         s[0] = "1";
         s[0] = "2";
         s[0] = "3";
 
         UnityDatabase1 UNIDB = new UnityDatabase1(x.ToArray(), s);
         UNIDB.CreateNewEntry(x.ToArray());
 
         foreach (DatabaseRow DR in UNIDB.data)
         {
             foreach(IDatabaseType IDT in DR.data)
             {
                 Debug.Log(IDT.GetType());
             }
         }

1 Reply

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Bunny83 · Aug 21, 2016 at 11:23 PM

I answered the same question a couple of times now, so just read them over here:

http://answers.unity3d.com/answers/735548/view.html

http://answers.unity3d.com/answers/245769/view.html

http://answers.unity3d.com/answers/1107926/view.html

http://answers.unity3d.com/answers/406546/view.html

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image G4merSylver · Aug 22, 2016 at 09:18 AM 0
Share

Unfortunately Unity doesn't support inheritance in custom classes when it comes to serialization

Thank you very much!

avatar image Bunny83 G4merSylver · Aug 22, 2016 at 03:32 PM 0
Share

Of course that's only true for Unity's serialization system. The normal .NET serialization (binary formatter and the like) would work as expected. Unfortunately some crucial types in Unity (Vector2/3/4, Quaternion, Color, Color32, Rect, ...) are not marked as serializable and would need some special handling.

Unity now has two special helper classes (like the BinaryWriter / Reader) inside the Networking sub-namespace called NetworkReader / NetworkWriter. Of course they are designed for serializing data that should be send over a network connection, but they can be used to serialize most Unity types into a byte array. Of course only pure data types / structs.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

209 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

ScriptableObject error with Inheritance 2 Answers

An OS design issue: File types associated with their appropriate programs 1 Answer

Handling inheritance with JSONUtility.ToJSON 1 Answer

Why does the value of the public property of [Serializable] class persist in ScriptableObject between Editor runs ? 2 Answers

How can I improve the organization and storing of information for my FPS Character Controller? 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges