Sök…


Syntax

  • [MenuItem (strängobjektnamn)]
  • [MenuItem (string itemName, bool isValidateFunction)]
  • [MenuItem (string itemName, bool isValidateFunction, int prioritet)]
  • [ContextMenu (strängnamn)]
  • [ContextMenuItem (strängnamn, strängfunktion)]
  • [DrawGizmo (GizmoType gizmo)]
  • [DrawGizmo (GizmoType gizmo, Type DrawGizmoType)]

parametrar

Parameter detaljer
MenuCommand MenuCommand används för att extrahera kontexten för ett MenuItem
MenuCommand.context Objektet som är målet för menykommandot
MenuCommand.userData En int för att skicka anpassad information till ett menyalternativ

Anpassad inspektör

Med hjälp av en anpassad inspektör kan du ändra hur ett skript ritas i inspektören. Ibland vill du lägga till extra information i inspektören för ditt skript som inte är möjligt att göra med en egen egenskapslåda.

Nedan är ett enkelt exempel på ett anpassat objekt som med hjälp av en anpassad inspektör kan visa mer användbar information.

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

Först definierar vi vårt anpassade beteende med vissa fält

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

Fälten som visas ovan dras automatiskt (utan anpassad inspektör) när du visar skriptet i inspektionsfönstret.

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

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

Dessa egenskaper dras inte automatiskt av Unity. För att visa dessa egenskaper i inspektörsvyn måste vi använda vår anpassade inspektör.

Vi måste först definiera vår anpassade inspektör så här

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

Den anpassade inspektören måste härleda från Editor och behöver attributet CustomEditor . Parametern för attributet är typen av objekt som den anpassade inspektören ska användas för.

Nästa är OnInspectorGUI-metoden. Den här metoden kallas när skriptet visas i inspektionsfönstret.

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

Vi ringer till base.OnInspectorGUI () för att låta Unity hantera de andra fälten som finns i skriptet. Om vi inte skulle kalla detta skulle vi behöva göra mer arbete själva.

Nästa är våra anpassade egenskaper som vi vill visa

var ie = (InspectorExample)target;

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

Vi måste skapa en tillfällig variabel som håller mål som kastas till vår anpassade typ (målet är tillgängligt eftersom vi härstammar från Editor).

Därefter kan vi bestämma hur vi ska rita våra egenskaper, i det här fallet räcker det med två labelfields eftersom vi bara vill visa värdena och inte kunna redigera dem.

Resultat

Innan

resultat före

Efter

resultat efter

Anpassad egendomslåda

Ibland har du anpassade objekt som innehåller data men inte härrör från MonoBehaviour. Att lägga till dessa objekt som ett fält i en klass som är MonoBehaviour kommer inte att ha någon visuell effekt om du inte skriver din egna egna låda för objektets typ.

Nedan är ett enkelt exempel på ett anpassat objekt, läggs till i MonoBehaviour, och en anpassad egenskapslåda för det anpassade objektet.

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

Först definierar vi det anpassade objektet med alla dess krav. Bara en enkel klass som beskriver en användare. Den här klassen används i klassen PropertyDrawerExample som vi kan lägga till i ett 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;
}

Den anpassade klassen behöver attributet Serializable, annars kommer inte CustomPropertyDrawer att användas

Nästa upp är CustomPropertyDrawer

Först måste vi definiera en klass som härrör från PropertyDrawer. Klassdefinitionen behöver också attributet CustomPropertyDrawer. Den överförda parametern är typen av objekt som du vill att denna låda ska användas för.

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

Därefter åsidosätter vi GetPropertyHeight-funktionen. Detta gör att vi kan definiera en anpassad höjd för vår egendom. I detta fall vet vi att vår egendom kommer att ha fyra delar: etikett, namn, ålder och kön. Därför använder vi EditorGUIUtility.singleLineHeight * 4 , vi lägger till ytterligare 6 pixlar eftersom vi vill placera varje fält med två pixlar mellan.

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

Nästa är den faktiska OnGUI-metoden. Vi börjar med EditorGUI.BeginProperty ([...]) och avslutar funktionen med EditorGUI.EndProperty () . Vi gör detta så att om den här egenskapen skulle vara en del av en prefab, skulle den faktiska prefaböverskridande logiken fungera för allt mellan dessa två metoder.

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

Efter det visar vi en etikett som innehåller fältets namn och vi definierar redan rektanglarna för våra fält.

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

Varje fält är avstånd från 16 + 2 pixlar och höjden är 16 (vilket är samma som EditorGUIUtility.singleLineHeight)

Därefter sträcker vi in användargränssnittet med en flik för en lite trevligare layout, visar egenskaperna, avmarkerar indraget av GUI och avslutar med 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();

Vi visar fälten med EditorGUI.PropertyField som kräver en rektangel för positionen och en SerializedProperty för den egenskap som ska visas. Vi förvärvar fastigheten genom att ringa FindPropertyRelative ("...") på den egendom som skickats i OnGUI- funktionen. Observera att dessa är skiftlägeskänsliga och att icke-offentliga fastigheter inte kan hittas!

I det här exemplet sparar jag inte egenskapens avkastning från property.FindPropertyRelative ("..."). Du bör spara dessa i privata fält i klassen för att förhindra onödiga samtal

Resultat

Innan

Resultat före

Efter

Resultat efter

Menyval

Menyalternativ är ett bra sätt att lägga till anpassade åtgärder till redigeraren. Du kan lägga till menyalternativ i menyraden, ha dem som kontextklick på specifika komponenter, eller till och med som sammanhangsklick på fält i dina skript.

Nedan är ett exempel på hur du kan tillämpa menyalternativ.

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

Vilket ser ut så här

verktygsfältmenyn

Låt oss gå igenom det grundläggande menyalternativet. Som du kan se nedan måste du definiera en statisk funktion med ett MenuItem- attribut, som du passerar en sträng som titel för menyposten. Du kan lägga din menypost flera nivåer djupt genom att lägga till a / i namnet.

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

Du kan inte ha ett menyalternativ på toppnivå. Dina menyalternativ måste vara i en undermeny!

Specialtecknen i slutet av MenuItems namn är för genvägstangenter, dessa är inte ett krav.

Det finns specialtecken som du kan använda för dina genvägar, dessa är:

  • % - Ctrl på Windows, Cmd på OS X
  • # - Skift
  • & - Alt

Det betyder att genvägen % # & d står för ctrl + shift + alt + D på Windows, och cmd + shift + alt + D på OS X.

Om du vill använda en genväg utan några speciella tangenter, så till exempel bara 'D' -tangenten, kan du bero på _ (understreck) -tecknet på den genvägstangent som du vill använda.

Det finns några andra specialnycklar som stöds, som är:

  • Vänster, höger, upp, ner - för piltangenterna
  • F1..F12 - för funktionstangenterna
  • HOME, END, PGUP, PGDN - för navigeringsknapparna

Genvägstangenter måste separeras från all annan text med ett mellanslag

Nästa är valideringsmenyobjekt. Validator-menyalternativ tillåter att menyalternativ kan inaktiveras (nedtonad, icke-klickbar) när villkoret inte är uppfylld. Ett exempel på detta kan vara att ditt menyalternativ fungerar på det aktuella valet av GameObjects, som du kan kontrollera i valideringsmenyobjektet.

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

För att en valideringsmeny ska fungera måste du skapa två statiska funktioner, båda med attributet MenuItem och samma namn (genvägstangenten spelar ingen roll). Skillnaden mellan dem är att du markerar dem som en valideringsfunktion eller inte genom att skicka en boolesk parameter.

Du kan också definiera ordningen på menyalternativen genom att lägga till en prioritet. Prioriteten definieras av ett heltal som du passerar som den tredje parametern. Ju mindre antalet desto högre upp i listan, desto större nummer är det lägre i listan. Du kan lägga till en separator mellan två menyalternativ genom att se till att det finns minst 10 siffror mellan menypostens prioritering.

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

Om du har en menylista som har en kombination av prioriterade och icke-prioriterade objekt kommer de icke-prioriterade att separeras från de prioriterade artiklarna.

Nästa är att lägga till ett menyalternativ till snabbmenyn för en redan befintlig komponent. Du måste starta namnet på MenuItem med CONTEXT (skiftlägeskänslig) och låta din funktion ta in en MenuCommand-parameter.

Följande kodavsnitt lägger till ett sammanhangsmenyobjekt till kamerakomponenten.

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

Vilket ser ut så här

Menyalternativ för kamerakontext

Parametern MenuCommand ger dig åtkomst till komponentvärdet och alla användardata som skickas med det.

Du kan också lägga till ett sammanhangsmenyobjekt till dina egna komponenter med hjälp av attributet ContextMenu. Detta attribut tar endast ett namn, ingen validering eller prioritering och måste vara en del av en icke-statisk metod.

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

Vilket ser ut så här

Menyobjekt för anpassad kontext

Du kan också lägga till sammanhangsmenyobjekt till fält i din egen komponent. Dessa menyalternativ kommer att visas när du klickar på i fältet som de tillhör och kan utföra metoder som du har definierat i den komponenten. På det här sättet kan du till exempel lägga till standardvärden eller det aktuella datumet, som visas nedan.

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

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

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

Vilket ser ut så här

ange bildbeskrivning här

Gizmos

Gizmos används för att rita former i scenvyn. Du kan använda dessa former för att rita extra information om dina GameObjects, till exempel den stympa de har eller detekteringsområdet.

Nedan följer två exempel på hur du gör det

Exempel 1

Detta exempel använder OnDrawGizmos och OnDrawGizmosSelected (magiska) metoder.

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

I det här exemplet har vi två metoder för att rita gizmos, en som ritar när objektet är aktivt (OnDrawGizmos) och ett för när objektet är valt i hierarkin (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;
}

Först sparar vi gizmo-matrisen och färgen eftersom vi kommer att ändra den och vill återvända den när vi är klara för att inte påverka någon annan gizmo-ritning.

Därefter vill vi rita den frustum som vårt objekt har, men vi måste ändra Gizmos 'matris så att den matchar positionen, rotationen och skalan. Vi ställer också in Gizmos 'färg till röd för att betona frustum. När detta är gjort kan vi ringa Gizmos.DrawFrustum för att rita frustum i scenvyn.

När vi är klar med att rita det vi vill rita återställer vi Gizmos 'matris och färg.

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

Vi vill också rita ett detekteringsområde när vi väljer vårt GameObject. Detta görs genom klassen Handles eftersom Gizmos- klassen inte har några metoder för skivor.

Använda denna form av ritning av gizmos resulterar i utgången som visas nedan.

Exempel två

Detta exempel använder attributet 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() );
        }
    }
}

På det här sättet kan du skilja gizmo-samtal från ditt skript. Det mesta av detta använder samma kod som det andra exemplet förutom två saker.

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

Du måste använda DrawGizmo-attributet som tar enum GizmoType som den första parametern och en Type som den andra parametern. Typen ska vara den typ du vill använda för att rita gizmo.

Metoden för att rita gizmo måste vara statisk, offentlig eller icke-offentlig och kan namnges vad du vill. Den första parametern är typen, som ska matcha den typ som skickades som den andra parametern i attributet, och den andra parametern är enum GizmoType som beskriver det aktuella tillståndet för ditt objekt.

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

Den andra skillnaden är att för att kontrollera vad GizmoType för objektet är, måste du göra en OCH-kontroll av parametern och typen du vill ha.

Resultat

Ej valt

exempel en inte vald

Vald

exempel en vald

Editor Window

Varför ett redigeringsfönster?

Som du kanske har sett kan du göra många saker i en anpassad inspektör (om du inte vet vad en anpassad inspektör är, kolla exemplet här: http://www.riptutorial.com/unity3d/topic/2506 / extending-the-editor . Men vid en tidpunkt kanske du vill implementera en konfigurationspanel, eller en anpassad tillgångspalett. I dessa fall kommer du att använda ett EditorWindow . Unity UI består i själva Editor Windows; du kan öppna dem (vanligtvis genom den övre fältet), flik dem osv.

Skapa en grundläggande EditorWindow

Enkelt exempel

Att skapa ett anpassat redigeringsfönster är ganska enkelt. Allt du behöver göra är att utöka klassen EditorWindow och använda metoderna Init () och OnGUI (). Här är ett enkelt exempel:

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

De tre viktiga punkterna är:

  1. Glöm inte att utöka EditorWindow
  2. Använd Init () enligt exemplet. EditorWindow.GetWindow kontrollerar om en CustomWindow redan är skapad. Om inte kommer det att skapa en ny instans. Med detta kan du se till att du inte har flera instanser av ditt fönster samtidigt
  3. Använd OnGUI () som vanligt för att visa information i ditt fönster

Det slutliga resultatet kommer att se ut så här:

Enkel anpassad EditorWindow

Gå djupare

Naturligtvis kommer du förmodligen att vilja hantera eller ändra vissa tillgångar med detta EditorWindow. Här är ett exempel med klassen Selection (för att få det aktiva markeringen) och modifiera de valda tillgångsegenskaperna via SerializedObject och 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();
        }
    }

Här är resultatet:
Custom Editor Window AnimationClip

Avancerade ämnen

Du kan göra några riktigt avancerade saker i redigeraren, och klassen EditorWindow är perfekt för att visa stora mängder information. De mest avancerade tillgångarna i Unity Asset Store (som NodeCanvas eller PlayMaker) använder EditorWindow för visning för anpassade vyer.

Ritning i SceneView

En intressant sak att göra med en EditorWindow är att visa information direkt i din SceneView. På så sätt kan du skapa en helt anpassad kart- / världsredigerare, till exempel med din anpassade EditorWindow som en tillgångspalett och lyssna på klick i SceneView för att instansera nya objekt. Här är ett exempel :

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

Detta visar verktygsfältet direkt i din SceneView SceneView UI från EditorWindow

Här är en snabb inblick i hur långt du kan gå:

Map Editor EditorWindow



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow