【Unity3Dエディタ開発】Unity3Dエディタ開発の基本フレームワーク【総まとめ】

推奨読書

皆さんこんにちは、仏教エンジニア☆静かなる小さな魔法のドラゴン☆です。Unity の開発スキルを随時更新しています。役に立ったと思ったら、忘れずに 3 回クリックしてください。

I.はじめに

皆さんこんにちは、私は です恬静的小魔龙

クラスメートの皆さん、建国記念日おめでとうございます。休暇中に一生懸命勉強しましたか?

最近 Unity3D エディタについて知りました。学習の過程で、混同しやすい点をいくつか見つけました。自分自身やクラスメートが学びやすいように特別にまとめました。コピーを作成しました。ご批判やご意見をお待ちしております。修正Unity3D编辑器开发脉络图

2. Unity3Dエディターの開発

2-1. Unity3Dエディターの開発コンテキスト図

まず、コンテキスト図を作成します。
ここに画像の説明を挿入します
大きい画像ですので、拡大してご覧いただけます。

この写真を見て、どこから始めればよいかわからないと感じていませんか? 次に、この写真の見方を分析してみましょう。

2-2. Unity3Dエディタの開発分類

OnGUIブロガーが最初にエディター開発を学び始めたとき、なぜある時は描画ウィンドウを使い、別の瞬間には描画ウィンドウを使いEditor、また別の時には描画ウィンドウを使いEditorWindowsPropertyDrawer継承後の描画には属性を使うのかと混乱したように見えました。

それらの違いとつながりは何ですか?

  • 窗口绘制単純に、检视器绘制场景绘制に分けることができます。属性绘制
  • 窗口绘制EditorWindowクラスを継承して、OnGUIその中にウィンドウを描画する必要があります。
  • 检视器绘制Editorクラスを継承して、OnInspectorGUIその中にウィンドウを描画する必要があります。
  • 场景绘制Editorクラスを継承してから内部に描画する必要がありますOnSceneGUI
  • 属性绘制PropertyDrawerクラスを継承してから内部に描画する必要がありますOnGUI
  • 独立したインスペクター プロパティもあります。

このような分析の後、それが必要な場合は、ウィンドウを継承して描画する绘制窗口必要があることがある程度明確になります。EditorWindow类OnGUI

つまり、インスペクター ウィンドウを再描画するInspector窗口か、シーンを継承してからインスペクター ウィンドウを内側に描画し、その内側に描画するScene窗口必要がありますEditor类OnInspectorGUIOnSceneGUIScene窗口

2-3. ビューアのプロパティ

2-3-1、HideInInspector

はじめに: スクリプト内の変数へのアクセシビリティを確保しながら、パブリック メンバー変数を非表示にして、インスペクターの値が影響を及ぼさないようにすることができます。

例えば:

[HideInInspector] を追加しないでください。

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    public string Name;//注意这是public访问权限
}

ここに画像の説明を挿入します

[HideInInspector]を追加

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [HideInInspector]
    public string Name;//注意这是public访问权限
}

ここに画像の説明を挿入します

2-3-2、シリアライズフィールド

はじめに: プライベート変数を検査パネルで表示および変更できるように設定します。Unity はオブジェクトをシリアル化して保存します。プライベートであっても、シリアル化可能としてマークされた後に表示されます。パブリック変数はデフォルトでシリアル化可能です。

例えば:

[SerializeField] を追加しないでください

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    private string Name;
}

ここに画像の説明を挿入します

加[SerializeField]

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [SerializeField]
    private string Name;
}

ここに画像の説明を挿入します

2-3-3、スペース

はじめに: 現在のメンバー変数の上に 50 ピクセルの空白スペースを残します。

例えば:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [Space]
    public string Name;
}

ここに画像の説明を挿入します

2-3-4、ヘッダー

はじめに: 現在のメンバー変数の上にタイトル テキストを追加します。

例えば:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [Header("标题")]
    public string Name;
}

ここに画像の説明を挿入します

2-3-5、ツールチップ

はじめに: 変数フローティング プロンプトを追加すると、マウスを置くとプロンプトが表示されます。

例えば:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [Tooltip("输入名字")]
    public string Name;
}

ここに画像の説明を挿入します

2-3-6、レンジ

はじめに: 値の範囲を設定します。

例えば:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [Range(0,10)]
    public int Age;
}

ここに画像の説明を挿入します

2-3-7、複数行

はじめに: 入力行文字を指定します。パラメータは行数です。

