Domi
Domi

Reputation: 24548

Shortcut to automatically add custom editor for Script in Unity

The Unity context menu for Assets has all kinds of useful shortcuts, but there is no shortcut to quickly create a custom editor. Instead, if I want to create a custom editor, I have to go through all these annoying boilerplate steps:

  1. Create Editor directory (if not created yet), and go to the right sub-directory
  2. Create -> C# Script (which should actually be called C# Component Script)
  3. Remove all the MonoBehavior stuff
  4. Append Editor to the class name
  5. Add using UnityEditor
  6. Add the CustomEditor attribute
  7. Inherit from Editor

...before being able to actually start writing my stuff...

Is there a shortcut for that somewhere?

Upvotes: 3

Views: 2239

Answers (1)

Domi
Domi

Reputation: 24548

New "Add Custom Editor" MenuItem

I wrote this little script which adds a new Add Custom Editor MenuItem to the Assets context menu:

new MenuItem

Usage

  1. Right-click a Component script in the Scripts folder.
  2. Select Add Custom Editor.
  3. The component now has an Editor which gets automatically selected, so you can modify it as you like. It has the same name (with Editor appended) and is at the same relative path, but in the Scripts/Editor folder

The screenshots below show an example: Given script Scripts/Test/TestScript, it creates a new editor at Scripts/Editor/Test/TestScriptEditor.

NOTE: The MenuItem will be disabled unless you have selected a Script that:

  1. Has a .cs file ending
  2. Is located somewhere under the Scripts folder (can be nested in any sub-directory)
  3. Is not in the Editor folder
  4. Does not have an editor yet

after using it

Setup

  1. Create -> C# Script
  2. Call it AddCustomEditorMenuItem
  3. Replace its contents with the code from this gist
  4. Done!
  5. Test it: Right-click a script file in the Scripts directory.

Code Highlights

  • Figuring out all the paths took most of the time:
scriptName = scriptAsset.name;

// get system file path
scriptPath = Path.GetFullPath(ProjectRoot + AssetDatabase.GetAssetPath (scriptAsset));

// get file name of the editor file
editorFileName = GetEditorFileNameFor (scriptName);

// split the script path
var results = scriptPathRegex.Matches (scriptPath).GetEnumerator ();
results.MoveNext ();
var match = (Match)results.Current;
scriptsPath = match.Groups [1].Value;
scriptRelativePath = match.Groups [2].Value;

// re-combine editor path
editorPath = Path.Combine (scriptsPath, "Editor");
editorPath = Path.Combine (editorPath, scriptRelativePath);
editorPath = Path.Combine (editorPath, editorFileName);

// nicely formatted file path
editorPath = Path.GetFullPath(editorPath);
editorRelativeAssetPath = editorPath.Substring(ProjectRoot.Length);
  • Once, paths are figured out, actually writing the file is nice and easy!
public void WriteCustomEditorFile ()
{
  // create all missing directories in the hierarchy
  Directory.CreateDirectory (Path.GetDirectoryName (editorPath));

  // write file
  File.WriteAllText (editorPath, BuildCustomEditorCode(scriptName));

  // let Asset DB pick up the new file
  AssetDatabase.Refresh();

  // highlight in GUI
  var os = AssetDatabase.LoadAllAssetsAtPath(editorRelativeAssetPath);
  EditorGUIUtility.PingObject (os[0]);

  // log
  Debug.Log("Created new custom Editor at: " + editorRelativeAssetPath);
}

// ...

/// <summary>
/// The menu item entry
/// </summary>
[MenuItem ("Assets/Add Custom Editor %#e", false, 0)]
public static void AddCustomEditor ()
{
  var scriptAsset = Selection.activeObject;

  // figure out paths
  var scriptPathInfo = new ScriptPathInfo (scriptAsset);

  // write file
  scriptPathInfo.WriteCustomEditorFile ();
}
  • If you don't like the default contents of a newly created editor, feel free to edit this part:
static string BuildCustomEditorCode (string name)
{
  return @"using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(" + name + @"))]
public class " + name + @"Editor : Editor {

public override void OnInspectorGUI ()
{
  base.OnInspectorGUI ();
  var obj = (" + name + @") target;
  if (GUILayout.Button (""Hi!"")) {
    // do something with obj when button is clicked
    Debug.Log(""Button pressed for: "" + obj.name);
    EditorGUIUtility.PingObject (obj);
  }
}
}";
}
  • If your menu item is always grayed out, consider first checking out my explanations above, before debugging the code that determines if a valid script is selected:
[MenuItem ("Assets/Add Custom Editor %#e", true, 0)]
public static bool ValidateAddCustomEditor ()
{
  var scriptAsset = Selection.activeObject;

  if (scriptAsset == null) {
    // nothing selected? (should probably not happen)
    return false;
  }

  var path = ProjectRoot + AssetDatabase.GetAssetPath (scriptAsset);

  if (!scriptPathRegex.IsMatch (path)) {
    // not a Script in the Script folder
    return false;
  }

  if (editorScriptPathRegex.IsMatch (path)) {
    // we are not interested in Editor scripts
    return false;
  }
    

  if (Directory.Exists (path)) {
    // it's a directory, but we want a file
    return false;
  }

  var scriptPathInfo = new ScriptPathInfo (scriptAsset);

  //        Debug.Log (scriptPathInfo.scriptPath);
  //        Debug.Log (Path.GetFullPath(AssetsPath + "/../"));
  //        Debug.Log (scriptPathInfo.editorRelativeAssetPath);
  //        Debug.Log (scriptPathInfo.editorPath);

  if (File.Exists (scriptPathInfo.editorPath)) {
    // editor has already been created
    return false;
  }

  // all good!
  return true;
}

Upvotes: 5

Related Questions