Search Unity

Custom Editor

Discussion in 'Editor & General Support' started by JeevanjotSingh, Mar 8, 2016.

  1. JeevanjotSingh

    JeevanjotSingh

    Joined:
    Apr 30, 2014
    Posts:
    251
    Hello,
    I am getting a problem in custom script (Just not able to get it)

    1. In my custom script anything like a text field,label if i add through a button press in Override Function then it won't work(nothing happens,Just button clicks) even i try through function call .

    2. If i turn on play mode everything i insert using for loop get erased , is there any way to store those things (text field, color patterns, objects etc.)

    Here is my example code .

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEditor;
    4.  
    5. //Creates a custom Label on the inspector for all the scripts named ScriptName
    6. // Make sure you have a ScriptName script in your
    7. // project, else this will not work.
    8. [CustomEditor(typeof(AiOperations))]
    9. public class TestOnInspector : Editor
    10. {
    11.     public override void OnInspectorGUI()
    12.     {
    13.         DrawDefaultInspector();
    14.         if (GUILayout.Button("Click!")) {
    15.             newfunction();
    16.         }
    17.     }
    18.     public void newfunction()
    19.     {
    20.  
    21.         GUILayout.Label ("This is a Label in a Custom Editor");
    22.     }
    23. }
    24.  
     
  2. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    So, how the Editor scripts mode work is they are Immediate-mode GUIs. The OnInspectorGUI() function will get called whenever the editor gets updated, the editor's drawing surface will get cleared, and the entire editor area will get repainted, that's why the label is disappearing. All the GUI* functions do is setup a draw/layout order, and you control that with data in your editor or the object it's editing.

    As for keeping changes in an editor, they'll either need to be on the target object being updated (via SerializedProperty), or as member variables of the editor itself (I believe you can have variables be marked for serialization on an Editor or EditorWindow, and those values will survive a swap into play mode, I haven't messed with this in a while however).

    What I recommend is this, based on my own experience:

    1. Create an OnEnable() function on the Editor to setup serialized property references/initialize data or cache structures in the editor.
    2. Draw in OnInspectorGUI()
    3. Create commands to manipulate data that get called by buttons and sliders and whatever else you have setup in OnInpsectorGUI()
    4. As your editor code grows, break it down into smaller drawing sub-functions and helper data manipulator functions.
    I'm at work at the moment, so I can't give you a sample from here, but I will see if I can find a simple example when I get home and can get to my old project's codebase.

    As for your example right now:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEditor;
    4. using UnityEngine.Assertions; //I prefer using assertions as it makes things easier to debug in the long run.
    5.  
    6. //Creates a custom Label on the inspector for all the scripts named ScriptName
    7. // Make sure you have a ScriptName script in your
    8. // project, else this will not work.
    9. [CustomEditor(typeof(MYCUSTOMCLASS))]
    10. public class TestOnInspector : Editor
    11. {
    12.     // cache references to help make the editor run faster, wherever
    13.     // it's appropriate
    14.     private SerializedProperty m_exampleDataProp = null;
    15.  
    16.     private void OnEnable()
    17.     {
    18.           // grab an editable wrapper to the data we want to view and manipulate
    19.           m_exampleDataProp = serializedObject.FindProperty("m_exampleData");
    20.           // ensure our property exists and it's what we expect it to be
    21.           Assert.IsNotNull(m_exampleDataProp);
    22.           Assert.AreEqual(m_exampleDataProp.propertyType, SerializedPropertyType.Boolean);
    23.     }
    24.     public override void OnInspectorGUI()
    25.     {
    26.         // draws the base inspector first, if we're going fully custom, we don't need this step
    27.         DrawDefaultInspector();
    28.  
    29.         // show the value of our data in the button.
    30.         // you don't have to do it this way but I did it for the sake of a quick example.
    31.         if (GUILayout.Button(string.Format("Click! {0}", m_exampleDataProp.boolValue))
    32.         {
    33.            ToggleData();
    34.         }
    35.  
    36.        
    37.         // display our "sub-GUI" based on some data value
    38.         if(m_exampleDataProp.boolValue)
    39.            newfunction();
    40.     }
    41.    
    42.     public ToggleData()
    43.     {
    44.          m_exampleDataProp.boolValue = !m_exampleDataProp.boolValue;
    45.     }
    46.  
    47.     public void newfunction()
    48.     {
    49.         GUILayout.Label ("This is a Label in a Custom Editor");
    50.     }
    51. }
     
  3. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    Managed to find an old example on my laptop, WARNING: SOMEWHAT COMPLICATED AND OLD

    I'm not using this version of the framework anymore, as my Entity framework got complicated, but here's an old example from an Entity-Traits I made for an earlier version of my game (Unity 4.X, so I'd rewrite it if I were to use it again in 5.X):

    Edit:
    I will admit I've tried to clean up my coding style between then and now.

    Code (CSharp):
    1. using Hydrogen;
    2. using Hydrogen.Reflection;
    3. using System.Collections.Generic;
    4. using UnityEditor;
    5. using UnityEngine;
    6. using System.Linq;
    7.  
    8. using Hydrogen.Editor;
    9.  
    10. [CustomEditor(typeof(Entity))]
    11. public class EntityEditor : Editor
    12. {
    13.     private int m_lastSelectedTraitType = 0;
    14.     private GUIContent[] m_traitGUIContent = null;
    15.     private List<System.Type> m_availableTraitTypes = null;
    16.  
    17.     private static readonly GUIContent m_addLabel = new GUIContent("+");
    18.     private static readonly GUIContent m_selectTypeLabel = new GUIContent("Select Type: ");
    19.     private static readonly GUIContent m_removeLabel = new GUIContent("-");
    20.     private static readonly GUIContent m_upLabel = new GUIContent("^");
    21.     private static readonly GUIContent m_downLabel = new GUIContent("v");
    22.  
    23.     private Entity m_editingEntity = null;
    24.  
    25.     public static void RegisterHeirarchyChangedIfNeeded()
    26.     {
    27.         EditorApplication.hierarchyWindowChanged -= OnCheckForEntityDeleted;
    28.         EditorApplication.hierarchyWindowChanged += OnCheckForEntityDeleted;
    29.     }
    30.  
    31.     private static void OnCheckForEntityDeleted()
    32.     {
    33.         var all = Resources.FindObjectsOfTypeAll<Trait>();
    34.         foreach (var trait in all)
    35.         {
    36.             if (trait.Entity == null)
    37.                 trait.AcquireEntity();
    38.         }
    39.     }
    40.  
    41.     public void OnEnable()
    42.     {
    43.         m_editingEntity = serializedObject.targetObject as Entity;
    44.         Verify.Assert(m_editingEntity != null);
    45.  
    46.         RemoveDeadTraits();
    47.  
    48.         RegisterHeirarchyChangedIfNeeded();
    49.  
    50.         PopulateTraitTypeLabels();
    51.     }
    52.  
    53.     private void PopulateTraitTypeLabels()
    54.     {
    55.         // create a list of available types, and sort them
    56.         var existing_types = (from trait in m_editingEntity.Traits where !(trait is IMultiInstanceTrait) select trait.GetType()).ToList();
    57.         m_availableTraitTypes = TypeUtils.GetInstantiableSubClasses<Trait>();
    58.         m_availableTraitTypes = m_availableTraitTypes.Except(existing_types).ToList();
    59.         m_availableTraitTypes.Sort((_a, _b) => _a.Name.CompareTo(_b.Name));
    60.  
    61.         // create an array of GUIContents from the typenames
    62.         m_traitGUIContent = (from type in m_availableTraitTypes select new GUIContent(type.Name)).ToArray();
    63.     }
    64.  
    65.     private void RemoveDeadTraits()
    66.     {
    67.         if (m_editingEntity.RemoveDeadTraits() > 0)
    68.         {
    69.             EditorUtility.SetDirty(m_editingEntity);
    70.             serializedObject.UpdateIfDirtyOrScript();
    71.         }
    72.     }
    73.  
    74.     private bool hasValidTypeList
    75.     {
    76.         get { return m_traitGUIContent != null && m_availableTraitTypes != null; }
    77.     }
    78.  
    79.     public override void OnInspectorGUI()
    80.     {
    81.         serializedObject.UpdateIfDirtyOrScript();
    82.         DrawTypeSelector();
    83.         EditorGUILayout.Separator();
    84.         DrawTraitList();
    85.     }
    86.  
    87.     private void DrawTypeSelector()
    88.     {
    89.         EditorGUILayout.BeginHorizontal();
    90.         {
    91.             m_lastSelectedTraitType = EditorGUILayout.Popup(m_selectTypeLabel, m_lastSelectedTraitType, m_traitGUIContent);
    92.  
    93.             if (GUILayout.Button(m_addLabel, GUILayout.Width(20.0f)))
    94.             {
    95.                 var new_trait_type = m_availableTraitTypes[m_lastSelectedTraitType];
    96.                 Undo.RecordObject(serializedObject.targetObject, "Undo Add " + new_trait_type.Name);
    97.                 m_editingEntity.CreateTrait(new_trait_type);
    98.                 serializedObject.SetIsDifferentCacheDirty();
    99.                 serializedObject.UpdateIfDirtyOrScript();
    100.                 EditorUtility.SetDirty(serializedObject.targetObject);
    101.             }
    102.         }
    103.         EditorGUILayout.EndHorizontal();
    104.     }
    105.  
    106.     private void DrawTraitList()
    107.     {
    108.         var trait_list = serializedObject.FindProperty("m_traits");
    109.         var total_traits = trait_list.arraySize;
    110.         var is_dirty = false;
    111.  
    112.         trait_list.isExpanded = EditorGUILayout.Foldout(trait_list.isExpanded, "Traits: " + total_traits.ToString());
    113.  
    114.         if (trait_list.isExpanded && (total_traits > 0))
    115.         {
    116.             var should_remove = false;
    117.             var remove_idx = -1;
    118.  
    119.             EditorGUI.indentLevel++;
    120.  
    121.             string name_str = null;
    122.             for (var idx = 0; idx < total_traits; ++idx)
    123.             {
    124.                 var trait_prop_element = trait_list.GetArrayElementAtIndex(idx);
    125.                 var selected_trait = trait_prop_element.objectReferenceValue as Trait;
    126.  
    127.                 #region Trait Control Buttons
    128.                 EditorGUILayout.BeginHorizontal();
    129.                 {
    130.                     if (selected_trait == null)
    131.                         continue;
    132.  
    133.                     GUILayout.Space(-10.0f);
    134.  
    135.                     name_str = selected_trait.TraitName;
    136.                     EditorGUILayout.LabelField(name_str);
    137.  
    138.                     GUILayout.FlexibleSpace();
    139.  
    140.                     if (selected_trait.gameObject != m_editingEntity.gameObject)
    141.                     {
    142.                         name_str = "Edit: " + selected_trait.gameObject.name;
    143.                         if (GUILayout.Button(name_str))
    144.                             HEditorUtility.SelectAndPing(selected_trait);
    145.                     }
    146.  
    147.                     #region Removal
    148.  
    149.                     if (GUILayout.Button(m_removeLabel, GUILayout.Width(20.0f)))
    150.                     {
    151.                         remove_idx = idx;
    152.                         should_remove = true;
    153.                     }
    154.  
    155.                     #endregion Removal
    156.                 }
    157.                 EditorGUILayout.EndHorizontal();
    158.                 #endregion
    159.             }
    160.  
    161.             EditorGUI.indentLevel--;
    162.  
    163.             #region Handle Removal
    164.  
    165.             if (should_remove && remove_idx >= 0 && remove_idx < total_traits)
    166.             {
    167.                 var remove_trait = m_editingEntity.GetTraitAtIndex(remove_idx);
    168.                 var dependent_types = m_editingEntity.GetDependentsForTrait(remove_trait);
    169.  
    170.                 Verify.Assert(m_editingEntity.HasTrait(remove_trait));
    171.  
    172.                 if (dependent_types.Count == 0)
    173.                 {
    174.                     is_dirty |= true;
    175.  
    176.                     // capture the serialized field state for the trait list
    177.                     Undo.RegisterFullObjectHierarchyUndo(m_editingEntity, "Removed Trait " + remove_trait.TraitName);
    178.  
    179.                     // remove trait from trait list
    180.                     m_editingEntity.RemoveTrait(remove_trait);
    181.                     remove_trait.Entity = null;
    182.                     remove_trait.hideFlags = HideFlags.None;
    183.  
    184.                     // create an undo-able deletion
    185.                     Undo.DestroyObjectImmediate(remove_trait);
    186.                     serializedObject.SetIsDifferentCacheDirty();
    187.                     serializedObject.UpdateIfDirtyOrScript();
    188.                     is_dirty = true;
    189.                 }
    190.                 else if (dependent_types.Count > 0)
    191.                 {
    192.                     var display_str = dependent_types[0].Name;
    193.                     if (dependent_types.Count > 3)
    194.                     {
    195.                         display_str += " and " + (dependent_types.Count - 1).ToString() + " other Traits";
    196.                     }
    197.                     else if (dependent_types.Count > 1)
    198.                     {
    199.                         for (var idx = 0; idx < dependent_types.Count; ++idx)
    200.                         {
    201.                             if (idx < dependent_types.Count - 1)
    202.                                 display_str += ", " + dependent_types[idx];
    203.                             else
    204.                                 display_str += " and " + dependent_types[idx];
    205.                         }
    206.                     }
    207.  
    208.                     EditorUtility.DisplayDialog("Can't Remove Trait", "Cannot Remove " + remove_trait.TraitName + " because " + display_str + " depends on it!", "OK");
    209.                 }
    210.             }
    211.  
    212.             #endregion Handle Removal
    213.         }
    214.  
    215.         is_dirty |= serializedObject.ApplyModifiedProperties();
    216.  
    217.         if (is_dirty)
    218.         {
    219.             EditorUtility.SetDirty(m_editingEntity);
    220.         }
    221.     }
    222. }
     
  4. JeevanjotSingh

    JeevanjotSingh

    Joined:
    Apr 30, 2014
    Posts:
    251

    Such useful info,
    Thanks sir.
     
    recursive likes this.
  5. JeevanjotSingh

    JeevanjotSingh

    Joined:
    Apr 30, 2014
    Posts:
    251
    So in short we can say ,
    -use repaint or redraw kind of keyword to re-update oninspectorgui()
    -and use serialization ,To prevent getting disappear after playmode


    that's right sir?


    hat are drawing functions?
     
    Last edited: Mar 9, 2016
  6. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    1. Yes
    2. By drawing functions I mean GUI/GUILayout/EditorGUI/EditorGUILayout functions, anything that is meant to be called in OnInspectorGUI() or other similar editor methods. For drawing in a scene editor, this would be the Handles family of functions, for example.
     
    JeevanjotSingh likes this.
  7. JeevanjotSingh

    JeevanjotSingh

    Joined:
    Apr 30, 2014
    Posts:
    251
    Thanks for info and help :)
     
    recursive likes this.