Search Unity

Custom Editor with List<T> (or property enum) not serialized/saved

Discussion in 'Scripting' started by ThunderMusic, Jun 29, 2015.

  1. ThunderMusic

    ThunderMusic

    Joined:
    Sep 19, 2013
    Posts:
    43
    Hello everyone,
    I have a "complex" structure with inheritance and all. I had to create a custom editor to edit it in the Unity environment. I provided the code as an attached file because it would be a bit much here, but if you want me to post the code here, let me know.

    In summary, I add the ClickableObject to a GameObject in the scene (in my scene, it's a simple cube for testing purpose). The editor displays the "LoopActions" field and it is saved when I press "Play". After that, the editor displays the actions. If you want to test, you need to add an action using the button. In each action, the editor displays an EnumPopup which is never saved when I press "Play". So even if I choose "Interact" instead of "Inventory", it always comes back to "Inventory" when I press "Play". All my objects have the "Serializable" attribute. Does anyone know what I did wrong?

    Thanks

    [Edit] Even if I remove the field named "Action" in the "ClickAction" class and only keep the "ActionType" property it still does not save. And if I remove the "ActionType" property and make the "actionType" field public (and Capitalize the 'a'), it saves. Does Unity have difficulty serializing properties?[/Edit]
     

    Attached Files:

    Last edited: Jun 29, 2015
  2. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
  3. ThunderMusic

    ThunderMusic

    Joined:
    Sep 19, 2013
    Posts:
    43
    So if I understand well what is said in the link you posted, I have to create a new "Serializable" class, which inherits my generic List (i.e. public class ClickActionList : List<ClickAction>) and change the field in my "ClickableObject" class to use the "ClickActionList" class instead of "List<ClickAction>".

    That could have worked, but it's actually making it worst. Now, the collection does not serialize at all and the list is emptied when I press "Play".

    Thanks anyways. Anything else?
     
    Last edited: Jun 29, 2015
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Have you remembered to be dirty, like you should?

    Code (CSharp):
    1. if(GUI.changed) {
    2.     //be dirty in any editor script!
    3.     EditorUtility.SetDirty(target);
    4. }
    On a more serious note, if you're changing anything at all outside of an EditorGUILayout call, you have to SetDirty, otherwise the save won't change. This includes reacting to an enum popup change by changing a variable.
     
    ThunderMusic likes this.
  5. ThunderMusic

    ThunderMusic

    Joined:
    Sep 19, 2013
    Posts:
    43
    I thought about it, but I'm not sure how to use it. I tried to put it at the beginning and end of my editor, but it didn't change a thing.

    [Edit] I found EditorApplication.MarkSceneDirty() which seem to cause the editor to recognize the fact that something has changed, but the changes are still not being saved, as far as I know. [/Edit]

    Here is my editor's code :

    Code (CSharp):
    1.        
    2. public override void OnInspectorGUI()
    3.         {
    4.             this.serializedObject.Update();
    5.  
    6.             var clickableObject = (ClickableObject)this.serializedObject.targetObject;
    7.  
    8.             GUI.changed = false;
    9.  
    10.             EditorGUILayout.BeginVertical();
    11.  
    12.             clickableObject.LoopActions = EditorGUILayout.Toggle("Loop Actions", clickableObject.LoopActions);
    13.  
    14.             EditorGUILayout.PrefixLabel("Actions", EditorStyles.boldLabel);
    15.  
    16.             for (var index = 0; index < clickableObject.ActionsOnClick.Count; index++)
    17.             {
    18.                 EditorGUILayout.BeginHorizontal();
    19.                 clickableObject.ActionsOnClick[index].EditorShowContent =
    20.                     EditorGUILayout.Foldout(clickableObject.ActionsOnClick[index].EditorShowContent, "Action " + index);
    21.                 if (GUILayout.Button("Remove"))
    22.                 {
    23.                     clickableObject.ActionsOnClick.RemoveAt(index);
    24.                 }
    25.                 EditorGUILayout.EndHorizontal();
    26.  
    27.                 if (index < clickableObject.ActionsOnClick.Count
    28.                     && clickableObject.ActionsOnClick[index].EditorShowContent)
    29.                 {
    30.                     clickableObject.ActionsOnClick[index].DrawEditor();
    31.                 }
    32.             }
    33.  
    34.             if (GUILayout.Button("Add Action"))
    35.             {
    36.                 clickableObject.ActionsOnClick.Add(new ClickAction());
    37.             }
    38.  
    39.             EditorGUILayout.EndVertical();
    40.  
    41.             if (GUI.changed)
    42.             {
    43.                 EditorUtility.SetDirty(target);
    44.             }
    45.  
    46.             this.serializedObject.ApplyModifiedProperties();
    47.         }
    48.  
     
    Last edited: Jun 30, 2015
  6. ThunderMusic

    ThunderMusic

    Joined:
    Sep 19, 2013
    Posts:
    43
    As a reminder : Field enum saves perfectly, Property enum does not.

    I currently have this code in ClickAction.cs :

    Code (CSharp):
    1.         private ClickableActionTypes actionType;
    2.  
    3.         public ClickableActionTypes ActionType
    4.         {
    5.             get
    6.             {
    7.                 return this.actionType;
    8.             }
    9.             set
    10.             {
    11.                 if (value != this.actionType || this.Action == null)
    12.                 {
    13.                     this.actionType = value;
    14.                     switch (value)
    15.                     {
    16.                         case ClickableActionTypes.Inventory:
    17.                             if (this.Action is InventoryAction)
    18.                             {
    19.                                 return;
    20.                             }
    21.                             this.Action = new InventoryAction();
    22.                             break;
    23.                         case ClickableActionTypes.Interact:
    24.                             if (this.Action is InteractAction)
    25.                             {
    26.                                 return;
    27.                             }
    28.                             this.Action = new InteractAction();
    29.                             break;
    30.                         case ClickableActionTypes.MiniGame:
    31.                             if (this.Action is MiniGameAction)
    32.                             {
    33.                                 return;
    34.                             }
    35.                             this.Action = new MiniGameAction();
    36.                             break;
    37.                         case ClickableActionTypes.Viewport2D:
    38.                             if (this.Action is Viewport2DAction)
    39.                             {
    40.                                 return;
    41.                             }
    42.                             this.Action = new Viewport2DAction();
    43.                             break;
    44.                     }
    45.                 }
    46.             }
    47.         }
    48.  
    If I change it for this code, it works (though this.Action is not set, the enum value is saved) :

    Code (CSharp):
    1. public ClickableActionTypes ActionType;
     
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Since the backing field is private, Unity won't serialize it unless you specifically tell it to:

    Code (csharp):
    1. [SerializeField]
    2. privateClickableActionTypes actionType;
    3.  
    4. public ClickableActionTypes ActionType
    5. { ...
    That will make the actionType enum show up in the inspector if you're using the default inspector, but since you're not, that shouldn't be a problem.

    If you for any reason need to revert to the default inspector, you can add the HideInInspector attribute to hide it:

    Code (csharp):
    1. [SerializeField, HideInInspector]
    2. privateClickableActionTypes actionType;
    3.  
    4. public ClickableActionTypes ActionType
    5. { ...
     
    ThunderMusic likes this.
  8. ThunderMusic

    ThunderMusic

    Joined:
    Sep 19, 2013
    Posts:
    43
    Thank you. I had figured that out yesterday, but the "Action" field did not serialize, so I had to implement the "ISerializationCallbackReceiver" interface. There are many tutorials on the net. So each GameObject such as Animation, Transform, AudioClip and Sprite have to be loaded from a path and name.