"Editor" folder isn't really necessary

Not sure if this is common knowledge, but I haven’t seen this used much:
I recently figured out that you don’t really need an Editor folder most of the time.

I mostly don’t like the Editor folder requirement because, while encapsulation is a good thing, splitting things like classes and custom editors across multiple folders, especially for smaller classes, gave me headaches. But you can’t get around it, putting anything that uses the UnityEditor class inside something that isn’t inside an “Editor” folder will fail to build because the UnityEditor class is not available for that.

So what I do is using “#if UNITY_EDITOR” to enable the Editor code parts only for Editor. Here is a small template. It is a Monobehaviour-script that includes a special class, a custom Inspector and a custom property drawer for that special class, all in one:

script

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
#endif

//
    public    class TestMonobehavior : MonoBehaviour{
     
        public    Color        someColor;
        public    GameObject    somePrefab;
        public    float        someFloat;


        public SpecialClass someSpecialClass;

        public void DoSomerequiredRecalculations(){ someFloat = someColor.r; }
    }

    [System.Serializable]//-> makes class inspector assignable
    public    class SpecialClass{
        public    Object        aUnityObject;
        public    float        aFloat;
    }







//CUSTOM INSPECTOR and/or CUSTOM PROPERTY DRAWER
//this would normally be in another script inside the Editor folder
    #if UNITY_EDITOR

    [CustomEditor(typeof(TestMonobehavior), true)]
    public class TestMonobehavior_Drawer : Editor
    {
        public override void OnInspectorGUI(){


            GUILayout.Label( "Default Inspector of this Component:");
            DrawDefaultInspector(); 

            TestMonobehavior myObject    = target as TestMonobehavior;
         

            GUILayout.Space( 10F );
            GUILayout.Label( "Custom Editor starts here:");
         
            EditorGUI.BeginChangeCheck ();
             

                    GUILayout.Space(1);
                    GUILayout.BeginHorizontal();
             
                        //just demonstration

                        //Background
                        GUILayout.Label( "-", GUILayout.Width(10F));
                        Rect rect0 = GUILayoutUtility.GetLastRect();
                             rect0 = new Rect(rect0.x, rect0.y-1, EditorGUIUtility.currentViewWidth-rect0.x-4, rect0.height+3 );
                        GUI.DrawTexture(rect0 , EditorGUIUtility.whiteTexture, ScaleMode.StretchToFill, false, 0F, Color.cyan, 0F, 3F );
             
                        GUI.contentColor = Color.black;
                        //percentage display
                        GUILayout.Label( (myObject.someFloat/100).ToString("00.0000;-00.0000")+"%", GUILayout.Width(65F) );
                        GUI.contentColor = Color.white;
             
                        //saved data
                        myObject.someColor    = EditorGUILayout.ColorField    ( myObject.someColor, GUILayout.Width(30F)    );
                        myObject.somePrefab    = EditorGUILayout.ObjectField    ( myObject.somePrefab, typeof(GameObject), false) as GameObject;
                        myObject.someFloat    = EditorGUILayout.FloatField    ( myObject.someFloat, GUILayout.Width(30F)    );

                        //myObject.someSpecialClass    = EditorGUILayout.ObjectField (myObject.someSpecialClass, typeof(SpecialClass), false );//havent figured out this one : )

                    GUILayout.EndHorizontal();
                    GUILayout.Space(1);
             
            if (EditorGUI.EndChangeCheck()) {
                myObject.DoSomerequiredRecalculations();
                EditorUtility.SetDirty( target );
            }

        }
    }


    [CustomPropertyDrawer(typeof(SpecialClass))]
    public class SpecialClass_Drawer : PropertyDrawer
    {
 
        public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) { return 18F*2F+4; }
 
        // Draw the property inside the given rect
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            EditorGUI.BeginProperty(position, label, property);         
            position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
            var indent = EditorGUI.indentLevel;
            EditorGUI.indentLevel = 0;

            EditorGUI.LabelField(    new Rect(position.x +position.width *0.00F+00F, position.y +00, position.width *1.00F, 18), "This is the custom property drawer for SpecialClass:");
            GUI.contentColor = Color.cyan;
            GUI.backgroundColor = Color.black;
            //members
            EditorGUI.PropertyField(new Rect(position.x +position.width *0.00F+00F, position.y +20, position.width *0.60F-04F, 18), property.FindPropertyRelative("aUnityObject"), GUIContent.none);
            EditorGUI.PropertyField(new Rect(position.x +position.width *0.60F+00F, position.y +20, position.width *0.40F-00F, 18), property.FindPropertyRelative("aFloat"), GUIContent.none);
         
            EditorGUI.indentLevel = indent;
            EditorGUI.EndProperty();
        }
    }
    #endif
//

7397018--903476--upload_2021-8-7_11-37-7.png

Is there a good reason to not do this? Are there other things that require the Editor folder regardless of this technique?

Compiler directives for this purpose are great, especially to keep things nice and tidy in one file.

I suspect the purpose of the Editor folder is for when you have entire subsystems that need the UnityEditor space, especially with multiple classes that all interoperate.

1 Like

I’ve started adopting a similar approach as well for smaller things.

For example, I have a component that draws some gizmos in the scene to help visualize its data, and I wanted to be able to toggle on/off which type of data is drawn, as well as be able to change the color of the gizmos for convenience sake.
So I just created a struct wrapped around an #if UNITY_EDITOR directive to contain all the custom gizmos settings for that script.

To keep things organized, what I like to do is create a separate namespace that just appends the word “Editor” to the script’s namespace, like so:

namespace Some.Namespace.For.Component
{
  public class MyComponent : MonoBehaviour
  {
#if UNITY_EDITOR
    [SerializeField] private Editor.MyComponentGizmoSettings gizmoSettings;
#endif
  }
}
#if UNITY_EDITOR

namespace Some.Namespace.For.Component.Editor
{
  public struct MyComponentGizmoSettings
  {
    //etc...
  }
}

#endif
1 Like

I’ve been doing this a lot too, especially when I found that classes outside of scripts in an editor folder can’t access said editor-foldered scripts. Also when you use addons like Odin Inspector where you really start to blend your run-time and editor code together in the same script, then you really start to get in the habit of using these compiler directives.

The fun just starts when you go to build and find all the places where you forgot to slide them in.

1 Like

I have editor windows and custom inspectors for a tool I’ve made, and having them in the editor folder, regardless of being nested in or not, neatly sets them aside, preventing any bleed-over into runtime scripts.

In my case, I am also intending to make my scripts into DLLs, which requires my runtime scripts to be its own DLL, while the editor scripts to be their own DLL. IIRC your method does not allow that separation to occur.

1 Like