Unity Customize Editor

While developing games, I see many developers or teams will customize the editor of their owns corresponding to their demands on the project. For example, supposed someone is developing a game, in which PlayerPrefs data should be frequently checked and modified. In traditional way, we can only print out debug information or directly modify the PlayerPrefs file (Not recommended). By using editor extensions, we can now open a window from the toolbar, then check and modify the data inside the window. That’s so much convenient and efficient!

Reference tutorial: Editor Scripting

Customize Inspector

Customize inspector means we can modify the default inspector of any component, including the built-in ones and the custom ones.

Suppose we have a custom component named TestObject, which has two fields and a property.

  • Health: Current health.
  • Max Headth: Max health.
  • HealthPercentage: Percentage of the current health to max.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using UnityEngine;
using UnityEditor; // Import the necessary namespace

public class TestObject : MonoBehaviour {

public float health;
public float maxHealth;

public float HealthPercentage{
get{
return health / maxHealth * 100f;
}
}
}

By default, because HealthPercentage is a property with public getter and private set, it will not be shown in the inspector. Then we can use editor extension to show that value.

1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(TestObject))]
public class TestObjectEditor : Editor {

public override void OnInspectorGUI(){
DrawDefaultInspector();

TestObject to = (TestObject)target;
EditorGUILayout.LabelField("Health Percentage", to.HealthPercentage.ToString() + "%");
}
}

We create a new class inherited from Editor, and rewrite its OnInspectorGUI() function.

DrawDefaultInspector simply draws the default inspector view (here the view contains Health and MaxHealth).

Add Functional Buttons

Also we can bind function to a button in the inspector so that we can call our functions directly from the editor and improve our workflows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestObject : MonoBehaviour {
...

public void IncrementHealthByOne(){
maxHealth += 1;
}
}

public class TestObjectEditor : Editor{

public override void OnInspectorGUI(){
...

if(GUILayout.Button("Increment Max Headth By One"))
to.IncrementHealthByOne();
}
}

After that, a button will be shown in the inspector of TestObject, which allows us to increase the max health by one.

Menu Items

The Unity editor allows adding custom menus that look and behave like the built-in menus. If we want to add/extend a new menu to the top-level toolbar, we should write the corresponding static function and mark it with the MenuItem attribute.

1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;
using UnityEditor;

public class MenuItems
{
[MenuItem("Tools/Clear PlayerPrefs")]
private static void NewMenuOption()
{
PlayerPrefs.DeleteAll();
}
}

Customize Editor Windows

We can also create a new menu item binded with a custom window. For example, like what we said in the header of this article, we want to check and modify the PlayerPrefs data inside the inspector, just click a menu item from the toolbar, and do the manipulations in a window. That’t cooooooool.

We need to create a class inherited from EditorWindow and rewrite the OnGUI() function. Also we need to create a static function marked with the MenuItem attribute for the reason of menu item.

Here we give an example code implemented by Romejanic, which just do the job we need.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using UnityEngine;
using UnityEditor;
using System.Collections;

public class PlayerPrefsEditor : EditorWindow {

[MenuItem("Edit/Player Prefs")]
public static void openWindow() {
PlayerPrefsEditor window = (PlayerPrefsEditor)EditorWindow.GetWindow(typeof(PlayerPrefsEditor));
window.titleContent = new GUIContent("Player Prefs");
window.Show();
}

public enum FieldType { String,Integer,Float }

private FieldType fieldType = FieldType.String;
private string setKey = "";
private string setVal = "";
private string error = null;

void OnGUI() {

EditorGUILayout.LabelField("Player Prefs Editor", EditorStyles.boldLabel);
EditorGUILayout.LabelField("by RomejanicDev");
EditorGUILayout.Separator();

fieldType = (FieldType)EditorGUILayout.EnumPopup("Key Type", fieldType);
setKey = EditorGUILayout.TextField("Key to Set", setKey);
setVal = EditorGUILayout.TextField("Value to Set", setVal);

if(error != null) {
EditorGUILayout.HelpBox(error, MessageType.Error);
}

if(GUILayout.Button("Set Key")) {
if(fieldType == FieldType.Integer) {
int result;
if(!int.TryParse(setVal, out result)) {
error = "Invalid input \"" + setVal + "\"";
return;
}

PlayerPrefs.SetInt(setKey, result);
} else if(fieldType == FieldType.Float) {
float result;
if(!float.TryParse(setVal, out result)) {

error = "Invalid input \"" + setVal + "\"";
return;

}

PlayerPrefs.SetFloat(setKey, result);
} else {
PlayerPrefs.SetString(setKey, setVal);
}

PlayerPrefs.Save();
error = null;
}

if(GUILayout.Button("Get Key")) {
if(fieldType == FieldType.Integer) {
setVal = PlayerPrefs.GetInt(setKey).ToString();
} else if(fieldType == FieldType.Float) {
setVal = PlayerPrefs.GetFloat(setKey).ToString();
} else {
setVal = PlayerPrefs.GetString(setKey);
}
}

if(GUILayout.Button("Delete Key")) {
PlayerPrefs.DeleteKey(setKey);
PlayerPrefs.Save();
}

if(GUILayout.Button("Delete All Keys")) {
PlayerPrefs.DeleteAll();
PlayerPrefs.Save();
}

}

}