在 Unity 中自动为脚本添加自定义编辑器的快捷方式
Shortcut to automatically add custom editor for Script in Unity
Assets 的 Unity 上下文菜单有各种有用的快捷方式,但没有快速创建自定义编辑器的快捷方式。相反,如果我想创建一个自定义编辑器,我必须完成所有这些烦人的样板步骤:
- 创建
Editor
目录(如果还没有创建),然后转到正确的子目录
Create
-> C# Script
(实际上应该叫C# Component Script
)
- 删除所有
MonoBehavior
内容
- 将
Editor
附加到 class 名称
- 添加
using UnityEditor
- 添加
CustomEditor
属性
- 继承自
Editor
...在能够真正开始写我的东西之前...
有没有快捷方式?
新的“添加自定义编辑器”菜单项
我将 this little script which adds a new Add Custom Editor
MenuItem 写入了 Assets
上下文菜单:
用法
- 右键单击
Scripts
文件夹中的 Component
脚本。
- Select
Add Custom Editor
.
- 该组件现在有一个
Editor
会被自动选中,因此您可以随意修改它。它具有相同的名称(附加了 Editor
)并且位于相同的相对路径中,但在 Scripts/Editor
文件夹中
下面的屏幕截图显示了一个示例:给定脚本 Scripts/Test/TestScript
,它在 Scripts/Editor/Test/TestScriptEditor
.
处创建了一个新编辑器
注意:菜单项将被禁用,除非您选择了一个脚本:
- 有一个
.cs
文件结尾
- 位于
Scripts
文件夹下的某处(可以嵌套在任何子目录中)
- 不在
Editor
文件夹中
- 还没有编辑器
设置
Create
-> C# Script
- 称之为
AddCustomEditorMenuItem
- 用 this gist
中的代码替换其内容
- 完成!
- 测试一下:右键单击
Scripts
目录中的脚本文件。
代码亮点
- 找出所有路径花费了大部分时间:
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);
- 弄清楚路径后,实际上编写文件非常简单!
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 ();
}
- 如果您不喜欢新创建的编辑器的默认内容,请随意编辑这部分:
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);
}
}
}";
}
- 如果您的菜单项总是变灰,请考虑先查看我上面的解释,然后再调试确定是否选择了有效脚本的代码:
[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;
}
Assets 的 Unity 上下文菜单有各种有用的快捷方式,但没有快速创建自定义编辑器的快捷方式。相反,如果我想创建一个自定义编辑器,我必须完成所有这些烦人的样板步骤:
- 创建
Editor
目录(如果还没有创建),然后转到正确的子目录 Create
->C# Script
(实际上应该叫C# Component Script
)- 删除所有
MonoBehavior
内容 - 将
Editor
附加到 class 名称 - 添加
using UnityEditor
- 添加
CustomEditor
属性 - 继承自
Editor
...在能够真正开始写我的东西之前...
有没有快捷方式?
新的“添加自定义编辑器”菜单项
我将 this little script which adds a new Add Custom Editor
MenuItem 写入了 Assets
上下文菜单:
用法
- 右键单击
Scripts
文件夹中的Component
脚本。 - Select
Add Custom Editor
. - 该组件现在有一个
Editor
会被自动选中,因此您可以随意修改它。它具有相同的名称(附加了Editor
)并且位于相同的相对路径中,但在Scripts/Editor
文件夹中
下面的屏幕截图显示了一个示例:给定脚本 Scripts/Test/TestScript
,它在 Scripts/Editor/Test/TestScriptEditor
.
注意:菜单项将被禁用,除非您选择了一个脚本:
- 有一个
.cs
文件结尾 - 位于
Scripts
文件夹下的某处(可以嵌套在任何子目录中) - 不在
Editor
文件夹中 - 还没有编辑器
设置
Create
->C# Script
- 称之为
AddCustomEditorMenuItem
- 用 this gist 中的代码替换其内容
- 完成!
- 测试一下:右键单击
Scripts
目录中的脚本文件。
代码亮点
- 找出所有路径花费了大部分时间:
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);
- 弄清楚路径后,实际上编写文件非常简单!
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 ();
}
- 如果您不喜欢新创建的编辑器的默认内容,请随意编辑这部分:
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);
}
}
}";
}
- 如果您的菜单项总是变灰,请考虑先查看我上面的解释,然后再调试确定是否选择了有效脚本的代码:
[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;
}