- Home /
Recreate default editor look manually without base.OnInspectorGUI [UnityEditor]
I am trying to create my own custom editor. But I wanted to start with recreating the default editor look without using the base.OnInspectorGUI method.
I managed it somewhat, with 2 main issues.
1) I cannot recreate the greyed out "Script" horizontal line + it points to the target object rather than open the script in editor when clicked
2) My toggles do not work. I can click on them but they don't toggle on and off.
Here is the snippet of my new custom editor
using UnityEditor;
[CustomEditor(typeof(LineScript))]
public class LineScriptCustomEditor : Editor
{
public override void OnInspectorGUI()
{
LineScript myTarget = (LineScript)target;
//base.OnInspectorGUI();
LineScript lineScript_Editor = myTarget.GetComponent<LineScript>();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Script");
lineScript_Editor = (LineScript)EditorGUILayout.ObjectField(lineScript_Editor,typeof(LineScript),false);
EditorGUILayout.EndHorizontal();
GameObject triggerOfInfoPanel_Editor = myTarget.GetComponent<LineScript>().triggerOfInfoPanel;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Trigger Of InfoPanel");
triggerOfInfoPanel_Editor
= (GameObject)EditorGUILayout.ObjectField(triggerOfInfoPanel_Editor, typeof(GameObject),true);
EditorGUILayout.EndHorizontal();
GameObject popupScriptHolder_Editor = myTarget.GetComponent<LineScript>().PopupScriptHolder;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Popup Script Holder");
myTarget.GetComponent<LineScript>().PopupScriptHolder
= (GameObject)EditorGUILayout.ObjectField(popupScriptHolder_Editor, typeof(GameObject), true);
EditorGUILayout.EndHorizontal();
myTarget.GetComponent<LineScript>().widthFirst = EditorGUILayout.Toggle("Width First", false);
myTarget.GetComponent<LineScript>().DebugDirections = EditorGUILayout.Toggle("Debug Directions", false);
myTarget.GetComponent<LineScript>().DebugLinePositions = EditorGUILayout.Toggle("Debug Line Positions", false);
myTarget.GetComponent<LineScript>().DebugEdgePoints = EditorGUILayout.Toggle("Debug Edge Points", false);
Here are the relevant parts in the Line Script
public class LineScript : MonoBehaviour
{
public GameObject triggerOfInfoPanel;
public GameObject PopupScriptHolder;
public bool widthFirst;
public bool DebugDirections;
public bool DebugLinePositions;
public bool DebugEdgePoints;
....
Any idea how to fix 2 issues I have ?
Answer by Bunny83 · Sep 19, 2018 at 09:36 PM
Actually you have several issues in your script. First of all all those GetComponent calls are unnecessary and actually create more errors than they might solve. "myTarget" is already a reference to the LineScript that this editor is editing. Using GetComponent might actually return the wrong script in case there are several scripts on the same gameobject. As i said the GetComponent call is completely unnecessary anyways.
Next is your editor only supports single object editing. The "target" variable was the old way how custom inspectors were written. Usually you would use the SerializedObject / SerializedProperty instance(s) that the editor is providing. The SerializedProperty class represents an iterator through all serializable values of an object including sub classes. Though if you really want to manually implement every field yourself, go ahead.
About your concrete issues. First of all the script field does not reference the script instance but the MonoScript instance that implements the class. You can use MonoScript.FromMonoBehaviour to get the reference to the MonoScript asset which implements the given MonoBehaviour.
You can make any GUI control "disabled" (grayed out) by setting GUI.enabled to false before you draw it. Keep in mind to set it back to true after drawing it since this is a state that affects all controls. In some cases it might be convenient to use EditorGUI.BeginDisabledGroup / EndDisabledGroup to disable certain controls and automatically restore the enabled state in EndDisabledGroup.
Your toggles don't work because you don't pass in the current boolean state. You always pass in false and assign the result to the variable. You have to do
myTarget.widthFirst = EditorGUILayout.Toggle("Width First", myTarget.widthFirst);
Finally your editor does not use any Undo registration which also means that the changes your editor may apply to your edited object might not be saved. You might want to have a look at the Undo class and the RecordObject method.
What's exactly the reason you want to implement the whole GUI manually? You may want to have a look at the Editor documentation and the examples.
Thank you for the answer. I found it hard to find information on this, so this is very useful. I was trying things almost blindly, following some tutorials.
Part of the reason I wanted to recreate it manually was to add space between the "Width First" and other toggles, which I think you can't do if you use base.OnInspectorGUI (I might be wrong about this), the other part was just figuring how this works and with your help I am definitely much closer to that.
I also added extra functionality. When you click the Debug options, the information appears in the editor. I just didn't mention that part cause I solved it.
I'll try work around what you mentioned.
Well, Unity has several attributes and decorators already built in. You can add headers, spaces or even set a range of an int or float value.
Header - Adds a header above the field
Range - Shows a slider which uses the $$anonymous$$ / max values given in the attribute
Space - Adds a space before the field
TextArea / $$anonymous$$ultiline - Shows a string field as a multiline text area ins$$anonymous$$d of a text field
Tooltip - Sets a tooltip for the field
With those you don't need a custom editor at all. If you implement a custom inspector those attributes will only have an effect if you use SerializedProperties and EditorGUILayout.PropertyField. In many cases it may be easier to implement a PropertyDrawer or DecoratorDrawer for specific things. The great thing about them is that you can simply use them whereever you like.
Answer by CherryPicker · Sep 20, 2018 at 10:15 AM
I just wanted to thank the previous poster for his post. It helped immensely.
I have not applied all the things he mentioned yet. Particularly using Serialize and applying the Undo class / RecordObject method.
However. I applied the previous 3 things he mentioned.
1) Using theMonsoScript instance to get the asset rather than the script instance
2) Using GUI.enabled to gray a field out
3) Making my toggles work
I will post what I did so it helps others in the future.
1) this
MonoScript lineScriptAsset = MonoScript.FromMonoBehaviour(myTarget);
lineScriptAsset = (MonoScript)EditorGUILayout.ObjectField(lineScriptAsset, typeof(MonoScript), true);
instead of this
myTarget = (LineScript)EditorGUILayout.ObjectField(myTarget, typeof(LineScript), true);
2) added these 2 lines
MonoScript lineScriptAsset = MonoScript.FromMonoBehaviour(myTarget);
GUI.enabled = false; // <---------------------------- this part here
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Script");
lineScriptAsset = (MonoScript)EditorGUILayout.ObjectField(lineScriptAsset, typeof(MonoScript), true);
EditorGUILayout.EndHorizontal();
GUI.enabled = true; // <------------------------------- and this part here
3) this
myTarget.GetComponent<LineScript>().widthFirst
= EditorGUILayout.Toggle("Width First", myTarget.widthFirst);
myTarget.GetComponent<LineScript>().DebugDirections
= EditorGUILayout.Toggle("Debug Directions", myTarget.DebugDirections);
myTarget.GetComponent<LineScript>().DebugLinePositions
= EditorGUILayout.Toggle("Debug Line Positions", myTarget.DebugLinePositions);
myTarget.GetComponent<LineScript>().DebugEdgePoints
= EditorGUILayout.Toggle("Debug Edge Points", myTarget.DebugEdgePoints);
instead of this
myTarget.GetComponent<LineScript>().widthFirst
= EditorGUILayout.Toggle("Width First", false);
myTarget.GetComponent<LineScript>().DebugDirections
= EditorGUILayout.Toggle("Debug Directions", false);
myTarget.GetComponent<LineScript>().DebugLinePositions
= EditorGUILayout.Toggle("Debug Line Positions", false);
myTarget.GetComponent<LineScript>().DebugEdgePoints
= EditorGUILayout.Toggle("Debug Edge Points", false);
And now my editor looks like this and works like the original
Which is almost like the original, except the toggles are not perfectly aligned with the object fields
But it works just like the original (although I am not sure about the RecordObject method as the previous user mentioned, I'll need to look into that).
So thanks to the original poster. It is exactly what I needed. The only question left for now being how to align the toggles and the object field and then it will be perfect.
The issue is not that your toggles are not aligned but your object fields are not ^^. The ObjectField method comes with many overloads and one is taking a label, just like the Toggle does. Using a seperate PrefixLabel works a bit different. Pretty much any EditorGUI / EditorGUILayout control has an optional label parameter.
About RecordObject, have you actually tried to change anything with your editor and then close Unity and reopen it? Did the change persist? The way Unity serializes the data has changed a few times in the past. Some changes may persist if you additionally edit something else in the same scene / on the same object that does mark the scene dirty. However directly editing prefabs or scriptable object assets may be a different story.
You may want to read through this page.
Thank you. You are correct again. Funny how I thought my toggles were wrong ins$$anonymous$$d of the field.
I corrected it. I now use this
lineScriptAsset = ($$anonymous$$onoScript)EditorGUILayout.ObjectField("Script", lineScriptAsset, typeof($$anonymous$$onoScript), true);
ins$$anonymous$$d of this
EditorGUILayout.PrefixLabel("Script");
lineScriptAsset = ($$anonymous$$onoScript)EditorGUILayout.ObjectField(lineScriptAsset, typeof($$anonymous$$onoScript), true);
And now it works. For comparison. One object field was corrected and doesn't have a prefix label and the other 2 have. One is aligned, the other 2 are not.
Also, I did manage to add a space between the toggles just using EditorGUILayout.LabelField(""); But I would like to make the space smaller somehow.
myTarget.GetComponent<LineScript>().widthFirst = EditorGUILayout.Toggle("Width First", myTarget.widthFirst);
EditorGUILayout.LabelField("");
myTarget.GetComponent<LineScript>().DebugDirections = EditorGUILayout.Toggle("Debug Directions", myTarget.DebugDirections);
myTarget.GetComponent<LineScript>().DebugLinePositions = EditorGUILayout.Toggle("Debug Line Positions", myTarget.DebugLinePositions);
myTarget.GetComponent<LineScript>().DebugEdgePoints = EditorGUILayout.Toggle("Debug Edge Points", myTarget.DebugEdgePoints);
The layout system of the I$$anonymous$$GUI system has the GUILayout.Space method.
If you have some time and want to learn more about the immediate mode GUI system, have a look at my I$$anonymous$$GUI crash course. If you have other issues you should ask a seperate question. A question should address a single issue. If you're new to UnityAnswers i highly recommend to read through the FAQs
Your answer
Follow this Question
Related Questions
Custom Editor - Is there any way to detect whether the user is in Prefab editing mode? 1 Answer
Track when the value is changed and get it 0 Answers
Better Unity Event UI 0 Answers
Custom inspector difficulties creating a Box / Group like widget 1 Answer
Setting custom amount of space between elements in a custom inspector with EditorGUILayout ? 1 Answer