Unity Script Reference has some good info on making a custom editor, but all the examples use JavaScript.
I couldn’t find a decent C# example, so I’m writing one. I’ll show how to make use of custom editors to do away with those pesky public fields, and to constrain an input value using a slider.
UPDATE: The method below does not work in the latest releases of Unity 3.5, since Unity 3.5.5f3 or earlier. I have added an updated version that works for the current release of Unity 3.5, so I suggest referring to that after getting an overview from this post.
So let’s say I have a simple script that has a single customizable integral value:
// MyScript.cs
using UnityEngine;
public class MyScript : MonoBehaviour {
public int MyValue;
void Update () {
// MyValue should be between 1 and 10 inclusive
MyValue = Mathf.Clamp(MyValue, 1, 10);
// do something with MyValue
}
}
The main thing to note here is that we’re directly exposing MyValue as a field. This is generally considered bad coding practice because it means we have no control over how the value is set. This means that any time we want to use the value, we must first make sure it is valid, which adds extra code to distract from the core behaviour we’re writing.
Update: Here I am using Mathf.Clamp(…) to force myValue between 1 and 10 inclusive, but keep in mind that this one of the simplest checks we’ll ever need, and we could easily have something more complex. Since it is used in update, the check will also be run dozens of times per second regardless whether MyValue has been changed.
![]() |
Setting an invalid value is easy with public fields. |
I’ll come back to the public field issue later in this post, but for now I’ll put it aside and show how to at least make sure a valid value is entered through Unity’s inspector. You can get an overview of what custom editors are all about using the link at the top of this page. Suffice to say they are a means of taking control of the value input for a script in Unity’s Inspector.
To add a custom editor, you must add a script in Assets/Editor. The location is important, this will not work if the script is in the wrong place.
// MyScriptEditor.cs
using UnityEditor;
[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor {
public override void OnInspectorGUI() {
MyScript myTarget = (MyScript) target;
myTarget.MyValue = EditorGUILayout.IntSlider(
"Val-you", myTarget.MyValue, 1, 10);
}
}
There are a few things to note here:
- We’re using UnityEditor rather than the usual UnityEngine, and the script is extending Editor, not MonoBehaviour.
- We tell Unity which script to use this for with the annotation [CustomEditor(typeof(MyScript))].
- We override the OnInspectorGUI() method to hook into the generation of the section in the inspector for our script.
- The script instance that is being edited is available as target. Due to C#’s strict typing, we must cast it to the appropriate type before calling any type-specific methods on it.
- I’ve used an intSlider to allow setting of MyValue to values only from 1 to 10. Check out the EditorGUILayout documentation for others.
- I’m using “Val-you” as the display name just to illustrate that you can use any name you want, although I would recommend sticking with the property name to avoid confusion down the track.
![]() |
A slider can constrain the input value to the desired range. |
Note: I had some issues when I initially added the editor script – an error message in the console claiming that the script didn’t exist. It turned out this was just because it hadn’t refreshed properly for the currently selected GameObject. All I had to do was select something else, then re-focus the selection, and the error went away. If you get errors but can’t figure out the cause, I would advise first saving the scene and re-starting Unity to make sure everything refreshes properly.
I won’t demonstrate getter and setter methods, because C# provides an even nicer solution: properties. Properties are essentially syntactic sugar that let us define and use getters and setters without as much extra typing.
// MyScript.cs
using UnityEngine;
public class MyScript : MonoBehaviour {
private int myValue;
public int MyValue {
get { return myValue; }
set {
// MyValue should be between 1 and 10
myValue = Mathf.Clamp(value, 1, 10);
}
}
void Update () {
// do something with MyValue
}
}
Happy coding 🙂
You can also shorten your code:
// MyValue should be between 1 and 10
if (value < 1) myValue = 1;
else if (value > 10) myValue = 10;
else myValue = value;
to a Clamp
myValue = Mathf.Clamp(value, 1, 10);
That’s an excellent point. I was intentionally leaving that part pretty basic to make it easy to understand, but on second thought Mathf.Clamp is probably easier to follow, not to mention being a better option in real life.
The last thing I want to do is have people pick up bad habits from my examples.
Great – thanks for a very nice and tidy example.
As I mentioned in your retired blog, I have a problem that a value that I set using the slider gets reset once I press play.
Today I also tried cutting and pasting your MyScriptEditor.cs and MyScript.cs, then I added MyScript.cs to a brand new gameobject, changed the intslider to 5 and as I press play the value gets set back to 1. I’m using Unity 3.5.5f3 at the moment.
Update: I’ve done some more research and I believe Unity changed the way this works in Unity 3.5. I suspect you will get the same result as me with properties reverting upon pressing play if you try it in 3.5?
Now I believe you have to use serialization and I’m struggling to get my head around how that really works.
This is what I came up with:
// MyScriptEditor.cs
using UnityEditor;
[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor {
SerializedObject myTarget;
SerializedProperty myProperty;
void OnEnable() {
myTarget = new SerializedObject(target);
myProperty = myTarget.FindProperty("MyValue");
}
public override void OnInspectorGUI() {
myTarget.Update();
myProperty.intValue = EditorGUILayout.IntSlider("My Value", myProperty.intValue, 1, 10);
myTarget.ApplyModifiedProperties();
}
}
// MyScript.cs
using UnityEngine;
public class MyScript : MonoBehaviour {
// You can only expose the public int for serialization to work
// If you try to use the get/set below Unity will throw an error that
// there is a null reference to the object. Unfortunately we lose
// the ability to perform validation which is very frustrating.
public int MyValue;
//private int myValue;
//public int MyValue {
// get { return myValue; }
// set { myValue = Mathf.Clamp(value, 1, 10); }
// }
void Update () {
// do something with MyValue
}
}
I’ve looked into this and got an almost-satisfactory solution (in Unity 3.5.7f6). I’m switching to Unity 4 this weekend so it will have to suffice.
I’ll write up a new post for it shortly (I may need sleep before I finish it) and link it from the top of this one. The gist is that you can cover most bases by keeping the private field and public property, and manually marking the private field to be serialized. The property is there to ensure the value is correct when set from any other script, and we have to count on the input field in the custom editor to properly constrain values set through that. This still leaves one hole: invalid values can be deserialized, so if you decided to reduce your range of valid values, you could get some values that are out of the new range.
There are a few options for ensuring you never get an invalid value from a serialized object, but it starts to get a bit convoluted and I’d say it isn’t worth it in most cases: a slower (as in execution speed) but complete solution would be to put the Mathf.Clamp(…) call in the getter, then make sure you use the property rather than the private field in all your code. The down-side here is that you’re clamping the value every time it is accessed. You could add a non-serialized private boolean that’s set to true in the constructor, and use that to just clamp the value the first time it is accessed if it hasn’t been set since deserialization (the constructor is called before deserialization so the boolean will revert to true every time it goes through a serialize-deserialize cycle there’s no serialized boolean to overwrite the value of true).
Giving up validation is definitely not desirable. I’ll update to the latest editor and see what I can work out this week.
Hi,
I tried the above example and unity threw some errors.
The type or namespace name `MyEditor’ could not be found. Are you missing a using directive or an assembly reference?
I didn’t include the name `MyEditor` anywhere in my post, so I would have to see the code you are working with to give much insight. My suggestion would be to make sure you have `using MyEditor` at the start of the script where you are trying to use it, and make sure that the script files are in the appropriate location.