サーチ…


構文

  • [MenuItem(string itemName)]
  • [MenuItem(string itemName、bool isValidateFunction)]
  • [MenuItem(string itemName、bool isValidateFunction、int priority)]
  • [ContextMenu(文字列名)]
  • [ContextMenuItem(文字列名、文字列関数)]
  • [DrawGizmo(ギズモタイプギズモ)]
  • [DrawGizmo(GizmoTypeギズモ、タイプdrawnGizmoType)]

パラメーター

パラメータ詳細
MenuCommand MenuCommandは、MenuItemのコンテキストを抽出するために使用されます。
MenuCommand.context メニューコマンドの対象となるオブジェクト
MenuCommand.userData カスタム情報をメニュー項目に渡すためのint

カスタムインスペクタ

カスタムインスペクタを使用すると、インスペクタでスクリプトを描画する方法を変更できます。場合によっては、カスタムプロパティドロワーではできない追加の情報をインスペクターでスクリプトに追加することもできます。

以下に、カスタムインスペクタを使用してより有用な情報を表示できるカスタムオブジェクトの簡単な例を示します。

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class InspectorExample : MonoBehaviour {

    public int Level;
    public float BaseDamage;

    public float DamageBonus {
        get {
            return Level / 100f * 50;
        }
    }

    public float ActualDamage {
        get {
            return BaseDamage + DamageBonus;
        }
    }
}

#if UNITY_EDITOR
[CustomEditor( typeof( InspectorExample ) )]
public class CustomInspector : Editor {

    public override void OnInspectorGUI() {
        base.OnInspectorGUI();

        var ie = (InspectorExample)target;

        EditorGUILayout.LabelField( "Damage Bonus", ie.DamageBonus.ToString() );
        EditorGUILayout.LabelField( "Actual Damage", ie.ActualDamage.ToString() );
    }
}
#endif

まず、いくつかのフィールドでカスタム動作を定義します

public class InspectorExample : MonoBehaviour {
    public int Level;
    public float BaseDamage;
}

「インスペクタ」ウインドウでスクリプトを表示しているときは、上記のフィールドが自動的に(カスタムインスペクタなしで)描画されます。

public float DamageBonus {
    get {
        return Level / 100f * 50;
    }
}

public float ActualDamage {
    get {
        return BaseDamage + DamageBonus;
    }
}

これらのプロパティはUnityによって自動的に描画されません。インスペクタビューでこれらのプロパティを表示するには、カスタムインスペクタを使用する必要があります。

まず、このようなカスタムインスペクタを定義する必要があります

[CustomEditor( typeof( InspectorExample ) )]
public class CustomInspector : Editor {

カスタムインスペクタはエディタから派生し、 CustomEditor属性を必要とします。属性のパラメータは、このカスタムインスペクタを使用するオブジェクトのタイプです。

次は、OnInspectorGUIメソッドです。このメソッドは、スクリプトがインスペクタウィンドウに表示されるたびに呼び出されます。

public override void OnInspectorGUI() {
    base.OnInspectorGUI();
}

base.OnInspectorGUI()を呼び出して、Unityがスクリプト内の他のフィールドを処理できるようにします。私たちがこれを呼ぶつもりならば、自分たちでもっと仕事をしなければならないでしょう。

次は、私たちが見せたいカスタムプロパティです

var ie = (InspectorExample)target;

EditorGUILayout.LabelField( "Damage Bonus", ie.DamageBonus.ToString() );
EditorGUILayout.LabelField( "Actual Damage", ie.ActualDamage.ToString() );

カスタム型にキャストされたターゲットを保持する一時変数を作成する必要があります(ターゲットはエディタから派生したものです)。

次に、プロパティを描画する方法を決定できます。この場合、値を表示して編集することができないので、2つのラベルがあれば十分です。

結果

前の結果

後の結果

カスタムプロパティドロワ

場合によっては、データを含むカスタムオブジェクトがありますが、MonoBehaviourから派生しません。これらのオブジェクトをMonoBehaviourクラスのフィールドとして追加すると、オブジェクトの型に独自のカスタムプロパティードロワーを作成しない限り、視覚的な効果はありません。

以下は、MonoBehaviourに追加されたカスタムオブジェクトの簡単な例と、カスタムオブジェクトのカスタムプロパティドロワーです。

public enum Gender {
    Male,
    Female,
    Other
}

// Needs the Serializable attribute otherwise the CustomPropertyDrawer wont be used
[Serializable]
public class UserInfo {
    public string Name;
    public int Age;
    public Gender Gender;
}

// The class that you can attach to a GameObject
public class PropertyDrawerExample : MonoBehaviour {
    public UserInfo UInfo;
}

[CustomPropertyDrawer( typeof( UserInfo ) )]
public class UserInfoDrawer : PropertyDrawer {

