Build Unity Editor Tools With AI: Custom Inspectors and Menu Items

Illustration: a holographic toolbox opening above a game editor inspector panel

Editor tools are where AI coding assistants shine in Unity. The code is small, self-contained, and you see straight away whether it works. A batch-rename menu item, a custom inspector with a “Spawn” button, or a scene validator takes an AI a minute to draft. It would take you an hour of digging through the docs. This guide shows how to build Unity editor tools with AI: the rules the AI must follow (Editor folders, Undo, SerializedObject), prompts that work, and four complete examples you can drop into a Unity 6 project.

Why Editor Tools Suit AI So Well

Gameplay code is tangled up with your scenes, prefabs and other systems. Editor tools usually aren’t. A tool that renames selected objects needs to know about Selection and Undo, not your whole game. That makes it easy to describe in one prompt and easy to test: click the menu item, look at the result, press Ctrl+Z.

The risk is different too. A buggy gameplay script breaks a level. A buggy editor tool can modify hundreds of assets in one click. That’s why the rules below matter, especially Undo support. If you’re new to editor scripting, our editor scripting 101 guide explains the basics this post builds on.

Rules to Give the AI Up Front

Paste these with your request. AI editor code most often fails on exactly these points:

Unity 6 editor tool rules:
- Put editor code in a folder named "Editor" (or an Editor-only
  assembly definition). Never use UnityEditor in runtime scripts.
- Every change to scene objects must support Undo:
  Undo.RecordObject(s) before modifying, Undo.RegisterCreatedObjectUndo
  for new objects, Undo.DestroyObjectImmediate for deleting.
- In custom inspectors, edit fields through serializedObject and
  SerializedProperty, with Update() and ApplyModifiedProperties().
- When changing assets directly (not via SerializedObject), call
  EditorUtility.SetDirty on them.
- Add a validation function for menu items that need a selection.
- Use FindObjectsByType, not FindObjectsOfType.
- Menu items go under "Tools/DevForge/".

The Editor folder rule is the one that bites later. Any script in a folder called Editor is compiled into the editor-only assembly and left out of builds. Put an editor script anywhere else and the project works fine in the Editor, then the build fails with “The type or namespace name ‘UnityEditor’ could not be found”. If your project uses assembly definitions, the editor scripts need their own asmdef with only the Editor platform ticked. Our assembly definitions guide shows that setup.

For general prompt habits (Unity version, packages, constraints), see our guide to prompting AI for Unity C# that compiles.

Prompt:

Following the rules above, write a static editor class with a menu item “Tools/DevForge/Number Selected Objects”. It renames the selected GameObjects to BaseName_01, BaseName_02 and so on, in Hierarchy order. Take the base name from the first object, stripping any trailing numbers, underscores, spaces or brackets. Support Undo as one step. Grey out the menu item when nothing is selected.

Result, saved as Assets/Editor/NumberSelectedObjects.cs:

using System;
using UnityEditor;
using UnityEngine;

public static class NumberSelectedObjects
{
    const string MenuPath = "Tools/DevForge/Number Selected Objects";

    [MenuItem(MenuPath)]
    static void Run()
    {
        GameObject[] selected = Selection.gameObjects;
        Array.Sort(selected, (a, b) =>
            a.transform.GetSiblingIndex().CompareTo(b.transform.GetSiblingIndex()));

        string baseName = selected[0].name.TrimEnd("0123456789_ ()".ToCharArray());
        if (baseName.Length == 0) baseName = "Object";

        Undo.RecordObjects(selected, "Number Selected Objects");
        for (int i = 0; i < selected.Length; i++)
            selected[i].name = $"{baseName}_{i + 1:00}";
    }

    [MenuItem(MenuPath, true)]
    static bool Validate() => Selection.gameObjects.Length > 0;
}

Two details to check in any AI version of this. First, Undo.RecordObjects is called before the names change. Recording after does nothing useful. Second, the validation method uses the same menu path with true as the second argument, which is how Unity greys out the item. Sorting by sibling index is a simple approach that works when the objects share a parent.

