[Unity]客製化Inspector

 設計自己的Inspector!

緣由

自從改用GameManager的GameLoop來統一控制遊戲的整體流程後,唯一讓我困擾的是他的Inspector視窗,裡面的屬性真的太長~了,每次要除錯都要找一下子,雖然各資料屬性是class所以可以摺疊,但總歸還是非常不便(如圖,很長一大串)。


Editor資料夾

要使自己的腳本是在編輯中執行而不是在遊戲中執行的話,需要在Assets根目錄下新增一個資料夾取名為Editor,經由特殊資料夾名稱,系統會將下面的腳本認定為Editor 腳本(如圖)。


腳本

匯入與繼承

如我在資料夾內的那3個腳本一樣,新增一個腳本然後取一個你想要的名字吧。

1.首先需要匯入編輯器的命名空間 UnityEditor
2.然後添加Attribute : CustomEditor,其用途是告訴編輯器,哪一個腳本是你要自訂的對象。
3.使你腳本改繼承 Editor

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

/// <summary>
/// 客製化GameManager (MainGameLoop) 的視窗
/// </summary>
[CustomEditor(typeof(MainGameLoop))]
public class GameManagerInspector : Editor
{

覆寫Inspector的GUI設計

Editor有很多功能可以覆載,我們這次只要處理Inspector,所以只要覆載OnInspectorGUI就好。
並且指定要編輯的物件 _gameLoop,這裡有一點要注意,是可以多物件編輯的,但是Editor.target只會引用第一個已編輯物件。


    
public class GameManagerInspector : Editor
{
    public override void OnInspectorGUI()
    {
        MainGameLoop _gameLoop = (MainGameLoop)target;
   

開始Inspector內的處理吧

GameLoop內的屬性簡單介紹

在進行Inspector的覆寫之前,先來介紹目標類別的一些屬性吧,以好比對指定了那些屬性。

提供選單列舉

為了讓使用者可以選擇目前要設定的屬性,所以我們要創造一個列舉,在裡面放入選單的元素,在Unity內,只要有列舉型別的全域變數,在inspector內就會被視為選單(如圖)。

    
    /// <summary>
    /// Inspector設定的頁面類型
    /// </summary>
    public enum SettingType
    {
        MainCameraProcess,
        BookUI,
        SoundEffect,
        WarUI,
        OptionPage,
        SavePage
    }
    public SettingType MainSettingType;
  

屬性

在GameLoop或你自己的有繼承MonoBehavior的類別內,有些需要在Inspector指定數值或物件的資料類別屬性,在此不贅述。
    
    [Header("BookUI的資料")]
    public BookUIData MainBookUIData;

    [Header("SoundEffectData的資料")]
    public SoundEffectData MainSoundEffectData;

    [Header("選項頁面的資料")]
    public UIOptionCtrlData MainUIOptionCtrlData;

    [Header("戰爭UI的資料")]
    public WarUICtrlData MainWarUICtrlData;
  

顯示下拉清單

回到Editor腳本的OnInspectorGUI()這裡,由於覆載了GUI的顯示功能,所以目前GameLoop的屬性視窗是什麼都不會顯示的,為此我們使用EditorGUILayout.EmumPopup來顯示下拉式選單,"頁面類型"為會顯示在上面的清單名稱。
    
        MainGameLoop _gameLoop = (MainGameLoop)target;

        //顯示下拉是列舉清單
        _gameLoop.MainSettingType =
            (MainGameLoop.SettingType)EditorGUILayout.EnumPopup("頁面類型",
            _gameLoop.MainSettingType);
  

序列化物件

屬性欄位

像是物件屬性,或是實質型別屬性(float,int...)其實有 像是EditorGUILayout.FloatField或是EditorGUILayout.ObjectField這種方法可以使用,但原本的Attribute就會消失,而在原本屬性名稱的位置則可以自由地輸入你想要的名稱。

由於要抓取自己的類別屬性,或是抓取普通的屬性,都可以使用屬性欄位PropertuField,為了方便使用,我統一都使用

EditorGUILayout.PropertyField

完成修改後,應用屬性修改ApplyModifiedProperties()很重要,不能沒有加。

另一點,"CanvasUI"是你那個屬性的名稱,也就是說,如果你的屬性是 float _value1;,那你要輸入的就是"_value1"。

true是用來確認要不要顯示內藏的值,如果沒有打true,則會再inspector顯示屬性的名稱與箭頭,但是不管打開關閉箭頭都不會有內部的值跑出來讓你設定


    
        serializedObject.Update();
        EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("CanvasUI"), true);
        serializedObject.ApplyModifiedProperties();
  

加入選擇

在來就是透過選單來切換要在Inspector顯示的屬性們啦!
我的作法是把所需要的資料都封裝在各自的資料類別內,這樣只要我抓取所宣告的資料類別屬性,我要動態的增減都不用再動到Editor腳本。
    