    public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) {
        // The 6 comes from extra spacing between the fields (2px each)
        return EditorGUIUtility.singleLineHeight * 4 + 6;
    }

    public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
        EditorGUI.BeginProperty( position, label, property );

        EditorGUI.LabelField( position, label );

        var nameRect = new Rect( position.x, position.y + 18, position.width, 16 );
        var ageRect = new Rect( position.x, position.y + 36, position.width, 16 );
        var genderRect = new Rect( position.x, position.y + 54, position.width, 16 );

        EditorGUI.indentLevel++;

        EditorGUI.PropertyField( nameRect, property.FindPropertyRelative( "Name" ) );
        EditorGUI.PropertyField( ageRect, property.FindPropertyRelative( "Age" ) );
        EditorGUI.PropertyField( genderRect, property.FindPropertyRelative( "Gender" ) );

        EditorGUI.indentLevel--;

        EditorGUI.EndProperty();
    }
}

まず、すべての要件を持つカスタムオブジェクトを定義します。ユーザーを説明する単純なクラスです。このクラスはPropertyDrawerExampleクラスで使用され、GameObjectに追加できます。

public enum Gender {
    Male,
    Female,
    Other
}

[Serializable]
public class UserInfo {
    public string Name;
    public int Age;
    public Gender Gender;
}

public class PropertyDrawerExample : MonoBehaviour {
    public UserInfo UInfo;
}

カスタムクラスにはSerializable属性が必要です。そうでなければCustomPropertyDrawerは使用されません

次はCustomPropertyDrawerです

まず、PropertyDrawerから派生したクラスを定義する必要があります。クラス定義には、CustomPropertyDrawer属性も必要です。渡されるパラメーターは、このドロワーに使用するオブジェクトのタイプです。

[CustomPropertyDrawer( typeof( UserInfo ) )]
public class UserInfoDrawer : PropertyDrawer {

次に、GetPropertyHeight関数をオーバーライドします。これにより、プロパティのカスタム高さを定義することができます。この場合、私たちの財産にはラベル、名前、年齢、性別の4つの部分があることがわかります。このため、 EditorGUIUtility.singleLineHeight * 4を使用して、各フィールドに2ピクセルの間隔を空けたいので、別の6ピクセルを追加します。

public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) {
    return EditorGUIUtility.singleLineHeight * 4 + 6;
}

次は実際のOnGUIメソッドです。我々は([...])EditorGUI.BeginPropertyでそれを始めるとEditorGUI.EndProperty()で関数を終了します。このプロパティがプレハブの一部である場合、実際のプレハブオーバーライドロジックは、これらの2つのメソッドの間のすべてに対して機能するように、これを行います。

public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
    EditorGUI.BeginProperty( position, label, property );

その後、フィールドの名前を含むラベルを表示し、フィールドの長方形を定義します。

EditorGUI.LabelField( position, label );

var nameRect = new Rect( position.x, position.y + 18, position.width, 16 );
var ageRect = new Rect( position.x, position.y + 36, position.width, 16 );
var genderRect = new Rect( position.x, position.y + 54, position.width, 16 );

すべてのフィールドは16 + 2ピクセルの間隔を持ち、高さは16です(これはEditorGUIUtility.singleLineHeightと同じです)

次に、1つのタブでUIをインデントし、少し面白いレイアウトを表示し、プロパティを表示し、GUIをインデントしないで、 EditorGUI.EndPropertyで終了します

EditorGUI.indentLevel++;

EditorGUI.PropertyField( nameRect, property.FindPropertyRelative( "Name" ) );
EditorGUI.PropertyField( ageRect, property.FindPropertyRelative( "Age" ) );
EditorGUI.PropertyField( genderRect, property.FindPropertyRelative( "Gender" ) );

EditorGUI.indentLevel--;

EditorGUI.EndProperty();

フィールドの表示には、位置の矩形を必要とするEditorGUI.PropertyFieldと、プロパティを表示するSerializedPropertyを使用します。 OnGUI関数で渡されたプロパティでFindPropertyRelative( "...")を呼び出すことによってプロパティを取得します。これらは大文字と小文字を区別し、非公開のプロパティが見つからないことに注意してください。

この例では、property.FindPropertyRelative( "...")からの戻り値を保存していません。不要な呼び出しを防ぐには、これらのクラスをプライベートフィールドに保存する必要があります

結果

前の結果

後の結果

メニュー項目

メニュー項目は、カスタムアクションをエディタに追加する優れた方法です。メニューバーにメニュー項目を追加したり、特定のコンポーネントのコンテキストをクリックしたり、スクリプト内のフィールドをコンテキストクリックしたりすることもできます。

以下は、メニュー項目を適用する方法の例です。

public class MenuItemsExample : MonoBehaviour {

    [MenuItem( "Example/DoSomething %#&d" )]
    private static void DoSomething() {
        // Execute some code
    }

    [MenuItem( "Example/DoAnotherThing", true )]
    private static bool DoAnotherThingValidator() {
        return Selection.gameObjects.Length > 0;
    }

    [MenuItem( "Example/DoAnotherThing _PGUP", false )]
    private static void DoAnotherThing() {
        // Execute some code
    }

    [MenuItem( "Example/DoOne %a", false, 1 )]
    private static void DoOne() {
        // Execute some code
    }

    [MenuItem( "Example/DoTwo #b", false, 2 )]
    private static void DoTwo() {
        // Execute some code
    }

    [MenuItem( "Example/DoFurther &c", false, 13 )]
    private static void DoFurther() {
        // Execute some code
    }

    [MenuItem( "CONTEXT/Camera/DoCameraThing" )]
    private static void DoCameraThing( MenuCommand cmd ) {
        // Execute some code
    }

    [ContextMenu( "ContextSomething" )]
    private void ContentSomething() {
        // Execute some code
    }

    [ContextMenuItem( "Reset", "ResetDate" )]
    [ContextMenuItem( "Set to Now", "SetDateToNow" )]
    public string Date = "";

    public void ResetDate() {
        Date = "";
    }

    public void SetDateToNow() {
        Date = DateTime.Now.ToString();
    }
}

それはこのように見える

ツールバーメニュー

基本的なメニュー項目を見てみましょう。以下に示すように、メニュー項目のタイトルとして文字列を渡すMenuItem属性を持つ静的関数を定義する必要があります。名前に/を追加することで、複数レベルのメニュー項目を深く配置することができます。

[MenuItem( "Example/DoSomething %#&d" )]
private static void DoSomething() {
    // Execute some code
}

トップレベルにメニューアイテムを置くことはできません。あなたのメニュー項目はサブメニュー内にある必要があります!

MenuItemの名前の最後にある特殊文字はショートカットキーのためのものですが、これらは必須ではありません。

ショートカットキーに使用できる特殊文字は次のとおりです。

  • % - WindowsではCtrl、OS XではCmd
  • # - シフト
  • & - Alt

つまり、ショートカット%#&dは、Windowsではctrl + shift + alt + Dを表し、OS Xではcmd + shift + alt + Dを表します。

特別なキーなしでショートカットを使用したい場合は、例えば 'D'キーだけを使用する場合は、使用したいショートカットキーの前に_(アンダースコア)文字を付けることができます。

サポートされている他のいくつかの特別なキーがあります:

  • 左、右、上、下 - 矢印キー用
  • F1 ... F12 - ファンクションキー用
  • HOME、END、PGUP、PGDN - ナビゲーションキー用

ショートカットキーは、他のテキストとスペースで区切る必要があります