Custom Inspector With a Button

A runtime component, in a normal scripts folder:

using UnityEngine;

public class CircleSpawner : MonoBehaviour
{
    public GameObject prefab;
    [Min(1)] public int count = 8;
    [Min(0f)] public float radius = 3f;
}

Prompt:

Write a custom inspector for CircleSpawner. Draw the three fields with SerializedProperty. Show a warning HelpBox when prefab is empty. Add a “Spawn In Circle” button, disabled while prefab is empty, that instantiates count prefab instances as children, evenly spaced on a circle of radius around the spawner, keeping the prefab link. Full Undo support.

Result, in Assets/Editor/CircleSpawnerEditor.cs:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(CircleSpawner))]
public class CircleSpawnerEditor : Editor
{
    SerializedProperty prefabProp, countProp, radiusProp;

    void OnEnable()
    {
        prefabProp = serializedObject.FindProperty("prefab");
        countProp  = serializedObject.FindProperty("count");
        radiusProp = serializedObject.FindProperty("radius");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(prefabProp);
        EditorGUILayout.PropertyField(countProp);
        EditorGUILayout.PropertyField(radiusProp);
        serializedObject.ApplyModifiedProperties();

        bool noPrefab = prefabProp.objectReferenceValue == null;
        if (noPrefab)
            EditorGUILayout.HelpBox("Assign a prefab to spawn.", MessageType.Warning);

        using (new EditorGUI.DisabledScope(noPrefab))
        {
            if (GUILayout.Button("Spawn In Circle"))
                Spawn((CircleSpawner)target);
        }
    }

    static void Spawn(CircleSpawner spawner)
    {
        for (int i = 0; i < spawner.count; i++)
        {
            var instance = PrefabUtility.InstantiatePrefab(spawner.prefab, spawner.transform) as GameObject;
            if (instance == null)
            {
                Debug.LogWarning("Prefab field must reference a prefab asset, not a scene object.", spawner);
                return;
            }

            float angle = i * Mathf.PI * 2f / spawner.count;
            Vector3 offset = new Vector3(Mathf.Cos(angle), 0f, Mathf.Sin(angle)) * spawner.radius;
            instance.transform.position = spawner.transform.position + offset;
            Undo.RegisterCreatedObjectUndo(instance, "Spawn In Circle");
        }
    }
}

This is the pattern to insist on. Fields go through serializedObject, so multi-object editing, prefab overrides and Undo for the fields work for free. PrefabUtility.InstantiatePrefab keeps the prefab link, unlike Object.Instantiate, which creates unlinked copies. A common AI shortcut is to draw fields with EditorGUILayout.ObjectField and assign spawner.prefab directly. That works until you need Undo or prefab overrides, then quietly doesn’t save. Before asking for a custom inspector at all, check whether attributes like [Min], [Range] or [Header] already do what you need.

Validator: Find Missing Scripts

Validators are a great first AI tool because they only read. Prompt: “Menu item that logs every GameObject in the open scenes, including inactive ones, that has missing script components, with a clickable log entry for each.”

using UnityEditor;
using UnityEngine;

public static class MissingScriptFinder
{
    [MenuItem("Tools/DevForge/Find Missing Scripts In Scene")]
    static void Find()
    {
        int total = 0;
        GameObject[] all = Object.FindObjectsByType<GameObject>(
            FindObjectsInactive.Include, FindObjectsSortMode.None);

        foreach (GameObject go in all)
        {
            int missing = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(go);
            if (missing == 0) continue;
            total += missing;
            Debug.LogWarning($"{go.name}: {missing} missing script(s)", go);
        }

        Debug.Log($"Missing script scan finished. {total} found.");
    }
}

Passing go as the log context means clicking a warning selects the object. Once found, GameObjectUtility.RemoveMonoBehavioursWithMissingScript can strip them, but it’s safer to understand why they went missing first. Our missing script references guide explains the causes.

🧰 Before you build it, check if it exists

Some editor tools are worth writing yourself. Others are solved, polished and cheap on the Asset Store. Our list covers the tools that save indie devs the most time.

See the best Unity Asset Store tools →

A Simple EditorWindow

When a tool needs settings or a persistent panel, ask for an EditorWindow. A minimal one:

using UnityEditor;
using UnityEngine;

public class SceneStatsWindow : EditorWindow
{
    [MenuItem("Tools/DevForge/Scene Stats")]
    static void Open() => GetWindow<SceneStatsWindow>("Scene Stats");

    void OnGUI()
    {
        if (GUILayout.Button("Refresh")) Repaint();

        var renderers = Object.FindObjectsByType<MeshRenderer>(FindObjectsSortMode.None);
        var lights = Object.FindObjectsByType<Light>(FindObjectsSortMode.None);
        EditorGUILayout.LabelField("Mesh Renderers", renderers.Length.ToString());
        EditorGUILayout.LabelField("Lights", lights.Length.ToString());
    }
}

This uses IMGUI (OnGUI), which AI writes most reliably because there’s so much of it in training data. You can also ask for a UI Toolkit version that builds its UI in CreateGUI. Say which one you want, or you’ll get a mix. In a real tool, cache the counts and refresh on a button or on EditorApplication.hierarchyChanged rather than searching every repaint.

Reviewing AI Editor Code

Before running any AI editor tool on a real project, commit your work. Then check:

  • Location: is the file in an Editor folder or editor-only assembly?
  • Undo: is every modification recorded before it happens? Run the tool, then Ctrl+Z. Everything should revert.
  • Assets: if it changes assets, does it call EditorUtility.SetDirty and AssetDatabase.SaveAssets? Without that, changes vanish on restart.
  • Scope: does it act only on the selection or open scene, or does it loop over every asset in the project? Project-wide loops should ask for confirmation with EditorUtility.DisplayDialog.
  • Deletes: any DestroyImmediate on assets is a red flag. Use Undo.DestroyObjectImmediate for scene objects, and never delete assets without a confirmation dialog.

Test on a copy of a scene first. A tool that worked on three objects can surprise you on three hundred.

Troubleshooting

error CS0246: The type or namespace name ‘UnityEditor’ could not be found (only when building)

An editor script is outside an Editor folder, or in a runtime assembly definition. Move it, or wrap editor-only code in #if UNITY_EDITOR.

My menu item doesn’t appear

The project has compile errors, the method isn’t static, or the path has no slash (Unity needs at least one submenu level, like Tools/My Tool). Fix the Console first.

Changes from my custom inspector don’t save

The code assigns fields directly on target without Undo or SetDirty. Switch to SerializedProperty with ApplyModifiedProperties().

Ctrl+Z doesn’t undo what the tool did

Undo was recorded after the change, or not at all. Call Undo.RecordObject before modifying, and Undo.RegisterCreatedObjectUndo after creating.

Spawned objects aren’t linked to the prefab

The code uses Instantiate. Use PrefabUtility.InstantiatePrefab and make sure the field references a prefab asset.

FAQ

Do editor scripts go into my build?

No, as long as they’re in an Editor folder or an editor-only assembly. That’s exactly why the folder rule matters.

Should I use IMGUI or UI Toolkit for editor tools?

Either works. IMGUI is quicker for small tools and AI writes it reliably. UI Toolkit is better for larger windows with styling.

Can AI build editor tools through MCP instead?

Yes. With an MCP connection, an agent can run editor actions directly. Writing a reusable tool script is still better for anything you’ll do more than once.

Why use SerializedObject instead of setting fields directly?

It gives you Undo, multi-object editing and prefab overrides automatically, and marks the object dirty so changes save.

Is it safe to let AI write tools that modify assets?

Yes, if you commit first, review the code for Undo and SetDirty, and test on a small selection before a big one.

Comments

Leave a Reply

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