        switch (_gameLoop.MainSettingType)
        {
            case MainGameLoop.SettingType.MainCameraProcess:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainCameraProcessData"), true);
                break;
            case MainGameLoop.SettingType.BookUI:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainBookUIData"), true);
                break;
            case MainGameLoop.SettingType.SoundEffect:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainSoundEffectData"), true);
                break;
            case MainGameLoop.SettingType.WarUI:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainWarUICtrlData"), true);
                break;
        }


完成

完成的樣子

最後目標類別在Inspector視窗會顯示的樣子如圖,可以透過頁面類型,來自由切換想設定的值




完整程式碼

在此提供Editor部分的程式碼。

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

/// <summary>
/// 客製化GameManager (MainGameLoop) 的視窗
/// </summary>
[CustomEditor(typeof(MainGameLoop))]
public class GameManagerInspector : Editor
{
    public override void OnInspectorGUI()
    {
        MainGameLoop _gameLoop = (MainGameLoop)target;

        //顯示下拉是列舉清單
        _gameLoop.MainSettingType =
            (MainGameLoop.SettingType)EditorGUILayout.EnumPopup("頁面類型",
            _gameLoop.MainSettingType);
        serializedObject.Update();
        switch (_gameLoop.MainSettingType)
        {
            case MainGameLoop.SettingType.MainCameraProcess:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainCameraProcessData"), true);
                break;
            case MainGameLoop.SettingType.BookUI:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainBookUIData"), true);
                break;
            case MainGameLoop.SettingType.SoundEffect:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainSoundEffectData"), true);
                break;
            case MainGameLoop.SettingType.WarUI:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainWarUICtrlData"), true);
                break;
            case MainGameLoop.SettingType.OptionPage:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainUIOptionCtrlData"), true);
                break;
            case MainGameLoop.SettingType.SavePage:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainUISaveCtrlData"), true);
                break;
            case MainGameLoop.SettingType.MissionPage:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainUIMissionCtrlData"), true);
                break;
            case MainGameLoop.SettingType.ItemPage:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainUIItemCtrlData"), true);
                break;
            case MainGameLoop.SettingType.WeaponPage:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainUIWeaponCtrlData"), true);
                break;
            case MainGameLoop.SettingType.WeaponProcess:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainWeaponProcessData"), true);
                break;
            case MainGameLoop.SettingType.MusicCtrl:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainMusicCtrlData"), true);
                break;
            case MainGameLoop.SettingType.PlayerCtrl:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainPlayerCtrlData"), true);
                break;
            case MainGameLoop.SettingType.DialogSystem:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainDialogSystemData"), true);
                break;
            case MainGameLoop.SettingType.PlotSystem:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainPlotData"), true);
                break;
            case MainGameLoop.SettingType.Training:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainTrainingData"), true);
                break;
            case MainGameLoop.SettingType.LoadingProcess:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainLoadingProcessData"), true);
                break;
            case MainGameLoop.SettingType.StoreSystem:
                EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("MainStoreData"), true);
                break;

        }

        EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("PlayerObject"), true);
        EditorGUILayout.PropertyField(serializedObject.FindProperty
                  ("GameBlack"), true);
        EditorGUILayout.PropertyField(serializedObject.FindProperty
                ("CanvasUI"), true);
        serializedObject.ApplyModifiedProperties();
    }
}

留言

張貼留言

這個網誌中的熱門文章

[Unity] 關於Android 的環境設定,使用自己的SDK/JDK/NDK

[Godot] Sprite Sheet 使用以及HDR Glow 發光效果

[Godot] 起始之初,編輯器認識:關聯Rider IDE