例えば:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [Multiline(5)]
    public string Name;
}

ここに画像の説明を挿入します

2-3-8、テキストエリア

はじめに: デフォルトで 5 行を表示するように設定し、最大 10 行のコンテンツを表示します。表示を制御するにはスクロール バーを使用します。

例えば:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [TextArea(5,10)]//(最小行数,最大行数)
    public string Name;
}

ここに画像の説明を挿入します

2-3-9、コンテキストメニュー

はじめに: Pinion にコールバック関数を追加します。パラメータは関数名であり、この機能でマークされたメソッドを呼び出すために使用されます。

例えば:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [ContextMenu("CallBack")]
    public void CallBackFun()
    {
    
    
        Debug.Log("回调函数");
    }
}

ここに画像の説明を挿入します
ここに画像の説明を挿入します

2-3-10、ContextMenuItem

はじめに: 右クリック メニューを変数に追加します。最初のパラメータはメニュー名、2 番目のパラメータはコールバック関数です。

例えば:

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [ContextMenuItem("点击调用函数", "CallBackFun")]
    public string Name;

    public void CallBackFun()
    {
    
    
        Debug.Log("回调函数");
    }
}

ここに画像の説明を挿入します

ここに画像の説明を挿入します

2-3-11、コンポーネントメニューの追加

はじめに: エディターにコンポーネントを追加するためのメニュー項目を追加し、この属性を持つスクリプトを選択したオブジェクトに追加します。第一パラメータ:カテゴリ名/コンポーネント名、第二パラメータ:リストに表示される順序。

例えば:

using UnityEngine;

[AddComponentMenu("点击添加组件函数")]
public class Test01 : MonoBehaviour
{
    
    
}

ここに画像の説明を挿入します

2-3-12、編集モードで実行

はじめに: ライフサイクル機能はエディター状態で実行できるほか、ゲーム内でも通常に使用することができ、シーン内のオブジェクトが変更された場合やプロジェクト構成が変更された場合にはエディター内で Update() が実行されます。つまり、Start 機能と Awake 機能は、実行状態にないときでも実行できます。

例えば:

using UnityEngine;

[ExecuteInEditMode]
public class Test01 : MonoBehaviour
{
    
    
    private void Awake()
    {
    
    
        Debug.Log("Awake");
    }

    private void Start()
    {
    
    
        Debug.Log("Start");
    }

    private void Update()
    {
    
    
        Debug.Log("Update");
    }
}

ここに画像の説明を挿入します

2-3-13、必須コンポーネント

はじめに: 依存関係、バインディング。その機能は、スクリプトをゲームオブジェクトにバインドすると、依存する必要があるスクリプトも一緒にバインド (追加) されることです。

例えば:

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class Test01 : MonoBehaviour
{
    
    
}

ここに画像の説明を挿入します

2-3-14、複数のオブジェクトを編集可能

はじめに: このエディターを使用して複数のオブジェクトを選択し、それらをすべて同時に変更できることを Unity に伝えます。

例えば:

新しいスクリプト Test01.cs を作成し、コードを編集します。

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    public int m_MyInt = 75;
    public Vector3 m_MyVector = new Vector3(20, 1, 0);
    public GameObject m_MyGameObject;
}

プロジェクト ビューで、スクリプト フォルダーに新しいエディター フォルダーを作成し、このフォルダーに新しい Test01Editor.cs スクリプトを作成して、コードを編集します。

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Test01))]
public class Test01Editor : Editor
{
    
    
    SerializedProperty m_IntProp;
    SerializedProperty m_VectorProp;
    SerializedProperty m_GameObjectProp;

    void OnEnable()
    {
    
    
        m_IntProp = serializedObject.FindProperty("m_MyInt");
        m_VectorProp = serializedObject.FindProperty("m_MyVector");
        m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");
    }

    public override void OnInspectorGUI()
    {
    
    
        EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(20));
        EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));
        EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));
        serializedObject.ApplyModifiedProperties();
    }
}

この時点で、Test01 スクリプトを複数のオブジェクトにマウントし、複数のオブジェクトを選択してスクリプトを変更すると、「この時点で Test01Editor.cs スクリプトを変更しますMulti-object editing not supported.不支持多对象编辑と表示されます。
ここに画像の説明を挿入します

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Test01))]
[CanEditMultipleObjects]
public class Test01Editor : Editor
{
    
    
    SerializedProperty m_IntProp;
    SerializedProperty m_VectorProp;
    SerializedProperty m_GameObjectProp;

