Ricerca…


Sintassi

  • [MenuItem (stringa itemName)]
  • [MenuItem (stringa itemName, bool isValidateFunction)]
  • [MenuItem (stringa itemName, bool isValidateFunction, int priority)]
  • [ContextMenu (nome stringa)]
  • [ContextMenuItem (nome stringa, funzione stringa)]
  • [DrawGizmo (GizmoType gizmo)]
  • [DrawGizmo (GizmoType gizmo, Type drawnGizmoType)]

Parametri

Parametro Dettagli
MenuCommand MenuCommand viene utilizzato per estrarre il contesto per un oggetto Menu
MenuCommand.context L'oggetto che è la destinazione del comando di menu
MenuCommand.userData Un int per il passaggio di informazioni personalizzate a una voce di menu

Ispettore doganale

L'uso di un ispettore personalizzato ti consente di cambiare il modo in cui viene disegnato uno script in Inspector. A volte si desidera aggiungere ulteriori informazioni nell'ispettore per il proprio script che non è possibile fare con un cassetto delle proprietà personalizzate.

Di seguito è riportato un semplice esempio di oggetto personalizzato che con l'utilizzo di un'ispettore personalizzato può mostrare più informazioni utili.

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

Per prima cosa definiamo il nostro comportamento personalizzato con alcuni campi

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

I campi sopra mostrati vengono disegnati automaticamente (senza un ispettore personalizzato) quando stai visualizzando lo script nella finestra dell'Inspector.

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

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

Queste proprietà non vengono automaticamente disegnate da Unity. Per mostrare queste proprietà nella vista Inspector dobbiamo usare il nostro Custom Inspector.

Per prima cosa dobbiamo definire il nostro ispettore personalizzato come questo

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

L'ispettore personalizzato deve derivare da Editor e necessita dell'attributo CustomEditor . Il parametro dell'attributo è il tipo di oggetto per cui deve essere usato questo ispettore personalizzato.

Il prossimo è il metodo OnInspectorGUI. Questo metodo viene chiamato ogni volta che lo script viene mostrato nella finestra di ispezione.

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

Effettuiamo una chiamata a base.OnInspectorGUI () per consentire a Unity di gestire gli altri campi presenti nello script. Se non lo chiamassimo dovremmo fare più lavoro da soli.

Poi ci sono le nostre proprietà personalizzate che vogliamo mostrare

var ie = (InspectorExample)target;

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

Dobbiamo creare una variabile temporanea che tratti il ​​target castato nel nostro tipo personalizzato (l'obiettivo è disponibile perché deriviamo dall'Editor).

Successivamente possiamo decidere come disegnare le nostre proprietà, in questo caso sono sufficienti due etichette di riferimento, poiché vogliamo solo mostrare i valori e non essere in grado di modificarli.

Risultato

Prima

risultato prima

Dopo

risultato dopo

Cassetto delle proprietà personalizzate

A volte hai oggetti personalizzati che contengono dati ma che non derivano da MonoBehaviour. L'aggiunta di questi oggetti come campo in una classe che è MonoBehaviour non avrà alcun effetto visivo a meno che non si scriva il proprio cassetto delle proprietà personalizzate per il tipo dell'oggetto.

Di seguito è riportato un semplice esempio di oggetto personalizzato, aggiunto a MonoBehaviour e un cassetto delle proprietà personalizzate per l'oggetto personalizzato.

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();
    }
}

Per prima cosa definiamo l'oggetto personalizzato con tutti i suoi requisiti. Solo una semplice classe che descrive un utente. Questa classe viene utilizzata nella nostra classe PropertyDrawerExample che è possibile aggiungere a un oggetto 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;
}

La classe personalizzata necessita dell'attributo Serializable, altrimenti CustomPropertyDrawer non verrà utilizzato

Il prossimo è CustomPropertyDrawer

Per prima cosa dobbiamo definire una classe che deriva da PropertyDrawer. La definizione della classe richiede anche l'attributo CustomPropertyDrawer. Il parametro passato è il tipo di oggetto per il quale si desidera utilizzare questo cassetto.

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

