Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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
0
Question by al361647 · Jan 17, 2021 at 02:40 PM · scriptable objectdata storagedata visualization

Scriptable object using custom editor loses data after reopening Unity

So I'm working on a game and I decided to create a scriptable object class to store the data for different conversations. Here's the class:

 [Serializable]
 [CreateAssetMenu (fileName = "ConversationData", menuName = "ScriptableObjects/ConversationData")]
 public class ConversationData : ScriptableObject
 {
     public string NpcName;
     public string[] Interlocutors;
     public bool Loop;
     public List<List<Tuple<int, string>>> Conversations;
 }

And since Unity doesn't have a way (at least that I'm aware of) to show a list of lists of tuples in the inspector without making use of a custom inspector, that's exactly what I did:

 [ExecuteAlways]
 [CustomEditor (typeof (ConversationData))]
 public class DialogueGUI : Editor
 {
     private bool _dialoguesFoldout;
     private int _dialogues, _lines, _interlocutorIndex;
     private string _text;
     private List<List<Tuple<int, string>>> _conversations;
     private List<Tuple<int, string>> _conversation;
     private ConversationData _conversationData;
 
     public int Dialogues 
     {
         set 
         {
             _dialogues = value < 0 ? 0 : value;
         }
     }
     public int Lines 
     { 
         set => _lines = value < 0 ? 0 : value; 
     }
     public int InterlocutorIndex 
     {
         set 
         {
             _interlocutorIndex = Mathf.Clamp (value, 0, _conversationData.Interlocutors.Length);
         }
     }
 
 
     // .
     private void OnEnable ()
     {
         _conversationData = (ConversationData) this.target;
         _dialoguesFoldout = true;
     }
 
 
     // .
     public override void OnInspectorGUI ()
     {
         base.OnInspectorGUI ();
 
         _dialoguesFoldout = EditorGUILayout.Foldout (_dialoguesFoldout, "Dialogues");
         if (_dialoguesFoldout == true) 
         {
             _conversations = _conversationData.Conversations != null ? _conversationData.Conversations : new List<List<Tuple<int, string>>> ();
             EditorGUI.indentLevel += 1;
             Dialogues = EditorGUILayout.IntField ("Conversations", _conversations.Count);
 
             if (_conversations.Count != _dialogues) 
             {
                 if (_conversations.Count > _dialogues)
                 {
                     while (_conversations.Count != _dialogues)
                     {
                         _conversations.RemoveAt (_conversations.Count - 1);
                     }
                 }
                 else 
                 {
                     while (_conversations.Count != _dialogues)
                     {
                         _conversations.Add (new List<Tuple<int, string>> ()); ;
                     }
                 }
             }
             for (int i = 0; i < _dialogues; i += 1) 
             {
                 EditorGUILayout.LabelField ("CONVERSATION " + i);
 
                 _conversation = _conversations[i] != null ? _conversations[i] : new List<Tuple<int, string>> ();
                 EditorGUI.indentLevel += 1;
                 Lines = EditorGUILayout.IntField ("Lines", _conversation.Count);
 
                 if (_conversation.Count != _lines)
                 {
                     if (_conversation.Count > _lines)
                     {
                         while (_conversation.Count != _lines)
                         {
                             _conversation.RemoveAt (_conversation.Count - 1);
                         }
                     }
                     else
                     {
                         while (_conversation.Count != _lines)
                         {
                             _conversation.Add (new Tuple<int, string> (0, ""));
                         }
                     }
                 }
                 for (int j = 0; j < _lines; j += 1) 
                 {
                     EditorGUILayout.LabelField ("LINE " + j);
 
                     InterlocutorIndex = EditorGUILayout.IntField ("Interlocutor index", _conversation[j].Item1);
 
                     EditorGUILayout.LabelField ("Text");
 
                     _text = EditorGUILayout.TextArea (_conversation[j].Item2, new GUILayoutOption[] { GUILayout.Height (45) });
                     _conversations[i][j] = new Tuple<int, string> (_interlocutorIndex, _text);
                 }
                 EditorGUI.indentLevel -= 1;
             }
             _conversationData.Conversations = _conversations;
 
             if (GUI.changed == true)
             {
                 EditorUtility.SetDirty (_conversationData);
                 EditorSceneManager.MarkSceneDirty (SceneManager.GetActiveScene ());
             }
             this.serializedObject.ApplyModifiedProperties ();
         }
     }
 }

This seems to work fine when it comes to edit and store the different values of the list I've previously mentioned. However, I quickly found out that the data of that list dissapeared when closing and reopening the project for some reason (yes, only the info contained on the list, not the rest of variables that make up the class). After looking for some info on different forums, I learned of the existence of functions like "SetDirty" or "ApplyModifiedProperties" (which, as you can see, I'm using inside the "OnInspectorGUI" method after modifying the scriptable object) that are supposedly meant to fix these issues, but I haven't been able to solve the problem this way. I don't know if I'm using them wrong, if there's some stupid detail I'm missing that's provoking all of this or it's some kind of Unity bug. Whathever this might be, I'd appreciate the help a lot, I've been stuck on this for a few days now and it's becoming kind of frustrating...

Oh, by the way, the version of Unity that I'm using is 2020.1.15f1.

Comment
Add comment
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

1 Reply

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

Answer by Bunny83 · Jan 17, 2021 at 08:58 PM

It's great to see you did some research before posting here. However you have missed one important detail. Custom editors can not change how and what is serialized, only how the serialized data is presented. That means if something isn't serialized before you create your custom editor, it won't be serialized with the custom editor either.


Specifically in your case Unity's serialization system does not support your "Conversations" list.

 public List<List<Tuple<int, string>>> Conversations;

I don't have a Unity installation at hand, but as far as I can remember the generic Tuple class was never supported to begin with. Further more jagged arrays, multidimensional arrays and lists of lists are not supported either. For more information see the script serialization documentation.


You can have nested arrays, but only with a custom serializable class at each stage. So the array / list element type is a serializable class which in turn can contain another List of a different class. Be warned that Unity has a serialization depth limit. In the past it was 7 layers but I read somewhere recently that it has been increased slightly.


While the generic Tuple class is certainly quite handy, I would recommend to use your own custom type as you can give the fields proper and meaningful names. So you could do something like this:

 public class ConversationData : ScriptableObject
 {
     public string NpcName;
     public string[] Interlocutors;
     public bool Loop;
     public List<Conversation> Conversations;
 }
 
 [System.Serializable]
 public class Conversation
 {
     public List<ConversationLine> lines;
 }
 
 [System.Serializable]
 public class ConversationLine
 {
     public int interlocutorIndex;
     public string text;
 }

This construct can be serialized by Unity. You don't need any custom inspector for your scriptableobject. However if you want to customize the editor, I would recommend to look into PropertyDrawers. Usually you only need some parts to look differently. For example a PropertyDrawer for your ConversationLine class could display the text and int field inline which would make the editing a bit simpler.

Comment
Add comment · Show 1 · 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 al361647 · Jan 18, 2021 at 08:40 AM 0
Share

I don't know what to say except I love you. Thanks for saving my life.

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

112 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

Related Questions

Is there interest in more / better integrations with Unity, particularly at the Personal tier? 0 Answers

Best way to create runtime variables for Scriptable Objects?,What's the best way to create 'runtime values' for Scriptable Objects? 1 Answer

Save different game object's datas using the same script? 3 Answers

How can I record eye tracking data with the HTC VIVE SRanipal SDK? 0 Answers

How to upgrade from free to paid version and maintain established player prefrence data? 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