第三章——擴展Unity編輯器

目錄

擴展Project視圖中的右鍵視圖菜單

在project視圖中的Create按鈕中擴展資源創建菜單

擴展布局 (Project 視圖腳本 代碼的右側擴展自定義按鈕)

監聽事件 (判斷當移動一個文件時是否合法)

擴展Hierarch 視圖中的 Create 菜單選項

擴展布局(點擊Hierarch中的遊戲對象在右側擴展出一組按鈕 )

拓展 Inspector 視圖(擴展源生組件——攝像機)

 

拓展繼承組件(擴展Transform 組件)

組件不可以編輯 (禁掉某個組件,使它無法編輯)

          Context 菜單

 



擴展Project視圖中的右鍵視圖菜單


 

● 編譯器使用的代碼應該僅限於編輯模式下, 也就是說正式的遊戲包不應該包含這些代碼。

●  Unity的C#腳本分爲兩種,一種是給遊戲本身運行的,一種是個編輯器運行的;其中給編輯器運行的腳本必須在Editor文件夾下,如果屬於運行時執行的代碼,放在任意非 Editor 文件夾下即可。

● 可以把腳本資源放在一個命名爲scripts的文件夾下。

● 如何創建My Tools 菜單集合:

首先先跟上述那樣操作,創建一個Scripts文件夾,然後子文件夾Editor,然後鼠標右鍵Editor 文件夾點擊: Create-------->C# scripts. 這樣。

然後把下列代碼打進去:

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

public class Script_03_01
{
    [MenuItem("Assets/My Tools/Tools 1", false, 2)]
    static void MyTools1()
    {
        Debug.Log(Selection.activeObject.name);
    }
    [MenuItem("Assets/My Tools/Tools 2", false, 1)]
    static void MyTools2()
    {
        Debug.Log(Selection.activeObject.name);
    }
}

 

     然後切換到Unity3D,後發現右下角,有一個在轉圈的東西,這是Unity再編譯你剛剛改的代碼;帶起轉圈完畢後,就說明編譯好了。

  然後在project視圖中的腳本代碼上鼠標右鍵 或者 主菜單欄上的Assets裏就能看見我們在Unity 中擴展的功能。

我們點擊其中一個 Tools, 就會在 Console 中

輸出相應的日誌了、


● 說明: 自定義菜單的參數需要在MenuItem 方法中寫入顯示的菜單路徑。 如果菜單條比較多,可以在第三個參數處輸入表示排序的整數,數值越小,它的排序就越靠越靠前。 最後使用 Debug.Log 打印選擇的遊戲對象 Selection.activeObject.name 即可。


在project視圖中的Create按鈕中擴展資源創建菜單


 

● 跟上面方法一樣,貼代碼:

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

public class Script_03_02
{
    [MenuItem("Assets/Create/My Create/Cube", false, 2)]
    static void CreateCube()
    {
        GameObject.CreatePrimitive(PrimitiveType.Cube); //創建立方體
    }
    [MenuItem("Assets/Create/My Create/Sphere", false, 1)]
    static void CreateSphere()
    {
        GameObject.CreatePrimitive(PrimitiveType.Sphere);//創建球體
    }
}

● 擴展菜單的關鍵就是找到正確的菜單路徑,通過 “/ ” 符號將它們拼合而成, 

 GameObject.CreatePrimitive (PrimitiveType. Type) 方法用於創建 Unity 基礎原始遊戲對象;

 參數Type表示要創建的原始對象的類型。

最後成功擴展了 My Create 菜單:



using UnityEngine;

public class Example : MonoBehaviour
{
    //在場景中創建一個平面,球體,立方體,膠囊,圓柱體

    void Start()
    {
        GameObject plane  = GameObject.CreatePrimitive(PrimitiveType.Plane);

        GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube.transform.position = new Vector3(0, 0.5f, 0);

        GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        sphere.transform.position = new Vector3(0, 1.5f, 0);

        GameObject capsule = GameObject.CreatePrimitive(PrimitiveType.Capsule);
        capsule.transform.position = new Vector3(2, 1, 0);

        GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
        cylinder.transform.position = new Vector3(-2, 1, 0);
    }
}

擴展布局 (Project 視圖腳本 代碼的右側擴展自定義按鈕


●  跟上面描述的方法一樣, 先寫 C# Script, 代碼:

using UnityEngine;
using UnityEditor;

public class Script_03_03
{
    [InitializeOnLoadMethod]

    static void InitializeOnLoadMethod()
    {
        EditorApplication.projectWindowItemOnGUI = delegate (string guid, Rect selectionRect)
        {
            //在Project視圖中選擇一個資源
            if (Selection.activeObject &&
                guid == AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject)))
            {
                //設置拓展按鈕區域
                float width = 50f;
                selectionRect.x += (selectionRect.width - width);
                selectionRect.y += 2f;
                selectionRect.width = width;
                GUI.color = Color.red;
                //點擊事件
                if (GUI.Button(selectionRect, "click"))
                {
                    Debug.LogFormat("click : {0}", Selection.activeObject.name);//使用 Debug.Log 打印選擇的遊戲對象 Selection.activeObject.name 即可。
                }
                GUI.color = Color.white;
            }
        }; //這裏要添加分號
    }
}

● 這樣就可以在Project 視圖 代碼的右側擴展自定義按鈕 在代碼中既可以設置按鈕的區域,也可以監聽按鈕的事件。

可以看到圖中的一個資源的上面有一個擴展後的按鈕, 點擊這個按鈕, 程序會自動在 Console  窗口中打印選中的資源名。看下圖,點擊後,Console 打印的信息:


● EditorApplication.projectWindowItemOnGUI :爲ProjectWindow中的每個可見列表項委託GUI事件

●  Delegate至少0個參數,至多32個參數,可以無返回值,也可以指定返回值類型。

● AssetDatabase.AssetPathToGUID(string path) :  參數path,是資產的文件系統路徑。

GUI.Button:做一個按鈕。用戶單擊它們就會立即發生一些事情。Button(Rect position, string text):第一個參數是:屏幕上用於按鈕的矩形; 第二個參數是:文本顯示在按鈕上。

● 注意: 在方法前 添加 “ [InitializeOnLoadMethod] ” 表示此方法會在 C# 代碼每次編譯完成後首先調用。 監聽  “   EditorApplication.projectWindowItemOnGUI ”  的委託, 即可使用 GUI 的方法繪製自定義的 UI 元素。

●InitializeOnLoadMethod:允許在Unity加載時初始化編輯器類方法,而無需用戶的操作。


監聽事件 (判斷當移動一個文件時是否合法)


●  我們可以通過監聽資源的創建、刪除、移動、和保存事件 來創建程序,然後約束資源, 這樣資源就不會很凌亂。

● 例如,將某個文件移動到錯誤的目錄下,此時就可以監聽資源移動事件,程序判斷資源的原始位置以及將要
移動的位置是否合法,從而決定是否能阻止本次移動。 Unity 提供了監聽的基類

如下代碼所示:需要繼承UnityEditor.AssetModificationProcessor,接着重寫監聽資源創建、刪除、移動和保存的方法,處理自己的特殊邏輯。


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


public class Script_03_04 : UnityEditor.AssetModificationProcessor
{
   [InitializeOnLoadMethod]

    static void InitializeOnLoadMethod()
    {

        全局監聽Project視圖下的資源是否發生變化(添加、刪除、移動)
        EditorApplication.projectChanged += delegate () 
        {
            Debug.Log("change");
        };
    }

    監聽雙擊鼠標左鍵打開資源事件
    public static bool IsOpenForEdit(string assetPath, out string message)
    {
        message = null;
        Debug.LogFormat("assetPath : {0} ", assetPath);

        true表示該資源可以打開,false表示不允許在unity中打開該資源
        return false;
    }


    監聽資源即將被創建事件
    public static void onWillCreateAsset(string path)
    {
        Debug.LogFormat("path : {0}", path);
    }


    監聽資源即將被保存事件
   public static string[] OnWillSaveAssets(string[] paths)
    {
        if(paths != null)
        {
            Debug.LogFormat("path : {0}", string.Join(",", paths));
        }
        return paths;
    }

    監聽資源即將被移動事件
    public static AssetMoveResult OnWillMoveAsset(string oldPath,string newPaht)
    {
        Debug.LogFormat("from :{0} to : {1}", oldPath, newPaht);

        //AssetMoveResult.DidMove表示該資源可以移動
        return AssetMoveResult.DidMove;
    }


    監聽資源即將被刪除事件
    public static AssetDeleteResult OnWillDeleteAsset(string assetPath,RemoveAssetOptions option)
    {
        Debug.LogFormat("delete : {0}", assetPath);

        AssetDeleteResult.DidNotDelete表示該資源可以被刪除
        return AssetDeleteResult.DidDelete;
    }
}

 InitializeOnLoadMethod:允許在Unity加載時初始化編輯器類方法,而無需用戶的操作。

Debug.LogFormat(string, Object []):該函數的功能是 將格式化的消息記錄到Unity控制檯,第一個參數是包含在 “ ” 中的文本, 第二個參數是 要輸出的 遊戲對象

Debug.Log(): 將消息記錄到Unity控制檯.

string.Join() : 將數組的內容連接成一個字符串。元素由seperator字符串分隔並返回連接數組的副本。

AssetMoveResult.DidMove : 告訴內部實現,腳本在磁盤上物理移動了資產。

AssetMoveResult.DidMove : 告訴內部實現腳本將資產實際移動到磁盤上。

AssetMoveResult() : 監聽資源即將被移動事件的函數。

AssetDeleteResult() : 監聽資源即將被刪除事件。

RemoveAssetOptions : 刪除資源的選項。

AssetDeleteResult.DidDelete : 告訴Unity資產已被回調刪除。unity不會嘗試刪除資產,但會刪除緩存版本和預覽文件。

 


擴展Hierarch 視圖中的 Create 菜單選項


●   在Hierarch  視圖中可以創建所有的遊戲對象(遊戲對象由遊戲資源組成,遊戲場景由若干的遊戲對象組成),Project 視圖中可以管理所有的遊戲資源。 那也不是所有的資源都可以直接拖入 Hierarch 視圖中, 通常只有遊戲對象才能放進。

●  在 Hierarchy 視圖中,也可以對 Create 菜單項進行拓展。如圖 所示,在 Hierarchy 視圖中點擊 Create 按鈕,彈出的菜單 My Create→Cube 就是自定義拓展的菜單,代碼如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class Script_03_05
{
    [MenuItem("GameObject/My Create/Cube",false,0)]
    static void CreateCube()
    {
        GameObject.CreatePrimitive(PrimitiveType.Cube); 創建立方體
    }
}

 GameObject.CreatePrimitive (PrimitiveType. Type) 方法用於創建 Unity 基礎原始遊戲對象;

 參數Type表示要創建的原始對象的類型。

● MenuItem 方法: 第一個參數是要創建的菜單路徑,第二個是false, 第三個是菜單條的排序,越小越在前面。

● 菜單中已經包含了系統默認的一些菜單項,我們拓展的原理就是重寫 MenuItem 的自定義路徑。 Create 按鈕下的菜單項都在 GameObject 路徑下面,所以只要開頭是 GameObject/xx/xx,均可自由拓展。

 


擴展布局(點擊Hierarch中的遊戲對象在右側擴展出一組按鈕 )


● 選擇不同的遊戲對象後,在右側可根據 EditorGUI 拓展出一組按鈕,點擊 Unity 圖標按鈕後,在 Console 窗口中輸入這個
遊戲對象。
它的工作原理就是監聽 EditorApplication.hierarchyWindowItemOnGUI 渲染回調。

 

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

public class Script_03_06
{
    [InitializeOnLoadMethod]
    static void InitializeOnLoadMethod()
    {
        EditorApplication.hierarchyWindowItemOnGUI = delegate (int instanceID, Rect selectionRect) {
            在Hierarchy視圖中選擇一個資源
            if (Selection.activeObject &&
                instanceID == Selection.activeObject.GetInstanceID())
            {
                設置拓展按鈕區域
                float width = 50f;
                float height = 20f;
                selectionRect.x += (selectionRect.width - width);
                selectionRect.width = width;
                selectionRect.height = height;

                點擊事件
                if (GUI.Button(selectionRect, AssetDatabase.LoadAssetAtPath<Texture>("Assets/unity.png")))
                {
                    Debug.LogFormat("click : {0}", Selection.activeObject.name);
                }
            }
        };
    }
}

● 在代碼中實現 EditorApplication.hierarchyWindowItemOnGUI 委託,就可以重寫Hierarchy 視圖了。這裏我們使用 GUI.Button 來繪製自定義按鈕,點擊按鈕可監聽事件。

●  在VS中保存代碼後, 在Unity 中的 hierarch 視圖中的 遊戲對象就有一個小按鈕了, 點擊這個按鈕,就可以在 Console 中輸出這個遊戲對象的日誌了。

 

 

Object.GetInstanceID: 返回實例對象的ID

Button(Rect position, GUIContent content): 第一個參數爲:屏幕上用於按鈕的矩形, 第二個參數爲:該按鈕的文本、圖像和工具提示。返回結果爲: 當用戶單擊按鈕時爲真。

AssetDatabase.LoadAssetAtPath(string assetPath, Type type):第一個參數爲:資源的加載路徑, 第二個參數爲:資源的加載類型


重寫菜單(重寫Hierarch 視圖中的菜單,完全使用資金的菜單)


● 通過上面的學習,我們知道 Hierarchy 視圖中的菜單可以在原有基礎上拓展,那麼如果想徹底拋棄它的菜單項,完全使用自己的菜單項是否可行呢?答案是可行的。如圖 3-8 所示,先用鼠標選擇一個遊戲對象,點擊右鍵即可彈出我們的重寫菜單,這個菜單項已經和 Unity 自帶的完全不一樣了。它的工作原理就是監聽點擊的事件,打開一個新的菜單窗口。相關代碼如下所示:

下面看示例代碼:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class Script_03_07
{
    [MenuItem("Window/Test/HuangChengTao")]
    static void Test()
    {

    }
    [MenuItem("Window/Test/ManNiu")]
    static void Test1()
    {

    }
    [MenuItem("Window/Test/濤水/TAOSHUI")]
    static void Test2()
    {

    }

    [InitializeOnLoadMethod]  允許在Unity加載時初始化一個編輯器類方法,而不需要用戶的操作。
    static void StartInitializeOnLoadMethod()
    {
        EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyGUI;
    }

    static void OnHierarchyGUI(int instanceID,Rect selectionRect)
    {

        if (Event.current != null && selectionRect.Contains(Event.current.mousePosition)
          && Event.current.button == 1 && Event.current.type <= EventType.MouseUp)
        {
            GameObject selectedGameObject = EditorUtility.InstanceIDToObject(instanceID)
                as GameObject;


            這裏可以判斷 selectedGameObject 的條件
            if(selectedGameObject)
            {
                Vector2 mousePosition = Event.current.mousePosition;

                EditorUtility.DisplayPopupMenu(new Rect(mousePosition.x,mousePosition.y, 0, 0), "Window/Test", null);
                Event.current.Use();
            }
        }
    }
}

EditorApplication.hierarchyWindowItemOnGUI : 在hierarchy窗口中爲每個可見列表項委託GUI事件。

Event.current : 獲取當前正在處理的事件

Rect.Contains:如果點的x和y分量是該矩形內的點,則返回true。 如果允許反相存在且爲真,則允許矩形的寬度和高度取負值(即最小值大於最大值),並且測試仍然有效。參數point: 測試的點,參數allowInverse:測試是否允許矩形的寬度和高度爲負值?返回結果: 如果點位於指定的矩形內,則爲真。

 

Event.mousePosition : 用於判斷鼠標的位置。  用於EventType.MouseMove 和 EventType.MouseDrag事件。

Event.button : 表示哪個鼠標按鈕被按下了。 0表示鼠標左鍵, 1 表示鼠標右鍵, 2 表示鼠標中鍵,用於EventType.Mousedown和EventType.MouseUp事件。

Event.type:表示事件的類型。

EventType.MouseUp : 鼠標按鈕被鬆開了。 鬆開任何鼠標按鈕時,都會發送此事件。使用 Event.button來確定按下了哪個按鈕。

InstanceIDToObject: 將實例ID轉換爲對象的引用。參數是: instanceID:

EditorUtility: 編寫實用的功能。

Vector2 : 用給定的x, y分量構造一個新的向量。

DisplayPopupMenu : 顯示彈出菜單。參數是: position、menu Item Path、command 。

Rect: 創建一個新矩形.  參數x: 計算矩形的X值。 參數y: 計算矩形的y值。參數width: 矩形的寬度。 參數height: 參數的高度。

 Event.current.Use() : 使用此事件, 表示不再執行原有的操作, 所以就實現了重寫菜單。


此外, Hierarchy 視圖還可以重寫系統自帶的菜單行爲。例如,我覺得 Unity 創建的 Image 組件不好,可以複寫它的行爲,如圖所示:

創建 Image 組件時,會自動勾選 RaycastTarget。如果圖片不需要處理點擊事件,這樣會帶來一些額外的開銷。以下代碼就是複寫了創建 Image 組件的邏輯,讓 RaycastTarget 默認不勾選。
 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
public class Script_03_08
{
    [MenuItem("GameObject/UI/Image")]
	static void CreateImage()
    {
        if(Selection.activeTransform)
        {
            if(Selection.activeTransform.GetComponentInParent<Canvas>())
            {
                Image image = new GameObject("image").AddComponent<Image>();
                image.raycastTarget = false;
                image.transform.SetParent(Selection.activeTransform, false);

                //設置選中狀態
                Selection.activeTransform = image.transform;
            }
        }
    }
	
	
}

由於重寫了菜單,所以需要通過腳本自行創建 Image 對象和組件。接着,獲取到 image 組件對象,直接設置它的 raycastTarget 屬性即可。

GameObject.GetComponentInParent :  在GameObject 中 或者 其它父類型中 返回類型的組件。向上遞歸直到找到一個有效的組件。如果沒有找到組件,返回null。只返回GameObject上的組件


拓展 Inspector 視圖(擴展源生組件——攝像機)


●  可以在攝像機組件的最上面添加一個按鈕。它的侷限性就是拓展組件只能加在源生組件的最上面或者最下面,不能插在中間。
 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Camera))]
public class Script_03_09 : Editor
{
    public override void OnInspectorGUI()
    {
       if(GUILayout.Button("擴展按鈕"))
        {

        }
        base.OnInspectorGUI(); //實現此功能以創建自定義的Inspector
    }
}

CustomEditor()表示自定義哪個組件, OnInspectorGUI()可以對它進行重新繪製, base.OnInspectorGUI()表示是否繪製父類原有元素。

GUILayout.Button : 在按鈕上顯示文字。


拓展繼承組件(擴展Transform 組件)


● Unity 將大量的 Editor 繪製方法封裝在內部的 DLL 文件裏,開發者無法調用它的方法。如果想解決這個問題,可以使用 C# 反射的方式調用內部未公開的方法。如圖 所示,通過拓展的Transfom 組件,現在就可以保留原有的繪製方式了。
 

●  首先通過反射先得到 UnityEditor.TransformInspector 對象,然後就可以調用它內部的 OnInspectorGUI()方法了。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection;
[CustomEditor(typeof(Transform))]
public class Script_03_10 : Editor
{

    private Editor m_Editor;
    private void OnEnable()
    {
        m_Editor = Editor.CreateEditor(target,
                   Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.TransformInspector", true));

    }
    public override void OnInspectorGUI()
    {
        //調用系統繪製方法
        m_Editor.OnInspectorGUI();
        //base.OnInspectorGUI();
    }
}

Editor :派生自定義編輯器的基類。使用它爲對象創建自己的自定義Inspector和編輯器。

CreateEditor : 爲 targetObject 或 targetObjects 創建自定義編輯器。參數Object——所有對象必須具有相同的類型。

OnInspectorGUI(): 實現此函數以生成自定義的Inspector.

● 上述代碼中,我們重寫了 OnInspectorGUI()方法。使用 GUILayout.Button 繪製了自定義的按鈕元素,接着調用 m_Editor.OnInspectorGUI()繪製 Transform 原有面板信息,這樣我們拓展的按鈕就會顯示在 Transform 面板的上方。

就像下面這樣:


組件不可以編輯 (禁掉某個組件,使它無法編輯)


● 在 Unity 中,我們可以給組件設置狀態,這樣它就無法編輯了。如圖所示,將 Transform組件的原始功能禁掉(灰色表示不可編輯),而不影響我們上下拓展的兩個按鈕。代碼如下:
 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection;

[CustomEditor(typeof(Transform))]
public class Script_03_11 : Editor
{
    private Editor m_Editor;
    private void OnEnable()
    {
        m_Editor = Editor.CreateEditor(target,
            Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor." +
            "TransformInspector", true));
    }
    public override void OnInspectorGUI()
    {
        if(GUILayout.Button("擴展按鈕上"))
        {

        }
        // 開始禁止
        GUI.enabled = false;
        m_Editor.OnInspectorGUI();
        //結束禁止
        GUI.enabled = true;
        if (GUILayout.Button("擴展按鈕下"))
        {

        }
      
    }

}

● 如果想整體禁止組件,可以按照圖所示選擇任意遊戲對象,然後從右鍵菜單中選擇 3DObject→Lock→Lock(鎖定)或者 UnLock(解鎖)。它的原理就是設置遊戲對象的 hideFlags。需要說明的是,我們不一定非設置遊戲對象的 hideFlags,也可以單獨給某個組件設置hideFlags,這樣只會影響到某一個組件並非全部。相關代碼如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class Script_03_12
{
    [MenuItem("GameObject/3D object/Lock/lock",false,0)]
    static void Lock()
    {
        if(Selection.gameObjects!=null)
        {
            foreach(var gameObject in Selection.gameObjects)
            {
                gameObject.hideFlags = HideFlags.NotEditable;
            }
        }
    }
    [MenuItem("GameObject/3D object/Lock/UnLok",false,1)]
    static void UnLock()
    {
        if(Selection.gameObjects!=null)
        {
            foreach(var gameObject in Selection.gameObjects)
            {
                gameObject.hideFlags = HideFlags.None;
            }
        }
    }
}

HideFlags 可以使用按位或(|)同時保持多個屬性,其含義都很好理解,大家可以自行輸入代碼調試一下。


 HideFlags.None: 解除狀態。
 HideFlags.DontSave:  設置對象不會被保存(僅編輯模式下使用,運行時剔除掉)。
 HideFlags.DontSaveInBuild:  設置對象構建後不會被保存。


 HideFlags.DontSaveInEditor:  設置對象編輯模式下不會被保存。
 HideFlags.DontUnloadUnusedAsset:   設置對象不會被 Resources.UnloadUnusedAssets()卸載無用資源時卸掉。
 HideFlags.HideAndDontSave:      設置對象隱藏,並且不會被保存。


 HideFlags.HideInHierarchy:   設置對象在層次視圖中隱藏。
HideFlags.HideInInspector:     設置對象在控制面板視圖中隱藏。
HideFlags.NotEditable:          設置對象不可被編輯。

 

Selection.gameObjects——選擇返回實際的遊戲對象。包括prefab,不可修改的對象。當處理場景中主要的對象時,強烈建議使用Selection.transforms。

Selection —— 訪問編輯器中的選定內容。


Context 菜單


● 點擊組件中設置(鼠標右鍵),可以彈出 Context 菜單,如圖所示,我們可在原有菜單中拓展出新的菜單欄,相關代碼如下:
 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class Script_03_13
{
    [MenuItem("CONTEXT/Transform/ New Context 1")]
    public static void NewContext1(MenuCommand command)
    {
        //獲取對象名
        Debug.Log(command.context.name);
    }
	[MenuItem("CONTEXT/Transform/New Context 2")]
    public static void NewContext2(MenuCommand command)
    {
        Debug.Log(command.context.name);
    }
}

MenuCommand——用於提取MenuItem的上下文。MenuCommand對象被傳遞給使用MenuItem屬性定義的自定義菜單項函數。

注意:菜單項被添加到對象中,並且可以通過Inspector中的右擊來訪問。 腳本代碼需要CONTEXT選項。

MenuCommand.context——Context是菜單命令的目標對象。


其中, [MenuItem("CONTEXT/Transform/New Context 1")] 表示將新菜單擴展在Transform 組件上。如果想拓展在別的組件上,例如攝像機組件,直接修改字符串中的 Transform爲 Camera 即可。如果想給所有組件都添加菜單欄,這裏改成 Compoment 即可。


以上設置也可以應用在自己寫的腳本中。如圖 ,Script_03_14Component.cs 是自己創建的腳本,在代碼中可以通過 MenuCommand 來獲取腳本對象,從而訪問腳本中的變量,相關代碼下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class Script_03_14Component: MonoBehaviour
{
    public string contextName;
#if UNITY_EDITOR
    [MenuItem("CONTEXT/Script_03_14/NewTTContext 1")]
    public static void NewContext2(MenuCommand command)
    {
        Script_03_14 script = (command.context as Script_03_14);
        script.contextName = "hello world!!";
    }
#endif
}

注意: 在該腳本中繼承了MonoBehaviour類, 表示我們應該要在Script 文件夾中創建腳本,而不是在Editor中創建腳本,編輯好腳本後,保存。  然後鼠標左鍵拖到Hierarch 中的某一個遊戲對象上去。

MonoBehaviour是每個Unity腳本派生的基類。

MenuCommand 是獲取腳本對象。

ContextMenu —— ContextMenu屬性允許你向Context菜單添加指令。設置該功能的函數必須是靜態的。

在上述代碼中,我們使用到了宏定義標籤,其中UNITY_EDITOR 表示這段代碼只會在Editor模式下執行,發佈後將被剔除掉。
看我們擴展後的結果:

 

當然,我們也可以在自己的腳本中這樣寫。如果和系統的菜單項名稱一樣,還可以覆蓋它。比如,這裏重寫了刪除組件的按鈕,就可以執行一些自己的操作了:


[ContextMenu("Remove Component")]
void RemoveComponent()
{
Debug.Log("RemoveComponent");
//等一幀再刪除自己
UnityEditor.EditorApplication.delayCall = delegate() {
DestroyImmediate(this);
};
}


編輯模式下的代碼同步時有可能會有問題,比如上述 DestroyImmediate(this)刪除自己的代碼時,會觸發引擎底層的一個錯誤。不過我們可以使用 UnityEditor.EditorApplication.delayCall 來延遲一幀調用。後續如果大家在開發編輯器代碼時發現類似問題,也可以嘗試等
一幀再執行自己的代碼。

 

後續在這篇文章: 第三章——擴展Unity編輯器2 —— https://blog.csdn.net/qq_34536551/article/details/83793216

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章