8 comments on “Custom Editors in Unity – C# Example

  1. 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.

  2. 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.

  3. 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;

    public class MyScriptEditor : Editor {

    SerializedObject myTarget;
    SerializedProperty myProperty;

    void OnEnable() {
    myTarget = new SerializedObject(target);
    myProperty = myTarget.FindProperty("MyValue");

    public override void OnInspectorGUI() {
    myProperty.intValue = EditorGUILayout.IntSlider("My Value", myProperty.intValue, 1, 10);

    // 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).

  4. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *