Custom Unity Inspectors and Conditional Compilation

Custom inspectors in Unity are very handy to show and edit data on your behaviors and scriptable objects. To create custom inspectors in Unity you need to create a new class and extend Editor from the UnityEditor namespace. To tell Unity for which class the inspector is for, use the CustomEditor attribute.

A very simple custom Inspector for a class called TestBehaviour can look like this:

[CustomEditor(typeof(TestBehaviour))]
public class TestBehaviourInspector : Editor
{
    public override void OnInspectorGUI()
    {
        TestBehaviour myTarget = (TestBehaviour)target;
        myTarget.Variable1 = EditorGUILayout.IntField("Level", Mathf.RoundToInt(myTarget.Variable1/2f)*2);        
        EditorGUILayout.LabelField("Level", myTarget.Variable1.ToString());
    }
}

Sometimes it is useful to create custom inspectors even for built-in components like the Transform. For example for a 2D game we might exclude the 3D rotation options and the z axis for position and scale, to make it easier to use by artists and content creators.

The DrawDefaultInspector() method will draw all the default inspector editors for your target. Using this for the Transform component will look like this.snap

You can see that it looks quite different from the standard transform inspector which shows the Euler angles instead of the quaternions (the internal representation of rotation).

To create a simplified 2D inspector we can show just X and Y for position and use a single slider for the Z axis rotation in Euler angles. I also added a button for rounding the position to integer values.

[CustomEditor(typeof(Transform))]
public class TransformInspector : Editor
{
    public override void OnInspectorGUI()
    {
        Transform myTarget = (Transform)target;


        myTarget.position = EditorGUILayout.Vector2Field("Position", myTarget.position);
        myTarget.rotation = Quaternion.Euler(0, 0, 
            EditorGUILayout.Slider("Rotation", myTarget.localRotation.eulerAngles.z, 0, 365)
            );

        if (GUILayout.Button("Round Position"))
        {
            myTarget.position = new Vector3(
                    Mathf.Round(myTarget.position.x),
                    Mathf.Round(myTarget.position.y),
                    Mathf.Round(myTarget.position.z)
                );
        }
    } 
}

This will look like this:

snap2

It might be useful to go back to the standard inspector sometimes. That’s when conditional compilation comes in.

We can use #if FlagName in our code to tell the compiler to only compile the following code if the Flag is set. There are some flags automatically provided by Unity like the UNITY_EDITOR flag which can be used to only include code for use within the editor. We can set our own flags in Player Settings under Configuration/Scripting Define Symbols. More conveniently we can set these flags from code.

using System.Collections.Generic;
using System.Linq;
using UnityEditor;

public static class TestMenuItem
{
    public const string Flag = "USE_CUSTOM_INSPECTORS";

#if !USE_CUSTOM_INSPECTORS    
    [MenuItem("MyTools/Custom Inspectors On")]
    public static void CustomInspectorsOn()
    {
        RefreshCompilationFlag(true);
    }
#else    
    [MenuItem("MyTools/Custom Inspectors Off")]
    public static void CustomInspectorsOff()
    {
        RefreshCompilationFlag(false);
    }
#endif
    
    /**
     * Edits the custom symbols defined in Project settings. Triggers a recompile.
     */
    private static void RefreshCompilationFlag(bool value)
    {
        string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
        
        List<string> allDefines = definesString.Split(';').ToList();
            
        if (allDefines.Contains(Flag) && !value)
        {
            allDefines.Remove(Flag);
        }
        else if (!allDefines.Contains(Flag) && value)
        {
            allDefines.Add(Flag);
        }
           
        PlayerSettings.SetScriptingDefineSymbolsForGroup(
            EditorUserBuildSettings.selectedBuildTargetGroup,
            string.Join(";", allDefines.ToArray()));
    }
}

What this code does it creates a custom menu items that allow us to turn this specific compile flag On and Off. What’s neat is that we use the same flag to only show the relevant one (only showing On when the flag is Off and vice versa).

png.txt

This blog post is the first part of a series about Unity editor scripting based on a lecture I gave to gamedev students at gamedev.cuni.cz .

The source code along with other resources can be found at github.com/randalfien/unity-editor-scripting-tips

 

One thought on “Custom Unity Inspectors and Conditional Compilation

Leave a comment