【Unity編輯器】編輯器拓展(1)-Editor

【參考原文】Unity3D 場景編輯器擴展學習筆記-Editor
Unity3D 場景編輯器擴展學習筆記-Handles&Event
Unity3D 場景編輯器擴展學習筆記-EditorWindow
PS:適合對UnityEditor類有一些瞭解的童鞋閱讀,我沒有完全研究透這套東西,暫時先學到哪寫到哪吧。
*所有的Editor相關代碼,必須存在於Assets目錄下的任意路徑的名爲Editor文件夾內。U3D會自動爲這些文件生成編輯器工程。
重要的類
Editor,EditorWindow
GUILayout,EditorGUILayout
GUIUtility,EditorGUIUtility
Handles,Event

架構:Editor,EditorWindow
Editor類和EditorWindow類都繼承自同一個基類:ScriptableObject,因此他們都可以針對某種腳本類來進行操作。
Editor類只能定製針對腳本的擴展,從腳本內容在Inspector裏的顯示佈局,到變量在Scene視圖的可視化編輯
EditorWindow主要是擴展編輯器的功能,不必針對某種腳本(雖然可以做到),而且它有獨立的窗口,使用OnGUI函數來繪製2D的UI。

2D顯示:GUILayout,EditorGUILayout
GUILayout類用來繪製各種2D控件,比如按鈕,文本,可編輯文本,可摺疊的列表,以及對它們的分組和排版。這些控件既可以繪製在Editor類裏,也可以繪製在EditorWindow裏。
EditorGUILayout類和GUILayout類似,但是提供更多的預定義控件,而且只能在Editor下纔可以使用。

自定義2D控件:GUIUtility,EditorGUIUtility
GUIUtility類用來實現自定義的2D對象。
EditorGUIUtility類和GUIUtility類似,針對Editor下使用的GUI進行定製。

3D顯示:Handles
Handles類提供各種場景視圖裏的顯示,包括繪製線、面、扇形、方塊、圓錐、3D空間的按鈕(所謂按鈕,其實就是個響應區域,你要是信U3D的就囧了),還有一些預定義的操作控件,比如三向箭頭,旋轉球,自由旋轉等等,這些操作針對的不是物體,而是數值,因此除了可以控制腳本對象所在的GameObject,也可以控制某些成員變量,本質上都是在控制變量數值,因此場景擴展這個類肯定要大顯身手。

輸入:Event
Event類用來獲取輸入相關的信息,Event指的是InputEvent,包括鼠標位置,鍵盤按下的KEYCODE,各種命令鍵(Windows的Windows鍵和Mac的Command鍵),Shift,CapsLocks之類,還有U3DEditor裏的CommandName,比如 Copy,SeletecAll,Save這種菜單命令。這個信息在OnGUI,OnInspector,OnSceneGUI裏都可以使用。

Editor類
使用
using UnityEditor;
[Custom(typeof(TypeName))]
class YourEditorClass : Editor
該類可以自定義對腳本類在Inspector裏的視圖,重要的事件如下:
OnEnable
腳本所在對象被選中時觸發,當編輯器開始自動重新編譯時,對象會重新被選擇一次而觸發此消息。
OnDisable
腳本所在對象被取消選中時觸發,當編輯器開始自動重新編譯時,對象會重新被選擇一次而觸發此消息,腳本移除時也會觸發此消息。
OnDestroy
腳本從對象上移除時觸發
上述三個時機,適合初始化和反初始化,一些重要的對象都可以在這裏獲取並緩存出來,比如 target對象(正在處理的腳本實例)等。
注意:實際測試時,並不是Disable或Destroy之後,就沒有代碼在執行了。所以幹活的代碼在引用緩存下來的變量之前,先判斷下是否爲空,否則會報異常。這會影響腳本保存變量實例的值。不過這個問題很好查,只需要保證“腳本被移除時無異常,重新添加腳本時也無異常”這個測試用例就可以了。
OnInspectorGUI
定製腳本在Inspector裏的顯示方式,基本上配合GUILayout可以寫出十分複雜的定製界面。有時候Inspector太小或不想和其他組件一起顯示時,可以使用EditorWindow來做這個定製界面。
OnSceneGUI
定製腳本在Scene裏的表現,比如可以將腳本內一個怪物AI的”警戒半徑“變量,用很直觀的圓盤來表達出來,也有很多別的表現和操作,這些表現和操作都是通過GUILayout和Handles類來完成的。

OnInspectorGUI範例代碼:

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

class UIResource : MonoBehaviour
{
}
[CustomEditor(typeof(UIResource))]
class UIManifestCreator : Editor
{
    private UIResource _script;
    private static bool _toggle_all = true;
    private GameObject _rootObject;

    public void OnEnable()
    {
        var script = (UIResource)(serializedObject.targetObject);
        if (script != null)
        {
            _script = script;
            _rootObject = script.gameObject;
        }
        else
        {
            //這是什麼情況;
            Console.Error.WriteLine("tell uncle kun!");
        }
    }

    public void OnDisable()
    {
        var script = (UIResource)(serializedObject.targetObject);
        if (script == null)
        {
            // 這種情況是腳本對象被移除了;
        }
        else
        {
            // 這種情況是編譯腳本導致的重刷;
        }
        _script = null;
        _rootObject = null;
    }

    public void OnDestroy()
    {
    }

    public override void OnInspectorGUI()
    {
        GUILayout.BeginHorizontal();
        {
            if (GUILayout.Button("Save"))
            {
                EditorApplication.SaveCurrentSceneIfUserWantsTo();
            }
            if (GUILayout.Button(_toggle_all ? "Untoggle All" : "Toggle All"))
            {
                _toggle_all = !_toggle_all;
            }
        }
        GUILayout.EndHorizontal();

        EditorGUILayout.LabelField("", GUILayout.Width(150));
        Repaint();
    }
}

OnSceneGUI範例代碼:

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

public class SceneTag : MonoBehaviour
{
    public float shieldArea = 5;
}

[CustomEditor(typeof(SceneTag))]
public class SceneEditor : Editor {

    void OnSceneGUI()
    {
        SceneTag scene = target as SceneTag;
        Handles.color = Color.blue;
        Handles.Label(scene.transform.position + Vector3.up * 2,
                scene.transform.position.ToString() + "\nShieldArea: " +
                scene.shieldArea.ToString());

        Handles.color = Color.red;
        Handles.DrawWireArc(scene.transform.position,
                scene.transform.up,
                -scene.transform.right,
                180,
                scene.shieldArea);
        scene.shieldArea =
        Handles.ScaleValueHandle(scene.shieldArea,
                        scene.transform.position + scene.transform.forward * scene.shieldArea,
                        scene.transform.rotation,
                        1,
                        Handles.ConeCap,
                        1);

        //comment by kun 2014.2.18
        // GUI相關的繪製需要在Handles的繪製之後,否則會被覆蓋掉;
        // 使用Handles.BeginGUI會導致無法旋轉攝像機,原因不詳;
        GUILayout.BeginArea(new Rect(Screen.width - 100, Screen.height - 80, 90, 50));
        //Handles.BeginGUI(new Rect(Screen.width - 100, Screen.height - 80, 90, 50));
        try
        {
            float a = float.Parse(GUILayout.TextField(scene.shieldArea.ToString()));
            scene.shieldArea = a;
        }
        catch (System.Exception ex)
        {

        }
        if (GUILayout.Button("Reset Area"))
            scene.shieldArea = 5;
        //Handles.EndGUI();
        GUILayout.EndArea();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章