    void OnEnable()
    {
    
    
        m_IntProp = serializedObject.FindProperty("m_MyInt");
        m_VectorProp = serializedObject.FindProperty("m_MyVector");
        m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");
    }

    public override void OnInspectorGUI()
    {
    
    
        EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(20));
        EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));
        EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));
        serializedObject.ApplyModifiedProperties();
    }
}

複数のオブジェクトを同時に編集できます。
ここに画像の説明を挿入します

2-3-15、メニュー項目

はじめに: 上部に「ツール」メニューを表示します。

例えば:

新しいスクリプト Test01.cs を作成し、コードを編集します。

using UnityEditor;
using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    [MenuItem("Test/顶部菜单")]
    public static void CallBackFun()
    {
    
    

    }
}

ここに画像の説明を挿入します

2-3-16、カスタムエディター

はじめに: エディタをカスタマイズします。関連するコンポーネント検査パネルのプロパティを変更して再描画できます。エディタ ディレクトリにエディタ スクリプトを作成し、エディタ スクリプトを元のスクリプトに関連付けます。

例えば:

新しいスクリプト Test01.cs を作成し、コードを編集します。

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    public int m_MyInt = 75;
    public Vector3 m_MyVector = new Vector3(20, 1, 0);
    public GameObject m_MyGameObject;
}

プロジェクト ビューで、スクリプト フォルダーに新しいエディター フォルダーを作成し、このフォルダーに新しい Test01Editor.cs スクリプトを作成して、コードを編集します。

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Test01))]
public class Test01Editor : Editor
{
    
    
    SerializedProperty m_IntProp;
    SerializedProperty m_VectorProp;
    SerializedProperty m_GameObjectProp;

    void OnEnable()
    {
    
    
        m_IntProp = serializedObject.FindProperty("m_MyInt");
        m_VectorProp = serializedObject.FindProperty("m_MyVector");
        m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");
    }

    // 用于重新绘制Inspector面板中的属性
    public override void OnInspectorGUI()
    {
    
    
        // 设置高度
        EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(100));
        EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));
        EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));
        serializedObject.ApplyModifiedProperties();
    }
}

ここに画像の説明を挿入します

2-4. 窓の描画

2-4-1. ウィンドウ描画を使用する

良い!ここで立ち止まって考えてみてください。先ほどのウィンドウ描画ではどのクラスを継承すればよいか覚えていますか?



↓継承してクラス化し、その中にウィンドウを描画する
窗口绘制必要があります。EditorWindowOnGUI

継承されたEditorWindowクラスを有効にするには、エディター スクリプトにスクリプトを配置する必要があります。

Editor フォルダーに新しい Test02EditorWindow.cs スクリプトを作成し、コードを編集してみましょう。

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

public class Test02EditorWindow : EditorWindow
{
    
    
    [MenuItem("工具/创建窗口")]
    static void OpenWindow()
    {
    
    
        //泛型T 窗口类型。必须派生自 EditorWindow。
        //第一个参数设置为 true 可创建浮动实用程序窗口,设置为 false 可创建正常窗口。
        //第三个参数设置是否为窗口提供焦点(如果已存在)。
        Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "弹窗标题", true);
        window.minSize = new Vector2(40, 30);
        window.minSize = new Vector2(80, 60);
    }

    //开窗口调用
    private void OnEnable()
    {
    
    
        Debug.Log("enable");
    }

    //关窗口调用
    private void OnDisable()
    {
    
    
        Debug.Log("disable");
    }

    //窗口开启就调用
    private void Update()
    {
    
    
        Debug.Log("update");
    }

    //用于绘制窗口内容
    private void OnGUI()
    {
    
    
        if (GUILayout.Button("测试点击"))
        {
    
    
            Debug.Log("测试点击");
        }
    }

    //场景结构发生变化,执行回调函数
    private void OnHierarchyChange()
    {
    
    
        Debug.Log("hierarchy");
    }

    //项目结构发生变化,执行回调函数
    private void OnProjectChange()
    {
    
    
        Debug.Log("project");
    }

    //选中物体发生变化,执行回调函数
    private void OnSelectionChange()
    {
    
    
        //获取当前选中的物体的名称
        Debug.Log(Selection.activeGameObject.name);
    }
}

エディターがコンパイルされて渡されると、エディターのメニュー バーに表示されます工具→创建窗口
ここに画像の説明を挿入します
これはレンダリングされたウィンドウです。
ここに画像の説明を挿入します
描画は OnGUI で行われ、描画された UI も OnGUI でサポートされている UI です。