Quindi sostituiremo la funzione GetPropertyHeight. Questo ci consente di definire un'altezza personalizzata per la nostra proprietà. In questo caso sappiamo che la nostra proprietà avrà quattro parti: etichetta, nome, età e sesso. Quindi usiamo EditorGUIUtility.singleLineHeight * 4 , aggiungiamo altri 6 pixel perché vogliamo distanziare ogni campo con due pixel in mezzo.

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

Il prossimo è il metodo OnGUI effettivo. Iniziamo con EditorGUI.BeginProperty ([...]) e terminiamo la funzione con EditorGUI.EndProperty () . Facciamo questo in modo che se questa proprietà fosse parte di un prefabbricato, l'effettiva logica di sostituzione prefabbricata funzionerebbe per tutto ciò che si trova tra questi due metodi.

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

Dopodiché mostriamo un'etichetta contenente il nome del campo e già definiamo i rettangoli per i nostri campi.

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 );

Ogni campo è distanziato di 16 + 2 pixel e l'altezza è 16 (che è uguale a EditorGUIUtility.singleLineHeight)

Successivamente indentiamo l'interfaccia utente con una scheda per un layout un po 'più bello, visualizziamo le proprietà, annulliamo la rientranza della GUI e terminiamo con 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();

Visualizziamo i campi utilizzando EditorGUI.PropertyField che richiede un rettangolo per la posizione e una proprietà serializzata per la proprietà da mostrare. Acquisiamo la proprietà chiamando FindPropertyRelative ("...") sulla proprietà passata nella funzione OnGUI . Nota che queste sono case-sensitive e proprietà non pubbliche non possono essere trovate!

Per questo esempio non sto salvando il ritorno delle proprietà da property.FindPropertyRelative ("..."). Dovresti salvare questi in campi privati ​​della classe per evitare chiamate inutili

Risultato

Prima

Risultato prima

Dopo

Risultato dopo

Voci del menu

Le voci di menu sono un ottimo modo per aggiungere azioni personalizzate all'editor. È possibile aggiungere voci di menu alla barra dei menu, averli come clic-contesto su componenti specifici o anche come clic-clic su campi nei propri script.

Di seguito è riportato un esempio di come è possibile applicare le voci di menu.

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();
    }
}

Che assomiglia a questo

menu della barra degli strumenti

Andiamo oltre la voce di menu di base. Come puoi vedere di seguito, devi definire una funzione statica con un attributo MenuItem , che ti consente di passare una stringa come titolo per la voce di menu. Puoi mettere la tua voce di menu più livelli in profondità aggiungendo un / nel nome.

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

Non puoi avere una voce di menu al primo livello. Le voci del tuo menu devono essere in un sottomenu!

I caratteri speciali alla fine del nome di MenuItem sono per i tasti di scelta rapida, questi non sono un requisito.

Ci sono caratteri speciali che puoi usare per i tuoi tasti di scelta rapida, questi sono:

  • % - Ctrl su Windows, Cmd su OS X
  • # - Cambio
  • & - Alt

Ciò significa che la scorciatoia % # & d sta per ctrl + shift + alt + D su Windows e cmd + shift + alt + D su OS X.

Se si desidera utilizzare una scorciatoia senza tasti speciali, ad esempio solo il tasto 'D', è possibile anteporre il carattere _ (carattere di sottolineatura) al tasto di scelta rapida che si desidera utilizzare.

Esistono altre chiavi speciali supportate, che sono:

  • SINISTRA, DESTRA, SU, GIÙ - per i tasti freccia
  • F1..F12 - per i tasti funzione
  • HOME, END, PGUP, PGDN - per i tasti di navigazione

I tasti di scelta rapida devono essere separati da qualsiasi altro testo con uno spazio

Successivamente ci sono voci di menu del validatore. Le voci del menu Validator consentono di disabilitare le voci di menu (disattivate, non selezionabili) quando la condizione non viene soddisfatta. Un esempio potrebbe essere che la voce del menu agisce sulla selezione corrente di GameObjects, che è possibile verificare nella voce di menu del validatore.

[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
}

Perché una voce di menu del validatore funzioni è necessario creare due funzioni statiche, entrambe con l'attributo MenuItem e lo stesso nome (il tasto di scelta rapida non ha importanza). La differenza tra loro è che li stai contrassegnando come una funzione di validazione o meno passando un parametro booleano.

È inoltre possibile definire l'ordine delle voci di menu aggiungendo una priorità. La priorità è definita da un intero che si passa come terzo parametro. Più piccolo è il numero più in alto nell'elenco, più grande è il numero più in basso nell'elenco. È possibile aggiungere un separatore tra due voci di menu assicurandosi che vi siano almeno 10 cifre tra la priorità delle voci di menu.

[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
}

Se si dispone di un elenco di menu che ha una combinazione di elementi prioritari e non prioritari, i non prioritari verranno separati dagli elementi prioritari.

Il prossimo è aggiungere una voce di menu al menu di scelta rapida di un componente già esistente. È necessario avviare il nome di MenuItem con CONTEXT (con distinzione tra maiuscole e minuscole) e impostare la funzione in un parametro MenuCommand.

Il seguente frammento aggiungerà una voce di menu contestuale al componente Fotocamera.

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

Che assomiglia a questo

Voce del menu contestuale della fotocamera

Il parametro MenuCommand consente di accedere al valore del componente ea tutti i dati utente che vengono inviati con esso.

Puoi anche aggiungere una voce di menu contestuale ai tuoi componenti utilizzando l'attributo ContextMenu. Questo attributo richiede solo un nome, nessuna convalida o priorità e deve essere parte di un metodo non statico.

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

Che assomiglia a questo

Voce di menu contestuale personalizzata

È inoltre possibile aggiungere voci del menu di scelta rapida ai campi nel proprio componente. Queste voci di menu vengono visualizzate quando fai clic con il pulsante destro del mouse sul campo a cui appartengono e puoi eseguire i metodi che hai definito in quel componente. In questo modo è possibile aggiungere, ad esempio, valori predefiniti o la data corrente, come mostrato di seguito.

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

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

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

Che assomiglia a questo

inserisci la descrizione dell'immagine qui

aggeggi

I gizmo sono usati per disegnare forme nella vista scena. Puoi utilizzare queste forme per ottenere informazioni aggiuntive sui tuoi oggetti GameObjects, ad esempio il trofeo che hanno o il raggio di rilevamento.

Di seguito sono riportati due esempi su come farlo

Esempio 1

Questo esempio utilizza i metodi (magici) OnDrawGizmos e OnDrawGizmosSelected .

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() );
    }
}

In questo esempio abbiamo due metodi per disegnare i gizmo, uno che disegna quando l'oggetto è attivo (OnDrawGizmos) e uno per quando l'oggetto è selezionato nella gerarchia (OnDrawGizmosSelected).

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;
}

Per prima cosa salviamo la matrice gizmo e il colore perché lo cambieremo e vogliamo ripristinarlo quando avremo finito per non influenzare nessun altro disegno gizmo.

Poi vogliamo disegnare il trofeo che il nostro oggetto ha, tuttavia, abbiamo bisogno di cambiare la matrice dei Gizmos in modo che corrisponda alla posizione, alla rotazione e alla scala. Abbiamo anche impostato il colore dei Gizmos in rosso per enfatizzare il trofeo. Quando questo è fatto, possiamo chiamare Gizmos.DrawFrustum per disegnare il troncone nella scena.

Quando abbiamo finito di disegnare ciò che vogliamo disegnare, ripristiniamo la matrice e il colore dei Gizmos.

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

Vogliamo anche disegnare un intervallo di rilevamento quando selezioniamo il nostro GameObject. Questo viene fatto attraverso la classe Handles poiché la classe Gizmos non ha alcun metodo per i dischi.

L'utilizzo di questa forma di disegno di gizmo risulta nell'output mostrato di seguito.

Esempio due

Questo esempio utilizza l'attributo 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() );
        }
    }
}

In questo modo puoi separare le chiamate gizmo dal tuo script. La maggior parte di questo utilizza lo stesso codice dell'altro esempio ad eccezione di due cose.

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

È necessario utilizzare l'attributo DrawGizmo che prende l'enumerazione GizmoType come primo parametro e un tipo come secondo parametro. Il tipo dovrebbe essere il tipo che si desidera utilizzare per disegnare il gizmo.

Il metodo per disegnare il gizmo deve essere statico, pubblico o non pubblico e può essere chiamato come vuoi. Il primo parametro è il tipo, che deve corrispondere al tipo passato come secondo parametro nell'attributo e il secondo parametro è l'enum GizmoType che descrive lo stato corrente dell'oggetto.

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

L'altra differenza è che per verificare qual è il GizmoType dell'oggetto, è necessario eseguire un controllo AND sul parametro e sul tipo desiderato.

Risultato

Non selezionato

esempio non selezionato

Selezionato

esempio uno selezionato

Finestra dell'editor

Perché una finestra dell'editor?

Come potresti aver visto, puoi fare un sacco di cose in un ispettore personalizzato (se non sai cos'è un ispettore personalizzato, controlla l'esempio qui: http://www.Scriptutorial.com/unity3d/topic/2506 / estendere-l'-editor, ma a un certo punto potresti voler implementare un pannello di configurazione o una tavolozza delle risorse personalizzata, in questi casi utilizzerai una finestra Editor . L'interfaccia utente di Unity stessa è composta da Windows Editor, puoi aprirli (di solito attraverso la barra in alto), scheda, ecc.

Crea una finestra Editor di base

Semplice esempio

La creazione di una finestra di editor personalizzato è abbastanza semplice. Tutto quello che devi fare è estendere la classe EditorWindow e utilizzare i metodi Init () e OnGUI (). Qui c'è un semplice esempio :

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);
    }
}

I 3 punti importanti sono:

  1. Non dimenticare di estendere EditorWindow
  2. Utilizzare Init () come previsto nell'esempio. EditorWindow.GetWindow sta controllando se una CustomWindow è già stata creata. In caso contrario, creerà una nuova istanza. Usando questo ti assicuri di non avere più istanze della tua finestra allo stesso tempo
  3. Usa OnGUI () come al solito per visualizzare le informazioni nella tua finestra

Il risultato finale sarà simile a questo:

Simple Custom EditorWindow

Andando più a fondo

Ovviamente probabilmente vorrai gestire o modificare alcune risorse usando questa EditorWindow. Ecco un esempio che utilizza la classe Selection (per ottenere la selezione attiva) e modifica delle proprietà delle risorse selezionate tramite SerializedObject e 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();
        }
    }

Ecco il risultato:
Finestra dell'editor personalizzato AnimationClip

Argomenti avanzati

Nell'editor puoi fare alcune cose davvero avanzate e la classe EditorWindow è perfetta per la visualizzazione di grandi quantità di informazioni. Le risorse più avanzate di Unity Asset Store (come NodeCanvas o PlayMaker) utilizzano EditorWindow per la visualizzazione per visualizzazioni personalizzate.

Disegnare in SceneView

Una cosa interessante da fare con EditorWindow è visualizzare le informazioni direttamente in SceneView. In questo modo è possibile creare un editor di mappe / mondo completamente personalizzato, ad esempio, utilizzando l'EditorWindow personalizzato come una tavolozza delle risorse e ascoltando i clic in SceneView per creare un'istanza di nuovi oggetti. Ecco un esempio:

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();
    }
}

Questo mostrerà la barra degli strumenti direttamente in SceneView Interfaccia utente di SceneView da EditorWindow

Ecco una rapida panoramica di quanto lontano puoi andare:

Map Editor EditorWindow



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow