UnityEditor研究學習之自定義Editor
今天我們來研究下Unity3d中自定義Editor,這個會讓物體的腳本在Inspector視窗中,產生不同的視覺效果。
什么意思,舉個例子,比如游戲中我有個角色Player,他有攻擊力,護甲,裝備等屬性。
所以我定義一個腳本:MyPlayer.cs:
using UnityEngine; using System.Collections; public class MyPlayer : MonoBehaviour { public int armor = 100; public int attack = 100; public GameObject equipment; void Start() { } void Update() { } }
這邊我定義了三個變量,分別是護甲、攻擊力還有GameObject類型的裝備。
將這個腳本賦值給GameObject,可以看到Inspector視窗:
那么,如果我想要修改下護甲,攻擊力的顯示效果,那么就可以自定義Editor:
using UnityEngine; using UnityEditor; using System.Collections; [CustomEditor(typeof(MyPlayer))] public class MyPlayerEditor : Editor { public override void OnInspectorGUI() { var target = (MyPlayer)(serializedObject.targetObject); target.attack = EditorGUILayout.IntSlider("攻擊力",target.attack,0,100); ProgressBar(target.attack, "攻擊力"); target.equipment = EditorGUILayout.ObjectField("裝備",target.equipment,typeof(GameObject)); } private void ProgressBar(float value, string label) { Rect rect = GUILayoutUtility.GetRect(18, 18, "TextField"); EditorGUI.ProgressBar(rect, value, label); EditorGUILayout.Space(); } }
是不是一下子就好看了不少,操作性也變強了。這個就是編輯器的魅力所在。
還有一種寫法就是,通過SerializedObject的SerializedProperty
我個人不是很推薦,因為更加復雜,但是效果跟上面第一種完全一樣:
using UnityEngine; using UnityEditor; using System.Collections; [CustomEditor(typeof(MyPlayer))] public class MyPlayerEditor2 : Editor { SerializedProperty attack; void OnEnable() { attack = serializedObject.FindProperty("attack"); } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.IntSlider(attack, 0, 100, new GUIContent("攻擊力")); ProgressBar(attack.intValue/100, "攻擊力"); serializedObject.ApplyModifiedProperties(); } private void ProgressBar(float value, string label) { Rect rect = GUILayoutUtility.GetRect(18, 18, "TextField"); EditorGUI.ProgressBar(rect, value, label); EditorGUILayout.Space(); } }
接下來開始具體學Editor的變量和方法等:
Editor.serializedObject
自定義編輯器所需要序列化的物體。看下官方的描述:
Description
A SerializedObject representing the object or objects being inspected.
The serializedObject can be used inside the OnInspectorGUI function of a custom Editor as described on the page about the Editor class.
The serializedObject should not be used inside OnSceneGUI or OnPreviewGUI. Use the target property directly in those callback functions instead.
就是說serializedObject只能在OnInspectorGUI方法里面使用,其他地方是不行的,還有在OnSceneGUI和OnPreviewGUI使用target來代替serializedObject。
接着來看看Editor有哪些方法:
1.Editor.DrawDefaultInspector
Description
Draw the built-in inspector.
Call this function from inside OnInspectorGUI method to draw the automatic inspector. It is useful you don't want to redo the entire inspector, but you want to add a few buttons to it.
這個方法,我們要在OnInspectorGUI()方法里面調用,用來繪制一些平常的UI,比如滑塊,按鈕等等。反正只要記住在OnInspectorGUI()方法最后調用這個方法就行。
2.Editor.OnPreviewGUI
Parameters
r | Rectangle in which to draw the preview. |
background | Background image. |
Description
Implement to create your own custom preview for the preview area of the inspector, primary editor headers and the object selector.
If you implement OnInteractivePreviewGUI then this will only be called for non-interactive custom previews. The overidden method should use the rectangle passed in and render a preview of the asset into it. The default implementation is a no-op.
Note: Inspector previews are limited to the primary editor of persistent objects (assets), e.g., GameObjectInspector, MaterialEditor, TextureInspector. This means that it is currently not possible for a component to have its own inspector preview.
這個方法是創建我們自己的物體的Preview窗口的表現。我們接下來做個試驗看下這個Preview窗口長啥樣子。
首先,我們先創建一個窗口,然后在窗口中傳入我們需要想看的物體。
using UnityEngine; using System.Collections; using UnityEditor; public class MyPreviewWindow : EditorWindow { static EditorWindow myWindow; GameObject gameObject; Editor gameObjectEditor; [MenuItem("Window/MyPreviewWindow")] public static void Init() { myWindow = (MyPreviewWindow)EditorWindow.GetWindow(typeof(MyPreviewWindow), false, "MyPreviewWindow", false); myWindow.Show(); } void OnGUI() { gameObject = (GameObject)EditorGUILayout.ObjectField(gameObject, typeof(GameObject), true); if (gameObject != null) { if (gameObjectEditor == null) { gameObjectEditor = Editor.CreateEditor(gameObject); } gameObjectEditor.OnPreviewGUI(GUILayoutUtility.GetRect(500, 500), EditorStyles.whiteLabel); } } }
這里我直接拖入一個Cube,然后觀察下,下面的Preview窗口:
可以從下面的Preview窗口中看到Cube的模樣。
所以我們想要觀察一個物體,可以先為他創建他本身的Editor,然后直接修改他的OnPreviewGUI()方法就可以
了。
3.Editor.DrawPreview
Parameters
previewPosition | The available area to draw the preview. |
Description
The first entry point for Preview Drawing.
Draws a grid of previews if there are multiple targets available. Override this method if you want to customize this behaviour.
這個方法是繪制Preview視圖,我們可以重寫這個方法來完成當我們調用這個方法的時候會有不同的顯示效果。
using UnityEngine; using System.Collections; using UnityEditor; public class MyPreviewWindow : EditorWindow { static EditorWindow myWindow; GameObject gameObject; Editor gameObjectEditor; [MenuItem("Window/MyPreviewWindow")] public static void Init() { myWindow = (MyPreviewWindow)EditorWindow.GetWindow(typeof(MyPreviewWindow), false, "MyPreviewWindow", false); myWindow.Show(); } void OnGUI() { gameObject = (GameObject)EditorGUILayout.ObjectField(gameObject, typeof(GameObject), true); if (gameObject != null) { if (gameObjectEditor == null) { gameObjectEditor = Editor.CreateEditor(gameObject); } gameObjectEditor.OnPreviewGUI(GUILayoutUtility.GetRect(500, 500), EditorStyles.whiteLabel); gameObjectEditor.DrawPreview(GUILayoutUtility.GetRect(500, 500)); } } }
我們在原先的代碼中添加
gameObjectEditor.DrawPreview(GUILayoutUtility.GetRect(500, 500));
來看下會有啥不同的變化:
可以看到有兩個Cube,但是他們的背景不同,主要是我們沒有重寫DrawPreview()。