次に、バリデーターメニュー項目です。バリデーターメニュー項目では、条件が満たされていないときにメニュー項目を無効にすることができます(グレー表示、クリック不可)。あなたのメニュー項目は現在のGameObjectsの選択肢に作用し、Validatorメニュー項目で確認できます。

[MenuItem( "Example/DoAnotherThing", true )]
private static bool DoAnotherThingValidator() {
    return Selection.gameObjects.Length > 0;
}

[MenuItem( "Example/DoAnotherThing _PGUP", false )]
private static void DoAnotherThing() {
    // Execute some code
}

バリデーターメニュー項目を機能させるには、MenuItem属性と同じ名前(ショートカットキーは関係ありません)の両方で、2つの静的関数を作成する必要があります。それらの違いは、ブール値パラメータを渡すことによって、バリデータ関数としてマークすることです。

また、優先度を追加してメニュー項目の順序を定義することもできます。優先度は、3番目のパラメータとして渡す整数によって定義されます。リスト内の数字が小さいほど、リスト内の数字が大きくなります。 2つのメニュー項目の間にセパレータを追加するには、メニュー項目の優先順位の間に10桁以上あることを確認します。

[MenuItem( "Example/DoOne %a", false, 1 )]
private static void DoOne() {
    // Execute some code
}

[MenuItem( "Example/DoTwo #b", false, 2 )]
private static void DoTwo() {
    // Execute some code
}

[MenuItem( "Example/DoFurther &c", false, 13 )]
private static void DoFurther() {
    // Execute some code
}

優先順位付けされた項目と優先順位のない項目が組み合わされたメニューリストがある場合、優先順位のない項目は優先項目と切り離されます。

次に、既存のコンポーネントのコンテキストメニューにメニュー項目を追加します。 CONTEXT(大文字小文字を区別する)でMenuItemの名前を開始し、関数にMenuCommandパラメータを取り込ませる必要があります。

次のスニペットは、カメラコンポーネントにコンテキストメニュー項目を追加します。

[MenuItem( "CONTEXT/Camera/DoCameraThing" )]
private static void DoCameraThing( MenuCommand cmd ) {
    // Execute some code
}

それはこのように見える

カメラコンテキストメニュー項目

MenuCommandパラメータを使用すると、コンポーネントの値と、それとともに送信されるすべてのユーザーデータにアクセスできます。

また、ContextMenu属性を使用して、独自のコンポーネントにコンテキストメニュー項目を追加することもできます。この属性は、名前または検証や優先順位がなく、非静的メソッドの一部でなければなりません。

[ContextMenu( "ContextSomething" )]
private void ContentSomething() {
    // Execute some code
}

それはこのように見える

カスタムコンテキストメニュー項目

コンテキストメニュー項目は、独自のコンポーネントのフィールドに追加することもできます。これらのメニュー項目は、それらが属する項目をコンテキストクリックすると表示され、その項目で定義したメソッドを実行できます。このようにして、以下に示すように、例えばデフォルト値または現在の日付を追加することができます。

[ContextMenuItem( "Reset", "ResetDate" )]
[ContextMenuItem( "Set to Now", "SetDateToNow" )]
public string Date = "";

public void ResetDate() {
    Date = "";
}

public void SetDateToNow() {
    Date = DateTime.Now.ToString();
}

それはこのように見える

ここに画像の説明を入力

ギズモ

ギズモは、シーンビューで図形を描画するために使用されます。これらの図形を使用して、ゲームオブジェクトに関する追加の情報(例えば、彼らが持つ錐台または検出範囲)を描くことができます。

以下は、これを行う方法の2つの例です

例1

この例では、 OnDrawGizmosメソッドOnDrawGizmosSelected (magic)メソッドを使用しています。

public class GizmoExample : MonoBehaviour {

    public float GetDetectionRadius() {
        return 12.5f;
    }

    public float GetFOV() {
        return 25f;
    }

    public float GetMaxRange() {
        return 6.5f;
    }

    public float GetMinRange() {
        return 0;
    }

    public float GetAspect() {
        return 2.5f;
    }

    public void OnDrawGizmos() {
        var gizmoMatrix = Gizmos.matrix;
        var gizmoColor = Gizmos.color;

        Gizmos.matrix = Matrix4x4.TRS( transform.position, transform.rotation, transform.lossyScale );
        Gizmos.color = Color.red;
        Gizmos.DrawFrustum( Vector3.zero, GetFOV(), GetMaxRange(), GetMinRange(), GetAspect() );

        Gizmos.matrix = gizmoMatrix;
        Gizmos.color = gizmoColor;
    }

    public void OnDrawGizmosSelected() {
        Handles.DrawWireDisc( transform.position, Vector3.up, GetDetectionRadius() );
    }
}

この例では、オブジェクトがアクティブなときに描く(OnDrawGizmos)と、オブジェクトが階層で選択されているとき(OnDrawGizmosSelected)の2つの方法を使用してギズモを描画します。

public void OnDrawGizmos() {
    var gizmoMatrix = Gizmos.matrix;
    var gizmoColor = Gizmos.color;

    Gizmos.matrix = Matrix4x4.TRS( transform.position, transform.rotation, transform.lossyScale );
    Gizmos.color = Color.red;
    Gizmos.DrawFrustum( Vector3.zero, GetFOV(), GetMaxRange(), GetMinRange(), GetAspect() );

    Gizmos.matrix = gizmoMatrix;
    Gizmos.color = gizmoColor;
}

最初に、ギズモ行列と色を保存します。これは変更する予定であり、他のギズモ描画に影響を与えないようにするために元に戻したいからです。

次にオブジェクトにある錐台を描きたいが、Gizmosの行列を位置、回転、スケールに合わせて変更する必要がある。我々はまた、Gizmosの色を赤に設定して、錐台を強調しました。これが完了すると、 Gizmos.DrawFrustumを呼び出してシーンビューに錐台を描画することができます。

描画したいものを描画したら、ギズモの行列と色をリセットします。

public void OnDrawGizmosSelected() {
    Handles.DrawWireDisc( transform.position, Vector3.up, GetDetectionRadius() );
}

また、GameObjectを選択するときに検出範囲を描きたい。 Gizmosクラスにはディスクのメソッドがないため、これはHandlesクラスを通して行われます。

この形式の描画ギズモを使用すると、次のような結果が得られます。

例2

この例では、 DrawGizmo属性を使用してます。

public class GizmoDrawerExample {

    [DrawGizmo( GizmoType.Selected | GizmoType.NonSelected, typeof( GizmoExample ) )]
    public static void DrawGizmo( GizmoExample obj, GizmoType type ) {
        var gizmoMatrix = Gizmos.matrix;
        var gizmoColor = Gizmos.color;

        Gizmos.matrix = Matrix4x4.TRS( obj.transform.position, obj.transform.rotation, obj.transform.lossyScale );
        Gizmos.color = Color.red;
        Gizmos.DrawFrustum( Vector3.zero, obj.GetFOV(), obj.GetMaxRange(), obj.GetMinRange(), obj.GetAspect() );

        Gizmos.matrix = gizmoMatrix;
        Gizmos.color = gizmoColor;

        if ( ( type & GizmoType.Selected ) == GizmoType.Selected ) {
            Handles.DrawWireDisc( obj.transform.position, Vector3.up, obj.GetDetectionRadius() );
        }
    }
}

この方法で、スクリプトからギズモ呼び出しを分離することができます。これの大部分は、2つのことを除いて、他の例と同じコードを使用します。

[DrawGizmo( GizmoType.Selected | GizmoType.NonSelected, typeof( GizmoExample ) )]
public static void DrawGizmo( GizmoExample obj, GizmoType type ) {

最初のパラメーターとしてEnum GizmoTypeをとり、2番目のパラメーターとしてTypeをとるDrawGizmo属性を使用する必要があります。タイプは、ギズモを描画するために使用するタイプでなければなりません。

ギズモを描画する方法は、静的、パブリックまたは非パブリックである必要があり、任意の名前を付けることができます。最初のパラメータは型で、属性の2番目のパラメータとして渡される型と一致する必要があります。2番目のパラメータは、オブジェクトの現在の状態を示すenum GizmoTypeです。

if ( ( type & GizmoType.Selected ) == GizmoType.Selected ) {
    Handles.DrawWireDisc( obj.transform.position, Vector3.up, obj.GetDetectionRadius() );
}

もう1つの違いは、オブジェクトのGizmoTypeが何であるかを調べるために、必要なパラメータとタイプをANDでチェックする必要があることです。

結果

選択されていません

例1は選択されていません

選択された

選択された1つの例

エディタウィンドウ

なぜエディタウィンドウ?

ご覧のように、カスタムインスペクタで多くのことを行うことができます(カスタムインスペクタの内容がわからない場合は、こちらの例を参照してください)。http : //www.riptutorial.com/unity3d/topic/2506エディタを拡張することができますが、ある時点では、設定パネルやカスタマイズされたアセットパレットを実装することもできます。その場合、 EditorWindowを使用します。Unity UI自体はエディタウィンドウで構成され、 (通常はトップバーを介して)、タブなど

基本的なEditorWindowを作成する

簡単な例

カスタムエディタウィンドウの作成はかなり簡単です。 EditorWindowクラスを拡張し、Init()メソッドとOnGUI()メソッドを使用するだけです。ここに簡単な例があります:

using UnityEngine;
using UnityEditor;

public class CustomWindow : EditorWindow
{
    // Add menu named "Custom Window" to the Window menu
    [MenuItem("Window/Custom Window")]
    static void Init()
    {
        // Get existing open window or if none, make a new one:
        CustomWindow window = (CustomWindow) EditorWindow.GetWindow(typeof(CustomWindow));
        window.Show();
    }

    void OnGUI()
    {
        GUILayout.Label("This is a custom Editor Window", EditorStyles.boldLabel);
    }
}

3つの重要なポイントは次のとおりです。

  1. EditorWindowを拡張することを忘れないでください
  2. この例に示すようにInit()を使用してください。 EditorWindow.GetWindowは、CustomWindowがすでに作成されているかどうかを確認しています。そうでない場合は、新しいインスタンスが作成されます。これを使用すると、同時に複数のウィンドウインスタンスを持たないようにすることができます
  3. いつものようにOnGUI()を使ってウィンドウに情報を表示する

最終的な結果は次のようになります。

シンプルなカスタムEditorWindow

より深く進む

もちろん、このEditorWindowを使用していくつかのアセットを管理または変更したいと思うでしょう。次に、 Selectionクラスを使用して(アクティブなSelectionを取得する)、選択されたアセットプロパティをSerializedObjectおよびSerializedPropertyで変更する例を示します。

    using System.Linq;
    using UnityEngine;
    using UnityEditor;
    
    public class CustomWindow : EditorWindow
    {
        private AnimationClip _animationClip;
        private SerializedObject _serializedClip;
        private SerializedProperty _events;
    
        private string _text = "Hello World";
    
        // Add menu named "Custom Window" to the Window menu
        [MenuItem("Window/Custom Window")]
        static void Init()
        {
            // Get existing open window or if none, make a new one:
            CustomWindow window = (CustomWindow) EditorWindow.GetWindow(typeof(CustomWindow));
            window.Show();
        }
    
        void OnGUI()
        {
            GUILayout.Label("This is a custom Editor Window", EditorStyles.boldLabel);
    
            // You can use EditorGUI, EditorGUILayout and GUILayout classes to display anything you want
            // A TextField example
            _text = EditorGUILayout.TextField("Text Field", _text);
    
            // Note that you can modify an asset or a gameobject using an EditorWindow. Here is a quick example with an AnimationClip asset
            // The _animationClip, _serializedClip and _events are set in OnSelectionChange()
    
            if (_animationClip == null || _serializedClip == null || _events == null) return;
    
            // We can modify our serializedClip like we would do in a Custom Inspector. For example we can grab its events and display their information
    
            GUILayout.Label(_animationClip.name, EditorStyles.boldLabel);
    
            for (var i = 0; i < _events.arraySize; i++)
            {
                EditorGUILayout.BeginVertical();
    
                EditorGUILayout.LabelField(
                    "Event : " + _events.GetArrayElementAtIndex(i).FindPropertyRelative("functionName").stringValue,
                    EditorStyles.boldLabel);
                EditorGUILayout.PropertyField(_events.GetArrayElementAtIndex(i).FindPropertyRelative("time"), true,
                    GUILayout.ExpandWidth(true));
                EditorGUILayout.PropertyField(_events.GetArrayElementAtIndex(i).FindPropertyRelative("functionName"),
                    true, GUILayout.ExpandWidth(true));
                EditorGUILayout.PropertyField(_events.GetArrayElementAtIndex(i).FindPropertyRelative("floatParameter"),
                    true, GUILayout.ExpandWidth(true));
                EditorGUILayout.PropertyField(_events.GetArrayElementAtIndex(i).FindPropertyRelative("intParameter"),
                    true, GUILayout.ExpandWidth(true));
                EditorGUILayout.PropertyField(
                    _events.GetArrayElementAtIndex(i).FindPropertyRelative("objectReferenceParameter"), true,
                    GUILayout.ExpandWidth(true));
    
                EditorGUILayout.Separator();
                EditorGUILayout.EndVertical();
            }
    
            // Of course we need to Apply the modified properties. We don't our changes won't be saved
            _serializedClip.ApplyModifiedProperties();
        }
    
        /// This Message is triggered when the user selection in the editor changes. That's when we should tell our Window to Repaint() if the user selected another AnimationClip
        private void OnSelectionChange()
        {
            _animationClip =
                Selection.GetFiltered(typeof(AnimationClip), SelectionMode.Assets).FirstOrDefault() as AnimationClip;
            if (_animationClip == null) return;
    
            _serializedClip = new SerializedObject(_animationClip);
            _events = _serializedClip.FindProperty("m_Events");
            Repaint();
        }
    }

結果は次のとおりです。
カスタムエディタウィンドウAnimationClip

高度なトピック

あなたはエディタでいくつかの本当に高度なことをすることができ、EditorWindowクラスは大量の情報を表示するのに最適です。 Unity Asset Store(NodeCanvasやPlayMakerなど)のほとんどの高度なアセットは、カスタムビューを表示するためにEditorWindowを使用します。

SceneViewでの描画

EditorWindowで行う面白いことの1つは、SceneViewに直接情報を表示することです。この方法で、完全にカスタマイズされたマップ/ワールドエディタを作成できます。たとえば、カスタムEditorWindowをアセットパレットとして使用し、SceneViewのクリックをリッスンして新しいオブジェクトをインスタンス化します。次に例を示します。

using UnityEngine;
using System;
using UnityEditor;

public class CustomWindow : EditorWindow {

    private enum Mode {
        View = 0,
        Paint = 1,
        Erase = 2
    }

    private Mode CurrentMode = Mode.View;

    [MenuItem ("Window/Custom Window")]
    static void Init () {
        // Get existing open window or if none, make a new one:
        CustomWindow window = (CustomWindow)EditorWindow.GetWindow (typeof (CustomWindow));
        window.Show();
    }

    void OnGUI () {
        GUILayout.Label ("This is a custom Editor Window", EditorStyles.boldLabel);
    }

    void OnEnable() {
        SceneView.onSceneGUIDelegate = SceneViewGUI;
        if (SceneView.lastActiveSceneView) SceneView.lastActiveSceneView.Repaint();
    }

    void SceneViewGUI(SceneView sceneView) {
        Handles.BeginGUI();
        // We define the toolbars' rects here
        var ToolBarRect = new Rect((SceneView.lastActiveSceneView.camera.pixelRect.width / 6), 10, (SceneView.lastActiveSceneView.camera.pixelRect.width * 4 / 6) , SceneView.lastActiveSceneView.camera.pixelRect.height / 5);
        GUILayout.BeginArea(ToolBarRect);
        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
         CurrentMode = (Mode) GUILayout.Toolbar(
            (int) CurrentMode,
            Enum.GetNames(typeof(Mode)),
            GUILayout.Height(ToolBarRect.height));
        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();
        GUILayout.EndArea();
        Handles.EndGUI();
    }
}

これにより、ツールバーがSceneViewに直接表示されますEditorWindowからのSceneView UI

あなたがどこまで行くことができるかを簡単に見てみましょう:

マップエディタEditorWindow



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow