Suche…


Syntax

  • [MenuItem (String itemName)]
  • [MenuItem (Zeichenfolge itemName, bool isValidateFunction)]
  • [MenuItem (String itemName, bool isValidateFunction, int-Priorität)]
  • [ContextMenu (Name der Zeichenfolge)]
  • [ContextMenuItem (Stringname, Stringfunktion)]
  • [DrawGizmo (GizmoType-Gizmo)]
  • [DrawGizmo (GizmoType-Gizmo, Typ drawGizmoType)]

Parameter

Parameter Einzelheiten
MenuCommand MenuCommand wird verwendet, um den Kontext für ein MenuItem zu extrahieren
MenuCommand.context Das Objekt, das das Ziel des Menübefehls ist
MenuCommand.userData Ein int, um benutzerdefinierte Informationen an einen Menüpunkt zu übergeben

Benutzerdefinierter Inspektor

Wenn Sie einen benutzerdefinierten Inspektor verwenden, können Sie die Art und Weise ändern, in der ein Skript im Inspektor gezeichnet wird. Manchmal möchten Sie zusätzliche Informationen im Inspektor für Ihr Skript hinzufügen, die mit einem benutzerdefinierten Eigenschaftsauszug nicht möglich sind.

Nachfolgend finden Sie ein einfaches Beispiel für ein benutzerdefiniertes Objekt, das bei Verwendung eines benutzerdefinierten Inspektors nützliche Informationen anzeigen kann.

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

Zuerst definieren wir unser benutzerdefiniertes Verhalten mit einigen Feldern

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

Die oben gezeigten Felder werden automatisch (ohne benutzerdefinierten Inspektor) gezeichnet, wenn Sie das Skript im Inspektorfenster anzeigen.

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

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

Diese Eigenschaften werden von Unity nicht automatisch gezeichnet. Um diese Eigenschaften in der Inspektoransicht anzuzeigen, müssen Sie unseren benutzerdefinierten Inspektor verwenden.

Wir müssen unseren Custom Inspector zunächst so definieren

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

Der benutzerdefinierte Inspektor muss vom Editor abgeleitet sein und benötigt das CustomEditor- Attribut. Der Parameter des Attributs ist der Typ des Objekts, für das der benutzerdefinierte Inspektor verwendet werden soll.

Als nächstes ist die OnInspectorGUI-Methode. Diese Methode wird aufgerufen, wenn das Skript im Inspektorfenster angezeigt wird.

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

Wir rufen base.OnInspectorGUI () auf, damit Unity die anderen Felder im Skript verarbeiten kann. Wenn wir das nicht nennen würden, müssten wir selbst mehr arbeiten.

Als nächstes sind unsere benutzerdefinierten Eigenschaften, die wir anzeigen möchten

var ie = (InspectorExample)target;

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

Wir müssen eine temporäre Variable erstellen, die ein auf unseren benutzerdefinierten Typ gegossenes Ziel enthält (Ziel ist verfügbar, da wir vom Editor abgeleitet sind).

Als Nächstes können wir entscheiden, wie unsere Eigenschaften gezeichnet werden sollen. In diesem Fall sind zwei Labelfelder ausreichend, da wir nur die Werte anzeigen und nicht bearbeiten können.

Ergebnis

Vor

Ergebnis vor

Nach dem

Ergebnis nach

Benutzerdefinierte Eigenschaftsschublade

Manchmal haben Sie benutzerdefinierte Objekte, die Daten enthalten, aber nicht von MonoBehaviour abgeleitet sind. Das Hinzufügen dieser Objekte als Feld in einer Klasse, die MonoBehaviour ist, hat keine visuellen Auswirkungen, es sei denn, Sie schreiben ein eigenes benutzerdefiniertes Eigenschaftsfeld für den Objekttyp.

Im Folgenden finden Sie ein einfaches Beispiel für ein benutzerdefiniertes Objekt, das zu MonoBehaviour hinzugefügt wurde, sowie ein benutzerdefiniertes Eigenschaftsfach für das benutzerdefinierte Objekt.

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

Zunächst definieren wir das benutzerdefinierte Objekt mit allen Anforderungen. Nur eine einfache Klasse, die einen Benutzer beschreibt. Diese Klasse wird in unserer PropertyDrawerExample-Klasse verwendet, die wir einem GameObject hinzufügen können.

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

Die benutzerdefinierte Klasse benötigt das Serializable-Attribut, andernfalls wird CustomPropertyDrawer nicht verwendet

Als nächstes ist der CustomPropertyDrawer

Zuerst müssen wir eine von PropertyDrawer abgeleitete Klasse definieren. Die Klassendefinition benötigt außerdem das CustomPropertyDrawer-Attribut. Der übergebene Parameter ist der Typ des Objekts, für das diese Schublade verwendet werden soll.

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

Als Nächstes überschreiben wir die GetPropertyHeight-Funktion. Dies ermöglicht uns, eine benutzerdefinierte Höhe für unser Grundstück festzulegen. In diesem Fall wissen wir, dass unser Eigentum aus vier Teilen besteht: Bezeichnung, Name, Alter und Geschlecht. Daher verwenden wir EditorGUIUtility.singleLineHeight * 4 und fügen weitere 6 Pixel hinzu, da wir jedes Feld mit zwei Pixeln dazwischen platzieren möchten.

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

Als nächstes ist die tatsächliche OnGUI-Methode. Wir beginnen mit EditorGUI.BeginProperty ([...]) und beenden die Funktion mit EditorGUI.EndProperty () . Wir tun dies, damit, wenn diese Eigenschaft Teil eines Prefab sein würde, die eigentliche Prefab-Überschreibungslogik für alles zwischen diesen beiden Methoden funktionieren würde.

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

Danach zeigen wir ein Label mit dem Namen des Feldes und definieren bereits die Rechtecke für unsere Felder.

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

Jedes Feld hat einen Abstand von 16 + 2 Pixeln und die Höhe beträgt 16 (was mit EditorGUIUtility.singleLineHeight identisch ist).

Als Nächstes rücken wir die Benutzeroberfläche mit einer Registerkarte für ein etwas schöneres Layout ein, zeigen die Eigenschaften an, lassen die Einrückung der GUI frei und enden mit 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();

Wir zeigen die Felder mithilfe von EditorGUI.PropertyField an, das ein Rechteck für die Position und eine SerializedProperty für die Anzeige der Eigenschaft erfordert. Wir erwerben die Eigenschaft, indem wir FindPropertyRelative ("...") für die in der OnGUI- Funktion übergebene Eigenschaft aufrufen . Beachten Sie, dass diese Groß- und Kleinschreibung beachtet werden und nicht öffentliche Eigenschaften nicht gefunden werden können!

Für dieses Beispiel speichere ich nicht die Eigenschaften, die von property.FindPropertyRelative ("...") zurückgegeben werden. Sie sollten diese in privaten Feldern in der Klasse speichern, um unnötige Anrufe zu vermeiden

Ergebnis

Vor

Ergebnis vor

Nach dem

Ergebnis nach

Menüpunkte

Menüelemente bieten eine hervorragende Möglichkeit, dem Editor benutzerdefinierte Aktionen hinzuzufügen. Sie können der Menüleiste Menüelemente hinzufügen, sie als Kontext-Klick auf bestimmte Komponenten oder sogar als Kontext-Klick auf Felder in Ihren Skripts verwenden.

Nachfolgend finden Sie ein Beispiel, wie Sie Menüelemente anwenden können.

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

Was sieht so aus

Symbolleistenmenü

Gehen wir den grundlegenden Menüpunkt durch. Wie Sie unten sehen können, müssen Sie eine statische Funktion mit einem MenuItem- Attribut definieren, dem Sie eine Zeichenfolge als Titel für den Menüeintrag übergeben. Sie können Ihr Menüelement auf mehrere Ebenen vertiefen, indem Sie dem Namen ein / hinzufügen.

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

Sie können keinen Menüpunkt auf oberster Ebene haben. Ihre Menüpunkte müssen sich in einem Untermenü befinden!

Die Sonderzeichen am Ende des Namens des MenuItem sind für Tastenkombinationen, diese sind nicht erforderlich.

Es gibt Sonderzeichen, die Sie für Ihre Tastenkombinationen verwenden können. Diese sind:

  • % - Strg unter Windows, Cmd unter OS X
  • # - Verschiebung
  • & - Alt

Das bedeutet, dass die Abkürzung % # & d unter Windows für Strg + Umschalt + Alt + D und unter OS X cmd + Umschalt + Alt + D steht.

Wenn Sie eine Tastenkombination ohne Sondertasten verwenden möchten, also beispielsweise nur die Taste 'D', können Sie das Zeichen _ (Unterstrich) der Tastenkombination voranstellen, die Sie verwenden möchten.

Es gibt einige andere spezielle Tasten, die unterstützt werden:

  • LINKS, RECHTS, AUF, AB - für die Pfeiltasten
  • F1..F12 - für die Funktionstasten
  • HOME, END, PGUP, PGDN - für die Navigationstasten

Tastenkombinationen müssen mit einem Leerzeichen von jedem anderen Text getrennt werden

Weiter sind die Menüpunkte des Validators. Überprüfungsmenüelemente ermöglichen das Deaktivieren von Menüelementen (ausgegraut, nicht anklickbar), wenn die Bedingung nicht erfüllt ist. Ein Beispiel dafür könnte sein, dass Ihr Menüpunkt auf die aktuelle Auswahl von GameObjects einwirkt, die Sie im Validator-Menüpunkt überprüfen können.

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

Damit ein Prüfer-Menüelement funktioniert, müssen Sie zwei statische Funktionen erstellen, die beide das Attribut MenuItem und denselben Namen haben (die Tastenkombination ist nicht wichtig). Der Unterschied zwischen ihnen besteht darin, dass Sie sie als Prüferfunktion markieren oder nicht, indem Sie einen booleschen Parameter übergeben.

Sie können auch die Reihenfolge der Menüelemente festlegen, indem Sie eine Priorität hinzufügen. Die Priorität wird durch eine Ganzzahl definiert, die Sie als dritten Parameter übergeben. Je kleiner die Zahl, desto höher in der Liste, desto größer ist die Zahl in der Liste. Sie können ein Trennzeichen zwischen zwei Menüelementen hinzufügen, indem Sie sicherstellen, dass zwischen den Prioritäten der Menüelemente mindestens 10 Stellen stehen.

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

Wenn Sie eine Menüliste mit einer Kombination aus priorisierten und nicht priorisierten Elementen haben, werden die nicht priorisierten Elemente von den priorisierten Elementen getrennt.

Als Nächstes fügen Sie dem Kontextmenü einer bereits vorhandenen Komponente ein Menüelement hinzu. Sie müssen den Namen des MenuItem mit CONTEXT beginnen (Groß- und Kleinschreibung beachten) und Ihre Funktion muss einen MenuCommand-Parameter übernehmen.

Das folgende Snippet fügt der Camera-Komponente ein Kontextmenüelement hinzu.

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

Was sieht so aus

Kamerakontextmenüelement

Mit dem Parameter MenuCommand haben Sie Zugriff auf den Komponentenwert und auf alle Nutzerdaten, die mit ihm gesendet werden.

Sie können Ihren eigenen Komponenten auch ein Kontextmenüelement hinzufügen, indem Sie das ContextMenu-Attribut verwenden. Dieses Attribut hat nur einen Namen, keine Validierung oder Priorität und muss Teil einer nicht statischen Methode sein.

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

Was sieht so aus

Benutzerdefiniertes Kontextmenüelement

Sie können den Feldern in Ihrer eigenen Komponente auch Kontextmenüelemente hinzufügen. Diese Menüelemente werden angezeigt, wenn Sie mit der rechten Maustaste auf das Feld klicken, zu dem sie gehören, und sie können Methoden ausführen, die Sie in dieser Komponente definiert haben. Auf diese Weise können Sie beispielsweise Standardwerte oder das aktuelle Datum hinzufügen (siehe unten).

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

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

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

Was sieht so aus

Geben Sie hier die Bildbeschreibung ein

Gizmos

Gizmos werden zum Zeichnen von Formen in der Szenenansicht verwendet. Sie können diese Formen verwenden, um zusätzliche Informationen über Ihre GameObjects zu zeichnen, z. B. über das Frustum oder den Erkennungsbereich.

Im Folgenden finden Sie zwei Beispiele dafür

Beispiel eins

In diesem Beispiel werden die Methoden OnDrawGizmos und OnDrawGizmosSelected (Magie) verwendet.

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 diesem Beispiel haben wir zwei Methoden zum Zeichnen von Gizmos, eine, die zeichnet, wenn das Objekt aktiv ist (OnDrawGizmos), und eine, wenn das Objekt in der Hierarchie ausgewählt wird (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;
}

Zuerst speichern wir die Gizmo-Matrix und -Farbe, da wir sie ändern und nach dem Beenden zurückkehren möchten, um keine anderen Gizmo-Zeichnungen zu beeinflussen.

Als Nächstes wollen wir den Kegelstumpf zeichnen, den unser Objekt hat. Wir müssen jedoch die Gizmos-Matrix so ändern, dass sie mit der Position, Rotation und Skalierung übereinstimmt. Wir haben auch die Farbe des Gizmos auf Rot gesetzt, um den Stumpf zu betonen. Wenn dies erledigt ist, können Sie Gizmos.DrawFrustum anrufen, um das Frustum in der Szenenansicht zu zeichnen.

Wenn wir mit dem Zeichnen fertig sind, setzen wir die Matrix und Farbe des Gizmos zurück.

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

Wir möchten auch einen Erkennungsbereich zeichnen, wenn wir unser GameObject auswählen. Dies geschieht über die Klasse Handles , da die Klasse Gizmos über keine Methoden für Datenträger verfügt.

Die Verwendung dieser Form des Zeichnens von Gizmos führt zu der unten gezeigten Ausgabe.

Beispiel zwei

In diesem Beispiel wird das DrawGizmo- Attribut verwendet.

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

Auf diese Weise können Sie die Gizmo-Aufrufe von Ihrem Skript trennen. Die meisten davon verwenden den gleichen Code wie das andere Beispiel, mit Ausnahme von zwei Dingen.

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

Sie müssen das DrawGizmo-Attribut verwenden, das die Aufzählung GizmoType als ersten Parameter und einen Typ als zweiten Parameter verwendet. Der Typ sollte der Typ sein, den Sie zum Zeichnen des Gizmos verwenden möchten.

Die Methode zum Zeichnen des Gizmos muss statisch, öffentlich oder nicht öffentlich sein und kann beliebig benannt werden. Der erste Parameter ist der Typ, der mit dem Typ übereinstimmen sollte, der als zweiter Parameter im Attribut übergeben wird, und der zweite Parameter ist das Aufzählungs-GizmoType, das den aktuellen Status Ihres Objekts beschreibt.

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

Der andere Unterschied besteht darin, dass Sie zur Überprüfung des GizmoType des Objekts eine UND-Prüfung des Parameters und des gewünschten Typs durchführen müssen.

Ergebnis

Nicht ausgewählt

Beispiel eins nicht ausgewählt

Ausgewählt

Beispiel eins ausgewählt

Editor-Fenster

Warum ein Editorfenster?

Wie Sie vielleicht gesehen haben, können Sie in einem benutzerdefinierten Inspektor eine Menge tun. Wenn Sie nicht wissen, was ein benutzerdefinierter Inspektor ist, überprüfen Sie das Beispiel hier: http://www.riptutorial.com/unity3d/topic/2506 / Erweitern des Editors . An einem bestimmten Punkt möchten Sie jedoch möglicherweise ein Konfigurationsfenster oder eine angepasste Palette für Assets implementieren. In diesen Fällen verwenden Sie ein EditorWindow . Die Unity-Benutzeroberfläche besteht aus Editor-Windows, das Sie öffnen können (normalerweise durch die obere Leiste), Tabulatoren usw.

Erstellen Sie ein einfaches Editorfenster

Einfaches Beispiel

Das Erstellen eines benutzerdefinierten Editorfensters ist ziemlich einfach. Alles was Sie tun müssen, ist die EditorWindow-Klasse zu erweitern und die Init () - und die OnGUI () - Methode zu verwenden. Hier ist ein einfaches Beispiel:

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

Die 3 wichtigsten Punkte sind:

  1. Vergessen Sie nicht, EditorWindow zu erweitern
  2. Verwenden Sie das Init () wie im Beispiel angegeben. EditorWindow.GetWindow prüft, ob bereits ein CustomWindow erstellt wurde. Wenn nicht, wird eine neue Instanz erstellt. Damit stellen Sie sicher, dass Sie nicht mehrere Instanzen Ihres Fensters gleichzeitig haben
  3. Verwenden Sie OnGUI () wie üblich, um Informationen in Ihrem Fenster anzuzeigen

Das Endergebnis wird so aussehen:

Einfaches Custom EditorWindow

Tiefer gehen

Natürlich werden Sie wahrscheinlich einige Assets mit diesem EditorWindow verwalten oder ändern wollen. Hier ist ein Beispiel, in dem die Selection- Klasse verwendet wird (um die aktive Selection abzurufen) und die ausgewählten Asset-Eigenschaften über SerializedObject und SerializedProperty geändert werden .

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

Hier ist das Ergebnis:
Benutzerdefiniertes Editor-Fenster AnimationClip

Fortgeschrittene Themen

Im Editor können Sie einige wirklich fortgeschrittene Dinge erledigen, und die EditorWindow-Klasse eignet sich hervorragend für die Anzeige großer Informationsmengen. Die meisten fortschrittlichen Assets im Unity Asset Store (wie NodeCanvas oder PlayMaker) verwenden EditorWindow zum Anzeigen von benutzerdefinierten Ansichten.

In der SceneView zeichnen

Eine interessante Sache, die Sie mit einem Editor-Fenster machen können, ist die Anzeige von Informationen direkt in SceneView. Auf diese Weise können Sie einen vollständig benutzerdefinierten Karten- / Welteditor erstellen, z. B. mit Ihrem benutzerdefinierten EditorWindow als Asset-Palette und zum Anhören von Klicks in der SceneView, um neue Objekte zu instanziieren. Hier ist ein Beispiel :

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

Dadurch wird eine Symbolleiste direkt in SceneView angezeigt SceneView-Benutzeroberfläche von EditorWindow

Hier ist ein kurzer Blick darauf, wie weit Sie gehen können:

Map Editor EditorWindow



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow