unity編輯器擴充套件#1 自定義視窗、面板、和屬性
擼一遍unity官方文件的給的三個案例:https://docs.unity3d.com/2017.4/Documentation/Manual/editor-EditorWindows.html
自定義視窗
首先,要注意和編輯器相關的指令碼都應該把放到Editor資料夾裡,可以有多個Editor資料夾。
然後建立一個繼承自EditorWindow的類
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; //要建立的編輯器視窗類應該繼承EditorWindow; public class OneEditorWindow : EditorWindow { //MenuItem是一個特性,會在頂部按路徑生成選項,點選就會呼叫下面的方法,也就是開啟視窗 [MenuItem("Unity編輯器/OneEditorWindow")] public static void OpenWindow() { //開啟視窗的方法 註釋掉的方法你可以設定開啟的視窗的位置和大小 //可以直接用沒引數的過載方法,引數用來設定視窗名稱,是否用標籤視窗形式之類的 EditorWindow.GetWindow<OneEditorWindow>(false,"一個視窗"); //EditorWindow.GetWindowWithRect<MyFirstWindow>(new Rect(0f, 0f, 500, 500)); } private Color col; private float f; //OnGUI裡寫你想繪製在窗口裡的內容 private void OnGUI() { EditorGUILayout.LabelField("一個label"); f = EditorGUILayout.FloatField("一個數字框", f); col=EditorGUILayout.ColorField("一個顏色選擇框",col); } }
就好了~
自定義類和成員的顯示
這裡提到的指令碼不需要放到Editor資料夾裡,因為他只是修改原有的顯示而已
修改類例項的的顯示
紅色框的就是我們要修改的類 Ingredient在Inspector面板上的顯示,左邊是Unity預設的顯示,右邊是修改後的。
首先是用於實驗的類
public class Recipe: MonoBehaviour { public Ingredient PotionResult; public Ingredient[] PotionIngredients; } public enum IngredientUnit {Cup,Bowl,Spoon,Piece} [Serializable]//這個特性表示該類可以被序列化,但是不加好像也沒關係 public class Ingredient { //需要重新設計屬性面板的類 public string Name; public int Amount; public IngredientUnit Unit; }
接下來就是寫可以修改顯示的類
修改顯示的類應該繼承:PropertyDrawer
用特性[CustomPropertyDrawer(typeof(type))]表明它是用來針對修改哪個類的
和視窗一樣,在OnGUI裡寫繪製的程式碼,
方法提供來三個引數,Rect是位置和寬高;property裡面存著類的相關資訊,可以用FindPropertyRelative(name)來找類成員;label就是名字;
在BeginProperty()和EndProperty()裡寫可以確保佈局的整齊
[CustomPropertyDrawer(typeof(Ingredient))] public class IngredientDrawer:PropertyDrawer { //重新繪製面板 public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(position, label, property); //每個例項的名字 本來應該是Element0123 ,但這裡顯示了Name字串 position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); //本來是1層,要縮排,設定成0和他的上級size同級,使之與其對齊; var index = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; //給三個屬性安排位置 var amountRect = new Rect(position.x, position.y, 30, position.height); var unitRect = new Rect(position.x + 35, position.y, 50, position.height); var nameRect = new Rect(position.x + 90, position.y, position.width - 90, position.height); //繪製屬性 EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("Amount"), GUIContent.none); EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("Unit"), GUIContent.none); EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("Name"), GUIContent.none); //重新設定為原來的層級 EditorGUI.indentLevel = index; EditorGUI.EndProperty(); } }
修改類中成員的顯示
修改類成員的顯示通過給成員新增屬性特性(Property Attributes)來實現,像unity自帶的[Range(min,max)]就是一個特性,這裡實現一個自己的Range來作為例子
先定義一個特性類,繼承:PropertyAttribute
public class MyRangeAttribute : PropertyAttribute
{
public float min;
public float max;
public MyRangeAttribute(float min, float max)
{
this.min = min;
this.max = max;
}
}
然後和修改類的顯示類似,寫一個繼承PropertyDrawer的類,在OnGUI裡寫顯示的程式碼
[CustomPropertyDrawer(typeof(MyRangeAttribute))]
public class RangeDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
MyRangeAttribute range = (MyRangeAttribute) attribute;
if (property.propertyType == SerializedPropertyType.Float)
EditorGUI.Slider(position, property, range.min, range.max, label);
else if (property.propertyType == SerializedPropertyType.Integer)
EditorGUI.IntSlider(position, property, (int) range.min, (int) range.max, label);
else
EditorGUI.LabelField(position, label.text, "Use MyRange with float or int.");
}
}
attribute是PropertyDrawer裡的成員,儲存了所對應的特性類,強轉出真正的特性。 然後就是讀取attribute和property裡的資料生成slider控制元件了,關於GUI裡的各種控制元件看#2.
自定義編輯器
就是像遊戲中的地形系統啊、布料系統啊,你新增元件到遊戲物體,然後就可以在編輯狀態,在Sence或者哪裡裡做各種修改和操作。
首先是寫一個用於實驗的類,因為要在編輯模式也能執行,所以加了個[ExecuteInEditMode]的特性
[ExecuteInEditMode]
public class LookAtPoint : MonoBehaviour
{
public Transform lookPoint;
public Vector3 lookPos;
public void Update()
{
if (lookPoint)
{
transform.LookAt(lookPos);
}
}
}
接下來就是重要的編輯器類了,繼承:Editor,要放在Editor資料夾裡
[CustomEditor(typeof(LookAtPoint))]
[CanEditMultipleObjects]
public class LookAtPointEditor : Editor
{
private SerializedProperty lookAtPoint;
private SerializedProperty lookPos;
private void OnEnable()
{
lookAtPoint = serializedObject.FindProperty("lookPoint");
lookPos = serializedObject.FindProperty("lookPos");
}
//調整在Inspector上的佈局
public override void OnInspectorGUI()
{
serializedObject.Update();//更新面板顯示
//選擇的transform
EditorGUILayout.PropertyField(lookPos);
serializedObject.ApplyModifiedProperties();//將面板值應用到屬性
/*
//轉化成實際的型別
Transform a=(Transform)lookAtPoint.objectReferenceValue;
if (a.position.y > ((LookAtPoint)target).transform.position.y)
{
EditorGUILayout.LabelField("(Above this object)");
}
if (a.position.y < ((LookAtPoint)target).transform.position.y)
{
EditorGUILayout.LabelField("(Below this object)");
}
*/
}
//在Scene場景裡新增繪製一些需要的UI佈局
public void OnSceneGUI()
{
var t = (target as LookAtPoint);
EditorGUI.BeginChangeCheck();
//繪製一個圖形(移動物體的那個三箭頭),位置對應lookPos
Vector3 pos = Handles.PositionHandle(t.lookPos, Quaternion.identity);
if (EditorGUI.EndChangeCheck())
{//有調整就修改數值
Undo.RecordObject(target, "Move point");
t.lookPos = pos;
t.Update();
}
}
//protected override void OnHeaderGUI();
//public override void OnPreviewGUI(Rect r, GUIStyle background)
//public override void OnInteractivePreviewGUI(Rect r, GUIStyle background)
}
首先是兩個特性,[CustomEditor(typeof(type))]和前面的類似,用來表示你這個Editor管的是哪個元件,這裡就是我們的LookAtPoint,[CanEditMultipleObjects]則表示該編輯器可以支援多個物體。
用serializedObject.FindProperty來找需要的資料
這裡用到了兩個顯示的函式,一個處理Inspector上指令碼的顯示,一個處理Scene場景中的顯示,後面還註釋了幾個沒用到的。
記得把SerializedProperty轉化成正確的型別,一般的資料結構它獨有對應的屬性,像lookAtPoint.doubleValue就會返回double,當然lookAtPoint不是double型別,會報錯,對於引用型別,用.objectReferenceValue,然後再轉一下,像文中註釋的部分一樣