Search Unity

Serializing a list of ScriptableObjects that persist between Unity restart in custom inspector

Discussion in 'Immediate Mode GUI (IMGUI)' started by Suduckgames, Aug 10, 2018.

  1. Suduckgames

    Suduckgames

    Joined:
    Nov 28, 2016
    Posts:
    218
    Hi I have a custom inspector created to manage the history of my game, the history is split into "steps", each step have the information about who say the text( with an enum), when it says (with an enum) and the text.

    I know that serialize custom class can be tricky and I have read a lot about it, and I am nearly to have it working. I know about how the assembly reloads works and about hideflag.

    So I have a base class derived from ScriptableObject called HistoryPart, who contain a list of Steps (another ScriptableObject inherited class with the HideFlag.HideAndDontSave assigned)

    It is working fine when I enter play/exit mode, since the data persist. However, when I restart Unity the data get lost.

    The historyPart gameObject still have the list of steps with the same size as before the restart, however that items are null since the system is unable to recover the information from the c++ side because it doesn't know the reference ( That is what I think, but not sure).

    I not sure how to solve this, another way can be to create the steps as a real scriptable objects in the Resources folder, but I don't think it is the correct way since it will create thousand of files that can be edited manually in a mistake.

    I am missing something? Any suggestion?

    HistoryPart class
    Code (CSharp):
    1.  
    2. [CreateAssetMenu(menuName = "ScriptableObjects/HistoryPart")]
    3. public class HistoryPart : ScriptableObject
    4. {
    5.     public List<Step> steps;
    6.  
    7.     public int level;
    8.  
    9.     int currentStep = 0;
    10.  
    11.     private void OnEnable()
    12.     {
    13.         if(steps == null) steps = new List<Step>();
    14.         currentStep = 0;
    15.         //hideFlags = HideFlags.HideAndDontSave;
    16.     }
    17.  
    18.  
    19.     public Step GetCurrentStep()
    20.     {
    21.         return steps[currentStep];
    22.     }
    23.     public void StepCompleted()
    24.     {
    25.         currentStep++;
    26.     }
    27.  
    28.     public bool IsOnCurrentStage(HistoryManager.WhenHistory when)
    29.     {
    30.         //if (steps.Length > currentStep)
    31.         if (steps.Count > currentStep)
    32.         {
    33.             return when == steps[currentStep].cuando;
    34.         }
    35.         return false;
    36.     }
    37.  
    Step Class

    Code (CSharp):
    1. [System.Serializable]
    2. public class Step : ScriptableObject
    3. {
    4.     public StoryTeller.Character character;
    5.  
    6.     public HistoryManager.WhenHistory cuando;
    7.  
    8.     public string texto;
    9.  
    10.  
    11.     public void OnEnable()
    12.     {
    13.         hideFlags = HideFlags.HideAndDontSave;
    14.     }
    15.  
    16. }
    Editor Custom

    Code (CSharp):
    1. public class HistoryPartEditor : Editor
    2. {
    3.     public HistoryPart historyPart;
    4.  
    5.  
    6.     void OnEnable()
    7.     {
    8.         historyPart = (HistoryPart)target;
    9.     }
    10.  
    11.  
    12.     public override void OnInspectorGUI()
    13.     {
    14.    
    15.         int val = 0;
    16.         Int32.TryParse(target.name, out val);
    17.         historyPart.level = val;
    18.  
    19.         EditorGUILayout.BeginHorizontal("Box");
    20.         GUILayout.Label("Steps in the level " + historyPart.level + "   : " + historyPart.steps.Count);
    21.         EditorGUILayout.EndHorizontal();
    22.  
    23.         int boxHeight = 120;
    24.  
    25.  
    26.         GUILayoutOption[] boxOptions = {
    27.                 GUILayout.MaxWidth(EditorGUIUtility.currentViewWidth - 50), GUILayout.MaxHeight(boxHeight) };
    28.         GUILayoutOption[] options = {
    29.             GUILayout.MaxWidth(150), GUILayout.MinWidth(100.0f)
    30.         };
    31.         GUILayoutOption[] miniButtonOption = { GUILayout.MaxWidth(20.0f), GUILayout.MinWidth(20.0f) };
    32.         GUILayoutOption[] textOptions = { GUILayout.MaxWidth(EditorGUIUtility.currentViewWidth), GUILayout.MinWidth(40.0f),GUILayout.MinHeight(60) };
    33.  
    34.         //historyPart.prueba = EditorGUILayout.TextArea(historyPart.prueba, EditorStyles.textArea, textOptions);
    35.  
    36.         EditorStyles.textArea.wordWrap = true;
    37.  
    38.         if (historyPart.steps.Count >= 1)
    39.         {
    40.             foreach (Step step in historyPart.steps)
    41.             {
    42.                 if (step != null)
    43.                 {
    44.                     switch (step.character)
    45.                     {
    46.                         case StoryTeller.Character.COMMANDER:
    47.                             //GUI.backgroundColor = new Color(170,200,247,255);
    48.                             GUI.backgroundColor = new Color(0.66f, 0.78f, 0.96f, 1f);
    49.                             break;
    50.                         case StoryTeller.Character.PHONE:
    51.                             GUI.backgroundColor = Color.yellow;
    52.                             break;
    53.                         case StoryTeller.Character.ENEMY_COMMANDER:
    54.                             GUI.backgroundColor = new Color(1f, 0.76f, 0.85f, 1f);
    55.                             break;
    56.                         case StoryTeller.Character.NARRATOR:
    57.                             GUI.backgroundColor = Color.white;
    58.                             break;
    59.                     }
    60.                     EditorGUILayout.BeginVertical("Box", boxOptions);
    61.                     EditorGUILayout.BeginHorizontal("Box");
    62.                     GUILayout.Label("Personaje");
    63.                     step.character = (StoryTeller.Character)EditorGUILayout.EnumPopup(step.character, options);
    64.                     if (GUILayout.Button("X", EditorStyles.miniButtonLeft, miniButtonOption))
    65.                     {
    66.                         if (EditorUtility.DisplayDialog("Are you sure you want to delete the step?", "Estas seguro de que quieres hacerlo", "Yes", "No"))
    67.                         {
    68.                             historyPart.Remove(step);
    69.                         }
    70.  
    71.                     }
    72.                     EditorGUILayout.EndHorizontal();
    73.  
    74.                     GUILayout.Space(2);
    75.  
    76.                     EditorGUILayout.BeginHorizontal("Box");
    77.                     GUILayout.Label("Cuando");
    78.                     step.cuando = (HistoryManager.WhenHistory)EditorGUILayout.EnumPopup(step.cuando, options);
    79.  
    80.                     EditorGUILayout.EndHorizontal();
    81.  
    82.                     GUILayout.Space(2);
    83.                     step.texto = EditorGUILayout.TextArea(step.texto, EditorStyles.textArea, textOptions);
    84.  
    85.                     EditorGUILayout.EndVertical();
    86.  
    87.                     EditorGUILayout.Space();
    88.  
    89.                     EditorUtility.SetDirty(step);
    90.                 }
    91.             }
    92.         }
    93.         GUI.backgroundColor = Color.white;
    94.  
    95.         if (GUILayout.Button("Añadir Step"))
    96.         {
    97.             Step step = ScriptableObject.CreateInstance<Step>();
    98.             step.Init();
    99.             historyPart.steps.Add(step);
    100.         }
    101.         if (GUILayout.Button("Grabar Asset"))
    102.         {
    103.             CreateMyAsset();
    104.         }
    105.  
    106.          EditorUtility.SetDirty(target);
    107.    
    108.     }
    109.  
    110. }
     
  2. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    I think the issue is by doing things such as
    Code (csharp):
    1. step.character = (StoryTeller.Character)EditorGUILayout.EnumPopup(step.character, options);
    this is setting the step which is in memory 's character to the enum, not the asset's character.

    To set the Asset's data you need to use serialized properties. i.e.
    Code (csharp):
    1. serializedObject.FindProperty("steps")
    and set it through it the accessor functions such as
    Code (csharp):
    1.  serializedObject.FindProperty("steps").GetArrayElementAtIndex(0).intValue =(StoryTeller.Character)EditorGUILayout.EnumPopup(step.character, options);
    or
    Code (csharp):
    1. EditorGUILayout.PropertyField(serializedObject.FindProperty("steps").GetArrayElementAtIndex(0))
    (I have only quickly written this code, so some names may be off)
     
    Suduckgames likes this.