OnGUI の使用方法についてはここでは詳しく説明しません。

以下に、より一般的に使用される小さな関数コードをいくつか示します。

2-4-2. ミップマップを有効にして非 2 乗マップを確認します。
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class Test02EditorWindow : EditorWindow
{
    
    
    [MenuItem("工具/检查开启mipmap的非2的幂贴图")]
    static void OpenWindow()
    {
    
    
        Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "弹窗标题", true);
    }

    //用于绘制窗口内容
    private void OnGUI()
    {
    
    
        if (GUILayout.Button("检查开启mipmap的非2的幂贴图"))
        {
    
    
            CheckNPOT();
        }
    }

    private void CheckNPOT()
    {
    
    
        List<string> files = AssetDatabase.FindAssets("t:Texture").Select(AssetDatabase.GUIDToAssetPath).ToList();
        List<string> outputList = new List<string>();
        foreach (var file in files)
        {
    
    
            TextureImporter textureImporter = AssetImporter.GetAtPath(file) as TextureImporter;
            if (textureImporter)
            {
    
    
                //贴图为Sprite或设置了2的幂scale
                if (textureImporter.textureType == TextureImporterType.Sprite || textureImporter.npotScale != TextureImporterNPOTScale.None)
                {
    
    
                    continue;
                }
                //贴图长宽均为2的幂
                textureImporter.GetSourceTextureWidthAndHeight(out var width, out var height);
                if (IsPowerOfTwo(width) && IsPowerOfTwo(height))
                {
    
    
                    continue;
                }
                if (textureImporter.mipmapEnabled)
                {
    
    
                    outputList.Add(file);
                    Debug.Log(file);
                }
            }
        }
        WriteLog("NPOT.log", outputList);
    }

    private void WriteLog(string fileName, List<string> outputList)
    {
    
    
        if (!Directory.Exists(@"Logs"))
        {
    
    
            Directory.CreateDirectory(@"Logs");
        }
        if (!File.Exists("Logs/" + fileName))
        {
    
    
            using (FileStream fs = new FileStream("Logs/" + fileName, FileMode.CreateNew))
            {
    
    
            }
        }
        File.WriteAllLines("Logs/" + fileName, outputList);
    }

    private bool IsPowerOfTwo(int value)
    {
    
    
        return (value & (value - 1)) == 0;
    }
}
2-4-3. 選択したフォルダー配下の全リソースを取得
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class Test02EditorWindow : EditorWindow
{
    
    
    [MenuItem("工具/获取选中文件夹下的所有资源")]
    static void OpenWindow()
    {
    
    
        Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "获取选中文件夹下的所有资源", true);
    }

    //用于绘制窗口内容
    private void OnGUI()
    {
    
    
        if (GUILayout.Button("获取选中文件夹下的所有资源"))
        {
    
    
            List<string> pathList = new List<string>();
            Object[] m_objects = Selection.GetFiltered(typeof(Object), SelectionMode.Unfiltered | SelectionMode.DeepAssets);
            foreach (var obj in m_objects)
            {
    
    
                string path = AssetDatabase.GetAssetPath(obj);
                if (!pathList.Contains(path))
                {
    
    
                    pathList.Add(path);
                }
            }
            foreach (var item in pathList)
            {
    
    
                Debug.Log(item);
            }
        }
    }
}
2-4-4. プレハブ内の不足しているスクリプトを削除する
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class Test02EditorWindow : EditorWindow
{
    
    
    [MenuItem("工具/删除prefab中missing的script")]
    static void OpenWindow()
    {
    
    
        Test02EditorWindow window = GetWindow<Test02EditorWindow>(false, "删除prefab中missing的script", true);
    }

    //用于绘制窗口内容
    private void OnGUI()
    {
    
    
        if (GUILayout.Button("删除prefab中missing的script"))
        {
    
    
            List<string> logList = new List<string>();
            List<string> prefabPathList = new List<string>();
            foreach (var prefabPath in prefabPathList)
            {
    
    
                if (EditorUtility.DisplayCancelableProgressBar("Processing", string.Format("{0} {1}/{2}",
                       prefabPath, prefabPathList.IndexOf(prefabPath), prefabPathList.Count),
                       prefabPathList.IndexOf(prefabPath) / (float)prefabPathList.Count))
                {
    
    
                    EditorUtility.ClearProgressBar();
                    return;
                }
                GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
                if (go)
                {
    
    
                    int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(go);
                    if (count > 0)
                    {
    
    
                        GameObjectUtility.RemoveMonoBehavioursWithMissingScript(go);
                        logList.Add(string.Format("删除了{0}中的{1}个missing的script", prefabPath, count));
                    }
                }
            }
            EditorUtility.ClearProgressBar();
        }
    }
}

引き続き、追加歓迎です。

2-5. 検査官の図面

通常のクラスをエディタ ツールに関連付けて、特別な機能を実装します。

たとえば、Test01.cs クラスは共通クラスです。Editor フォルダーに新しい編集クラス Test01Editor.cs を作成します。Test01Editor.cs はカスタム エディターであり、Test01Editor.cs のOnInspectorGUI関数でプロパティを変更して描画します。

次に、使用方法を示す古い例を示します。

例えば:

新しいスクリプト Test01.cs を作成し、コードを編集します。

using UnityEngine;

public class Test01 : MonoBehaviour
{
    
    
    public int m_MyInt = 75;
    public Vector3 m_MyVector = new Vector3(20, 1, 0);
    public GameObject m_MyGameObject;
}

デフォルトは次のとおりです。

ここに画像の説明を挿入します

プロジェクト ビューで、スクリプト フォルダーに新しいエディター フォルダーを作成し、このフォルダーに新しい Test01Editor.cs スクリプトを作成して、コードを編集します。

using UnityEngine;
using UnityEditor;

//CustomEditor 属性告知 Unity 应该作为哪个组件的编辑器。
[CustomEditor(typeof(Test01))]
public class Test01Editor : Editor
{
    
    
    SerializedProperty m_IntProp;
    SerializedProperty m_VectorProp;
    SerializedProperty m_GameObjectProp;

    void OnEnable()
    {
    
    
        m_IntProp = serializedObject.FindProperty("m_MyInt");
        m_VectorProp = serializedObject.FindProperty("m_MyVector");
        m_GameObjectProp = serializedObject.FindProperty("m_MyGameObject");
    }

    // 用于重新绘制Inspector面板中的属性
    public override void OnInspectorGUI()
    {
    
    
        // 设置高度
        EditorGUILayout.PropertyField(m_IntProp, new GUIContent("Int Field"), GUILayout.Height(100));
        EditorGUILayout.PropertyField(m_VectorProp, new GUIContent("Vector Object"));
        EditorGUILayout.PropertyField(m_GameObjectProp, new GUIContent("Game Object"));
        serializedObject.ApplyModifiedProperties();
    }
}

変更後は次のようになります。
ここに画像の説明を挿入します

2-6.情景描画

OnSceneGUI実行方法は と非常に似ていますOnInspectorGUIが、シーン ビューで実行されます。

独自の編集コントロールの作成を容易にするために、Handles クラスで定義された関数を使用できます。

すべての機能は 3D モードのシーン ビュー用に設計されています。

例:
新しいスクリプト Test01.cs を作成し、コードを編集します。

using UnityEditor;
using UnityEngine;

[ExecuteInEditMode]
public class Test01 : MonoBehaviour
{
    
    
    public Vector3 lookAtPoint = Vector3.zero;

    public void Update()
    {
    
    
        transform.LookAt(lookAtPoint);
    }
}

プロジェクト ビューで、スクリプト フォルダーに新しいエディター フォルダーを作成し、このフォルダーに新しい Test01Editor.cs スクリプトを作成して、コードを編集します。

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Test01))]
[CanEditMultipleObjects]
public class Test01Editor : Editor
{
    
    
    SerializedProperty lookAtPoint;

    void OnEnable()
    {
    
    
        lookAtPoint = serializedObject.FindProperty("lookAtPoint");
    }

    public void OnSceneGUI()
    {
    
    
        var t = (target as Test01);

        EditorGUI.BeginChangeCheck();
        Vector3 pos = Handles.PositionHandle(t.lookAtPoint, Quaternion.identity);
        if (EditorGUI.EndChangeCheck())
        {
    
    
            Undo.RecordObject(target, "Move point");
            t.lookAtPoint = pos;
            t.Update();
        }
    }
}

ここに画像の説明を挿入します

2-7. 物件図面

カスタム プロパティ ペインタの派生元となる基本クラス。この基本クラスを使用して、独自の Serializable クラスまたはカスタム PropertyAttributes を持つスクリプト変数のカスタム ペインターを作成します。

PropertyDrawer には 2 つの用途があります。 Serializable クラスの各インスタンスの GUI をカスタマイズします。カスタム PropertyAttributes を使用して、スクリプト メンバーの GUI をカスタマイズします。カスタム Serializable クラスがある場合は、PropertyDrawer を使用してインスペクターでの外観を制御できます。

たとえば、
新しいスクリプトを作成しRecipe.cs、コードを編集します。

using System;
using UnityEngine;


public enum IngredientUnit {
    
     Spoon,Cup,Bowl,Piece}

[Serializable]
public class Ingredient
{
    
    
    public string name;
    public int amount = 1;
    public IngredientUnit unit;
}

public class Recipe : MonoBehaviour
{
    
    
    public Ingredient potionResult;
    public Ingredient[] pointIngredients;
}

Editor フォルダーに新しいスクリプト IngredientDrawerUIE.cs を作成し、コードを編集します。

using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawerUIE : PropertyDrawer
{
    
    
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
    
    
        EditorGUI.BeginProperty(position, label, property);

        // label
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

        // 控制字段缩进 设置为不缩进
        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        // 计算矩形范围
        var nameRect = new Rect(position.x, position.y, 30, position.height); 
        var amountRect = new Rect(position.x + 35, position.y, 50, position.height);
        var unitRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);


        // 绘制字段
        EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);
        EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);
        EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);
        

        // 控制字段缩进 设置为原来的数值
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

Recipe スクリプトをオブジェクトに追加して効果を確認します。
ここに画像の説明を挿入します

2-8. 参考リンク

Unity学習ノートエディターの開発

Unity エディター ツールの開発経験の概要

カスタムエディター

プロパティペインター

3. 追記

わかりました。結論は。

Unity3D エディターの開発は Unity3D エディターに基づいており、開発を支援するいくつかの小さなツールを作成します。

检视器属性それは、 、界面绘制、 の属性绘制3 つの主要な側面に分けることができます。

检视器属性セクション 2-3 を参照してください。

界面绘制窗口绘制检视器界面绘制に分けることができます场景绘制

窗口绘制その場合は、EditorWindow クラスを継承して、OnGUI で UI をレンダリングする必要があります。

检视器界面绘制Editor クラスを継承してからOnInspectorGUI描画する必要があります。

场景绘制Editor クラスを継承してからOnSceneGUI描画する必要があります。

属性绘制PropertyDrawer クラスを継承してからOnGUI描画する必要があります。

この記事が役に立ったと思われる場合は、忘れずに「フォロー」をクリックして、Unity に関するさらに役立つ記事を共有し続けてください。


あなたの「いいね!」はブロガーへのサポートとなります。ご質問がございましたら、メッセージを残してください:

ブロガーのホームページに連絡先情報が記載されています。

ブロガーには、あなたが発見できるのを待っているお宝記事もたくさんあります。

カラム 方向 導入
Unity3D は小さなゲームを開発します ミニゲーム開発チュートリアル Unity3D エンジンを使用して開発されたいくつかの小さなゲームを共有し、小さなゲームの作成に関するいくつかのチュートリアルを共有します。
Unity3D の入門から上級まで はじめる Unityを独学することでインスピレーションを得て、Unityをゼロから学習するルートをまとめ、C#とUnityの知識を身につけます。
Unity3D UGUI ウグイ Unity の UI システム UGUI を徹底的に分析し、UGUI の基本的な制御から始めて、UGUI の原理と使い方を包括的に説明します。
Unity3D でのデータの読み取り ファイルの読み取り Unity3D を使用して、txt ドキュメント、json ドキュメント、xml ドキュメント、csv ドキュメント、および Excel ドキュメントを読み取ります。
Unity3Dデータ収集 データ収集 配列コレクション: 配列、リスト、辞書、スタック、リンク リストなどのデータ コレクションに関する知識の共有。
Unity3D VR/AR(仮想シミュレーション)開発 バーチャルリアリティ ブロガーの一般的な仮想シミュレーションのニーズをまとめ、事例を交えて説明します。
Unity3D プラグイン プラグイン 主にUnity開発で使用するプラグインの活用法やプラグインの紹介などをシェアします。
Unity3Dの日々の開発 日々の記録 主にブロガーが日々の開発で使用する手法やテクニック、開発アイデア、コード共有など。
Unity3D の毎日のバグ 日々の記録 Unity3D エディターを使用してプロジェクト開発中に遭遇したバグや落とし穴を記録し、将来の世代が参照できるようにします。

おすすめ

転載: blog.csdn.net/q764424567/article/details/133563242