Unity Editor: Menu items
經常自建 Unity 選單 (Menu items) 來執行專案客製化的功能,紀錄在 Unity5 中如何建立選單以及各式自建選單的使用範例。
Menu Items
自定選單通常使用 UnityEditor.MenuItem 來建立:
using UnityEditor;
using UnityEngine;
public class Example {
[MenuItem ("Example/Do Something")]
static void DoSomething () {
Debug.Log ("Doing Something...");
}
}
由於引用 UnityEditor,為了能夠建置專案成功,通常會將 Example.cs 放置於 Editor 資料夾或其子資料夾內,確保該 script 不會遊戲發佈時建置,進而導致 CS0246 錯誤:
error CS0246: The type or namespace name `UnityEditor' could not be found. Are you missing an assembly reference?
或者使用 #if UNITY_EDITOR
定義來修正建置時 CS0246 的錯誤:
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
public class Example {
[MenuItem ("Example/Do Something")]
static void DoSomething () {
Debug.Log ("Doing Something...");
}
}
#endif
Hotkeys (Keystrokes)
可自訂選單熱鍵,透過幾個符號的組合來達成:
- %,ctrl on Windows,cmd on macOS
- #, shift
- &,alt
- _,如果不使用以上修飾鍵
- #KEY,特殊鍵
- #LEFT,#RIGHT,#UP,#DOWN,方向左右上下按鍵
- #F1, #F2, ... , #F12
- #HOME, #END, #PGUP, #PGDN
// 按下 G
[MenuItem ("Example/Do1 _g")]
// 按下 Ctrl + G
[MenuItem ("Example/Do2 %g")]
// 按下 Shift + Ctrl + G
[MenuItem ("Example/Do3 #%g")]
// 按下 Shift + Ctrl + Alt + G
[MenuItem ("Example/Do4 #%&g")]
Validation
Menu item 可以加入驗證 (validate function),只有當條件判斷滿足時,使用者才可以操作點擊該選單:
// 只有當編輯器是 Play Mode 時,才可以操作 Example/Do Something
using UnityEditor;
public class Example
{
[MenuItem ("Example/Do Something")]
static void DoSomething () {
Debug.Log ("Doing Something...");
}
[MenuItem("Example/Do Something", true)]
static bool DoSomethingValidate()
{
return EditorApplication.isPlaying;
}
}
Item Priority
若沒有特別宣告順序優先度的 menuItem,通常會根據其函數宣告順序,擺在該選單的最底下。但可以自訂優先度,確保 menuItem 順序:
using UnityEditor;
using UnityEngine;
public class Example
{
[MenuItem("Example/Do Something", false, 10)]
static void DoSomething()
{
Debug.Log("Doing Something...");
}
[MenuItem("Example/Do Something2", false, 8)]
static void DoSomething2()
{
Debug.Log("Doing Something2...");
}
[MenuItem("Example/Do Something3", false, 5)]
static void DoSomething3()
{
Debug.Log("Doing Something3...");
}
}
注意 MenuItem 的宣告,可以使用其建構子設定所有參數,或者用 C# 5.0 新語法 (syntax) 設定其參數:
// 原本範例
[MenuItem("Example/Do Something", false, 10)]
// 可以改成 (validate 預設值為 false)
[MenuItem("Example/Do Something", priority = 10)]
// 或者是
[MenuItem("Example/Do Something", priority = 10, validate = false)]
其 MenuItem 定義:
namespace UnityEditor
{
public sealed class MenuItem : Attribute
{
public string menuItem;
public int priority;
public bool validate;
public MenuItem(string itemName);
public MenuItem(string itemName, bool isValidateFunction);
public MenuItem(string itemName, bool isValidateFunction, int priority);
}
}
Sepetatror
若要增加選單中增加分隔線 (sepetatror),如上圖所示,只要兩個 menu items 其優先度差超過 100 以上,這兩個 menu items 中間便自動產生分隔線,例如以下範例:
using UnityEditor;
using UnityEngine;
public class Example
{
[MenuItem("Example/Do Something", false, 0)]
static void DoSomething()
{
Debug.Log("Doing Something...");
}
[MenuItem("Example/Do Something2", false, 1)]
static void DoSomething2()
{
Debug.Log("Doing Something2...");
}
[MenuItem("Example/Do Something3", false, 300)]
static void DoSomething3()
{
Debug.Log("Doing Something3...");
}
}
Special Paths
-
Assets/,除了在上方功能表會出現以外,也能在 Project view 的功能選單 (context menu) 出現
using UnityEditor; public class Example { [MenuItem("Assets/Do Something")] static void DoSomething() { } }
-
Assets/Create/,建立自訂 assets,詳見 Create Assets。
-
CONTEXT/ComponentName,在 inspector 該組件編輯頁中,滑鼠右鍵的功能列表中新增選項,詳見 Context Menu。
-
GameObject/,這算是小花招,若想要在 Hierarchy view 中,滑鼠右鍵功能選單出現自訂選項的話,設定路徑為 GameObject 且 priority 在 11-49 之間,便可達到此功能,如以下例子:
using UnityEditor; public class Example { [MenuItem("GameObject/Toggle Active", priority = 11)] static void ToggleActive() { var go = Selection.activeGameObject; go.SetActive(!go.activeSelf); } [MenuItem("GameObject/Toggle Active", true)] static bool ToggleActiveValidate() { return Selection.activeGameObject != null; } }
Add Compoments
自定義 Component/,來建立特別組件的新增:
using UnityEngine;
public class Example : MonoBehaviour
{
#if UNITY_EDITOR
[UnityEditor.MenuItem("Component/Example")]
static void AddComponent()
{
UnityEditor.Selection.activeGameObject.AddComponent();
}
[UnityEditor.MenuItem("Component/Example", true)]
static bool AddComponentValidate()
{
return UnityEditor.Selection.activeGameObject != null;
}
#endif
}
或是使用已經定義好的 attribute 達到相同功能:
using UnityEngine;
[AddComponentMenu("Example")]
public class Example : MonoBehaviour
{
}
Create Assets
自定 Assets/Create/Somthing,來建立此專案定義的自訂型態的 assets。例如定義 Example scriptable object,並且設定 menu item 去建立 assets:
using System.IO;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class Example : ScriptableObject
{
#if UNITY_EDITOR
[MenuItem("Assets/Create/Example")]
static void DoSomething()
{
var asset = ScriptableObject.CreateInstance();
var path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (string.IsNullOrEmpty(path))
{
path = "Assets";
}
else if (Path.GetExtension(path) != "")
{
path = path.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), string.Empty);
}
var assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path + "/New " + asset.GetType().ToString() + ".asset");
AssetDatabase.CreateAsset(asset, assetPathAndName);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.FocusProjectWindow();
Selection.activeObject = asset;
}
#endif
}
或是直接使用 attribute 定義達到相同的功能:
using UnityEngine;
[CreateAssetMenu]
public partail class Example : ScriptableObject
{
}
Context Menu
在指定組件 (component) 功能選單 (context menu) 加入選項,讓使用者能在 inspector 中,在該組件編輯視窗中,直接按下滑鼠右鍵,能夠點選該選項操作:
using UnityEditor;
using UnityEngine;
public class Example
{
[MenuItem("CONTEXT/Transform/Set Identity")]
static void SetIdentity(MenuCommand command)
{
var transform = (command.context as Transform);
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
}
}
或是使用已經定義好的 attribute 對自建組件達到相同功能,必須是 non-static function,但此方式沒辦法為其他組件擴充:
using UnityEngine;
class Example : MonoBehaviour
{
[SerializeField]
int number;
[ContextMenu("Set Random Number")]
void SetRandomNumber()
{
this.number = Random.Range(int.MinValue, int.MaxValue);
}
}
亦或是針對欄位建立選單:
using UnityEngine;
public class Example : MonoBehaviour
{
[ContextMenuItem("Set infinity", "SetInfinity")]
[SerializeField]
float number;
void SetInfinity()
{
this.number = float.PositiveInfinity;
}
}
Checkbox
在選單上設定核取方框 (Check box),讓使用者知道目前是什麼設定,使用 Menu.SetChecked
設定,亦可用 Menu.GetChecked
來取得是否選取,例如自訂語言的例子:
using UnityEditor;
public enum Language
{
English,
TraditionalChinese,
Japanese,
}
public class LanguageMenuItems
{
const string MenuItemEnglish = "Lanuage/English";
const string MenuItemTraditionalChinese = "Lanuage/Traditional Chinese";
const string MenuItemJapanese = "Lanuage/Japanese";
public static Language Current
{
get;
set;
}
[MenuItem(MenuItemEnglish)]
static void SetEnglish()
{
Current = Language.English;
}
[MenuItem(MenuItemEnglish, true)]
static bool SetEnglishValidate()
{
Menu.SetChecked(MenuItemEnglish, Current == Language.English);
return true;
}
[MenuItem(MenuItemTraditionalChinese)]
static void SetTraditionalChinese()
{
Current = Language.TraditionalChinese;
}
[MenuItem(MenuItemTraditionalChinese, true)]
static bool SetTraditionalChineseValidate()
{
Menu.SetChecked(MenuItemTraditionalChinese, Current == Language.TraditionalChinese);
return true;
}
[MenuItem(MenuItemJapanese)]
static void SetJapanese()
{
Current = Language.Japanese;
}
[MenuItem(MenuItemJapanese, true)]
static bool SetJapaneseValidate()
{
Menu.SetChecked(MenuItemJapanese, Current == Language.Japanese);
return true;
}
}
Others
-
UnityEditor.EditorApplication.ExecuteMenuItem,執行指定 menu item 的功能,例如以下呼叫 menu item 來建立一個空 GameObject 的範例:
UnityEditor.EditorApplication.ExecuteMenuItem("GameObject/Create Empty");
-
UnityEditor.GenericMenu,客製化自己的選單,請參見 ScriptReference。
沒有留言: