Search Unity

persistent fields for custom editor

Discussion in 'Scripting' started by pierre_l, Nov 17, 2017.

  1. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    Hi all.
    The custom editor of my scripts works fine except one thing, the parameters selected or assigned in the fields are lost when leaving the scene.
    How can I save these data so that I don't have to manually change the settings in the editor fields each time I open the scene in the EDI ?
    The same way when the focus change in the customeditor, the selection of the Enumpopup is reinitialized.
    thks beforehand.
     
    Last edited: Nov 17, 2017
  2. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    I tested
    EditorUtility.SetDirty(target);
    Doesn't work.
     
  3. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    To make it clearer, here is my code. If I select PARAM_INI_2 and I move the focus to another gameobject in the EDI it comes back to PARAM_INI_1 when I put the focus back on the ENumPopup.
    Same behavior when I leave after saving the scene and I open it again.
    Code (CSharp):
    1. public class myscript : MonoBehaviour
    2. {
    3. public bool toto
    4. ...
    5. void Start()
    6.     {
    7. if (toto) { DoSomething; } else {DoSomethingElse; }
    8. }
    9. ...
    10. }
    11. [CustomEditor(typeof(myscript))]
    12. public class myscriptEditor : Editor
    13. {
    14.     public enum OPTIONS
    15.     {
    16.         PARAM_INI_1= 0,
    17.         PARAM_INI_2= 1,
    18.     }
    19.  
    20.     public OPTIONS op;
    21.    bool Type_CI;
    22. public override void OnInspectorGUI()
    23.     {
    24.         DrawDefaultInspector();
    25.         myscript myScript = (myscript)target;
    26.         op = (OPTIONS)EditorGUILayout.EnumPopup("Type of Initial Conditions:", op);
    27.         Type_CI = InstantiatePrimitive(op);
    28.         if (Type_CI) { myScript.toto = true; } else { myScript.toto = false; }
    29. }
    30. bool InstantiatePrimitive(OPTIONS op)
    31.     {
    32.         switch (op)
    33.         {
    34.             case OPTIONS.PARAM_INI_1:
    35.                 return true;
    36.                 break;
    37.             case OPTIONS.PARAM_INI_2:
    38.                 return false;
    39.                 break;
    40.             default:
    41.                 Debug.LogError("Unrecognized Option");
    42.                 return false;
    43.                 break;
    44.         }
    45.     }
    46. }
     
  4. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    I guess it's got to do with serialize but how it works , ouch ?
     
  5. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    797
    Unity won't serialize values in your editor class. You could store your options in a scriptable object, or the PlayerPrefs, or the simplest option is to move your Options data to the "myscript" class rather than its editor.
     
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    The tl;dr is that EditorUtility.SetDirty doesn't set the scene dirty, so your data isn't saved. Unity broke this when multi-scene editing was introduced, and decided that it was a feature. Go figure. There's three fixes:

    - Explicitly set the scene dirty:
    Code (csharp):
    1. EditorUtility.SetDirty(target);
    2. EditorSceneManager.MarkSceneDirty(((MyScript) target).gameObject.scene);
    - Use Undo.RecordObject instead:
    Code (csharp):
    1. public override void OnInspectorGUI() {
    2.     Undo.RecordObject(target);
    3.     //old code goes here
    4. }
    - Use the serializedObject interface to work with your code instead of working directly with the target.
    Code (csharp):
    1. //instead of:
    2. if (Type_CI) { myScript.toto = true; } else { myScript.toto = false; }
    3. //you do:
    4. var toto = serializedObject.FindProperty("toto");
    5. if (Type_CI) { toto.boolValue = true; } else { toto.boolValue = false; }
    6. serializedObject.ApplyModifiedProperties;

    The last version might look absolutely bonkers, but the advantage is that you get multi-object editing for free, as well as some other editor niceness properties.
     
  7. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    thk you both. Let's start with the simplest suggestion. flashframe, when you write
    do you mean move the block :
    Code (CSharp):
    1.     public enum OPTIONS
    2.     {
    3.         PARAM_INI_1= 0,
    4.         PARAM_INI_2= 1,
    5.     }
    6.     public OPTIONS op;
    into
    Code (CSharp):
    1.  public class myscript : [URL='http://unity3d.com/support/documentation/ScriptReference/30_search.html?q=MonoBehaviour']MonoBehaviou[/URL]r
    2. {...
    3. }
    Then OPTIONS is no longer known in the Editor
     
  8. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    flashframe is just wrong, they didn't read your code properly. You never use the OPTIONS enum inside myscript, only in the editor, so it doesn't matter if myscript has access to the enum or not.

    If you put OPTIONS next to myscript instead of inside myscript, the editor would have access. You could also have accessed it as "myscript.OPTIONS", as that's how you reference internal types. eg. if you want to reference the OPTIONS enum outside of myscriptEditor right now, it's named myscriptEditor.OPTIONS. But as long as the enum's only used inside of the editor, it belongs inside of the editor.


    (also standard naming convention is "MyScript" and "Options")
     
  9. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    797
    I thought he was trying to find a way to make his enum selection in the editor persist, not the data in his script. My bad.
     
  10. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    Thks Baste for your detailed answer.
    the first 2 lines have to be written in OnInspectorGUI() ?
    I did it.
    for the moment I get :
    Assets/Scripts/tire.cs(389,9): error CS0103: The name `EditorSceneManager' does not exist in the current context
    Assets/Scripts/tire.cs(390,14): error CS1501: No overload for method `RecordObject' takes `1' arguments
    Assets/Scripts/tire.cs(397,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
     
    Last edited: Nov 17, 2017
  11. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    Those were three options. You only need to use one of them, not all three.

    For the first one, You'll need to import UnityEditor.SceneManagement to get access to EditorSceneManager.


    My Undo example is just straight up wrong, forgot a parameter. It's:
    Code (csharp):
    1. Undo.RecordObject(target, "Text Goes Here");
    where "Text goes here" is what shows up on the label under Edit->Undo
     
  12. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    ok thks
    I tested the third option, it did'nt solve the issue. the fields are still not persistent. What am I doing wrong?
    here is the code :
    Code (CSharp):
    1. public class myscript : MonoBehaviour
    2. {
    3. public bool toto
    4. ...
    5. void Start()
    6.     {
    7. if (toto) { DoSomething; } else {DoSomethingElse; }
    8. }
    9. ...
    10. }
    11. [CustomEditor(typeof(myscript))]
    12. public class myscriptEditor : Editor
    13. {
    14.     public enum OPTIONS
    15.     {
    16.         PARAM_INI_1= 0,
    17.         PARAM_INI_2= 1,
    18.     }
    19.     public OPTIONS op;
    20.    bool Type_CI;
    21. public override void OnInspectorGUI()
    22.     {
    23.         var toto = serializedObject.FindProperty("toto");
    24.         DrawDefaultInspector();
    25.         myscript myScript = (myscript)target;
    26.         op = (OPTIONS)EditorGUILayout.EnumPopup("Type of Initial Conditions:", op);
    27.         Type_CI = InstantiatePrimitive(op);
    28.         if (Type_CI) { toto.boolValue = true; } else { toto.boolValue = false; }
    29.         serializedObject.ApplyModifiedProperties;
    30.  
    31. }
    32. bool InstantiatePrimitive(OPTIONS op)
    33.     {
    34.         switch (op)
    35.         {
    36.             case OPTIONS.PARAM_INI_1:
    37.                 return true;
    38.                 break;
    39.             case OPTIONS.PARAM_INI_2:
    40.                 return false;
    41.                 break;
    42.             default:
    43.                 Debug.LogError("Unrecognized Option");
    44.                 return false;
    45.                 break;
    46.         }
    47.     }
    48. }
     
    Last edited: Nov 20, 2017
  13. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    with the undo option it doesn't change anything.
    the settings ar not persistent either
     
    Last edited: Nov 20, 2017
  14. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    So does the 1st option too. It doesn't solve the issue with unpersistent settings
     
  15. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    I wrote the script according to https://answers.unity.com/questions/458987/dropdownlist-with-string-array-in-editor-inspector.html . It doesn't work either :
    Code (CSharp):
    1. public class myscript:MonoBehaviour
    2. {
    3.     public bool toto;
    4. ...
    5. }
    6. [CustomEditor(typeof(myscript))]
    7.  
    8. public class myscriptEditor : Editor
    9. {
    10.     string[] _choices = new[] { "PARAM_INI_1", "PARAM_INI_2" };
    11.     int _choiceIndex = 0;
    12.     public override void OnInspectorGUI()
    13.     {
    14.         DrawDefaultInspector();
    15.  
    16.         _choiceIndex = EditorGUILayout.Popup(_choiceIndex, _choices);
    17.         var myScript = target as myscript;
    18.         if (_choiceIndex == 0) { myScript.toto= true; } else { myScript.toto= false; };
    19.         EditorUtility.SetDirty(target);
    20. }
    21. }
    22.  
     
  16. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,334
    You're not setting the value of op to anything during OnEnable, so whenever you select the object after a script reload (ie going in or out of play mode, or recompiling scripts), you reset it to PARAM_INI_1, causing toto to be set to true again.


    So that code works if you check what toto is during OnEnable, or in the start of OnInspectorGUI, and set op accordingly.
     
  17. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    Thks for the information. I did not see the instruction to set the value of op in your fixes. Have I missed something ?
    should it be like that :
    Code (CSharp):
    1.     void OnEnable()
    2.     {
    3.         var myScript= target as myscript;
    4.         if (myScript.toto = true) { set the enum.selection to 0; } else { set the enum.selection to 1; }
    5.     }
    I did not find the right instruction for setting the enum selection
     
    Last edited: Nov 21, 2017
  18. pierre_l

    pierre_l

    Joined:
    Aug 24, 2017
    Posts:
    28
    ok I found the solution working here https://answers.unity.com/questions/458987/dropdownlist-with-string-array-in-editor-inspector.html (see kamicazer7 's answer, the only one which works, the other solutions are not correct). I adapted it to deal with my variable 'toto' of boolean type:

    Code (CSharp):
    1.  
    2.  
    3. public class myscript : Monobehaviour
    4. {
    5. public bool toto
    6. ...
    7. void start
    8.     {
    9. if (toto) { DoSomething; } else {DoSomethingElse; }
    10. }
    11. ...
    12. }
    13.  
    14. [CustomEditor(typeof(myscript))]
    15. public class myscriptEditor : Editor
    16. {
    17.     SerializedProperty orientationProp;
    18.     string[] _choices = new[] { "param_1", "param_2" };
    19.     int _choiceIndex = 0;
    20.  
    21.     void OnEnable()
    22.     {
    23.         orientationProp = serializedObject.FindProperty("toto");
    24.         if (orientationProp.boolValue) { _choiceIndex = 0; } else { _choiceIndex = 1; }
    25.     }
    26.  
    27.     public override void OnInspectorGUI()
    28.     {
    29.         serializedObject.Update();
    30.         _choiceIndex = EditorGUILayout.Popup("Initial_Condition", _choiceIndex, _choices);
    31.         if (_choiceIndex < 0)
    32.             _choiceIndex = 0;
    33.         if (_choiceIndex == 0) { orientationProp.boolValue = true; } else { orientationProp.boolValue = false; };
    34.         serializedObject.ApplyModifiedProperties();
    35.     }
    36. }
     
    Last edited: Nov 21, 2017