Recherche…


Syntaxe

  • [MenuItem (string itemName)]
  • [MenuItem (string itemName, bool isValidateFunction)]
  • [MenuItem (string itemName, bool isValidateFunction, int priority)]
  • [ContextMenu (nom de chaîne)]
  • [ContextMenuItem (nom de chaîne, fonction de chaîne)]
  • [DrawGizmo (GizmoType gizmo)]
  • [DrawGizmo (gizmoType, type drawnGizmoType)]

Paramètres

Paramètre Détails
MenuCommand MenuCommand permet d'extraire le contexte d'un MenuItem
MenuCommand.context L'objet qui est la cible de la commande de menu
MenuCommand.userData Un int pour transmettre des informations personnalisées à un élément de menu

Inspecteur Personnalisé

L'utilisation d'un inspecteur personnalisé vous permet de modifier la manière dont un script est dessiné dans l'inspecteur. Parfois, vous souhaitez ajouter des informations supplémentaires dans l'inspecteur pour votre script, ce qui n'est pas possible avec un tiroir de propriétés personnalisé.

Vous trouverez ci-dessous un exemple simple d'objet personnalisé qui, avec l'utilisation d'un inspecteur personnalisé, peut afficher des informations plus utiles.

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

Tout d'abord, nous définissons notre comportement personnalisé avec certains champs

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

Les champs affichés ci-dessus sont automatiquement dessinés (sans inspecteur personnalisé) lorsque vous consultez le script dans la fenêtre Inspecteur.

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

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

Ces propriétés ne sont pas automatiquement dessinées par Unity. Pour afficher ces propriétés dans la vue Inspecteur, nous devons utiliser notre inspecteur personnalisé.

Nous devons d'abord définir notre inspecteur personnalisé comme ceci

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

L'inspecteur personnalisé doit dériver de l' éditeur et nécessite l'attribut CustomEditor . Le paramètre de l'attribut est le type d'objet pour lequel cet inspecteur personnalisé doit être utilisé.

Le suivant est la méthode OnInspectorGUI. Cette méthode est appelée chaque fois que le script est affiché dans la fenêtre de l'inspecteur.

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

Nous appelons base.OnInspectorGUI () pour laisser Unity gérer les autres champs du script. Si nous n'appelions pas cela, nous devions faire plus de travail nous-mêmes.

Ensuite sont nos propriétés personnalisées que nous voulons montrer

var ie = (InspectorExample)target;

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

Nous devons créer une variable temporaire contenant la cible convertie dans notre type personnalisé (la cible est disponible car nous dérivons de l'éditeur).

Ensuite, nous pouvons décider comment dessiner nos propriétés, dans ce cas deux labelfields sont suffisants car nous voulons juste montrer les valeurs et ne pas pouvoir les éditer.

Résultat

Avant

résultat avant

Après

résultat après

Tiroir de propriété personnalisée

Parfois, vous avez des objets personnalisés qui contiennent des données mais ne dérivent pas de MonoBehaviour. L'ajout de ces objets en tant que champ dans une classe MonoBehaviour n'aura aucun effet visuel, sauf si vous écrivez votre propre tiroir de propriétés personnalisé pour le type de l'objet.

Vous trouverez ci-dessous un exemple simple d'objet personnalisé, ajouté à MonoBehaviour et un tiroir de propriétés personnalisé pour l'objet personnalisé.

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

Tout d'abord, nous définissons l'objet personnalisé avec toutes ses exigences. Juste une classe simple décrivant un utilisateur. Cette classe est utilisée dans notre classe PropertyDrawerExample que nous pouvons ajouter à un 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 personnalisée a besoin de l'attribut Serializable, sinon CustomPropertyDrawer ne sera pas utilisé

Le suivant est le CustomPropertyDrawer

Nous devons d'abord définir une classe dérivée de PropertyDrawer. La définition de classe nécessite également l'attribut CustomPropertyDrawer. Le paramètre passé est le type de l'objet à utiliser pour ce tiroir.

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

Ensuite, nous remplaçons la fonction GetPropertyHeight. Cela nous permet de définir une hauteur personnalisée pour notre propriété. Dans ce cas, nous savons que notre propriété aura quatre parties: étiquette, nom, âge et sexe. Par conséquent, nous utilisons EditorGUIUtility.singleLineHeight * 4 , nous ajoutons 6 pixels supplémentaires car nous voulons espacer chaque champ de deux pixels.

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

Suivant est la méthode OnGUI réelle. Nous commençons avec EditorGUI.BeginProperty ([...]) et terminons la fonction avec EditorGUI.EndProperty () . Nous faisons cela pour que si cette propriété fait partie d'un préfabriqué, la logique de remplacement préfabriquée réelle fonctionnerait pour tout entre ces deux méthodes.

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

Après cela, nous montrons une étiquette contenant le nom du champ et nous définissons déjà les rectangles pour nos champs.

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

Chaque champ est espacé de 16 + 2 pixels et la hauteur est de 16 (ce qui est identique à EditorGUIUtility.singleLineHeight)

Ensuite, indentez l'interface utilisateur avec un onglet pour une présentation un peu plus agréable, affichez les propriétés, désélectionnez l'interface graphique et terminez avec 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();

Nous affichons les champs en utilisant EditorGUI.PropertyField qui nécessite un rectangle pour la position et un SerializedProperty pour la propriété à afficher. Nous acquérons la propriété en appelant FindPropertyRelative ("...") sur la propriété passée dans la fonction OnGUI . Notez que ces propriétés sont sensibles à la casse et que les propriétés non publiques sont introuvables!

Pour cet exemple, je ne sauvegarde pas les propriétés retournées par property.FindPropertyRelative ("..."). Vous devez les enregistrer dans des champs privés de la classe pour éviter les appels inutiles

Résultat

Avant

Résultat avant

Après

Résultat après

Articles de menu

Les éléments de menu sont un excellent moyen d'ajouter des actions personnalisées à l'éditeur. Vous pouvez ajouter des éléments de menu à la barre de menus, les utiliser en tant que clics contextuels sur des composants spécifiques, ou même en tant que clics contextuels sur des champs de vos scripts.

Vous trouverez ci-dessous un exemple de la manière d'appliquer des éléments de 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();
    }
}

Qui ressemble à ceci

menu de la barre d'outils

Passons en revue l'élément de menu de base. Comme vous pouvez le voir ci-dessous, vous devez définir une fonction statique avec un attribut MenuItem , auquel vous transmettez une chaîne comme titre pour l'élément de menu. Vous pouvez mettre votre élément de menu à plusieurs niveaux en ajoutant un / dans le nom.

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

Vous ne pouvez pas avoir un élément de menu au niveau supérieur. Vos éléments de menu doivent être dans un sous-menu!

Les caractères spéciaux à la fin du nom de MenuItem sont des raccourcis clavier, ils ne sont pas obligatoires.

Il existe des caractères spéciaux que vous pouvez utiliser pour vos touches de raccourci, à savoir:

  • % - Ctrl sous Windows, Cmd sous OS X
  • # - Décalage
  • & - Alt

Cela signifie que le raccourci % # & d signifie ctrl + shift + alt + D sur Windows et cmd + shift + alt + D sur OS X.

Si vous souhaitez utiliser un raccourci sans touche spéciale, par exemple juste la touche «D», vous pouvez ajouter le caractère de soulignement (_) au raccourci clavier que vous souhaitez utiliser.

Il existe d'autres clés spéciales qui sont prises en charge, à savoir:

  • GAUCHE, DROITE, HAUT, BAS - pour les touches fléchées
  • F1..F12 - pour les touches de fonction
  • HOME, END, PGUP, PGDN - pour les touches de navigation

Les touches de raccourci doivent être séparées de tout autre texte avec un espace

Viennent ensuite les éléments de menu du validateur. Les éléments du menu du validateur permettent de désactiver les éléments de menu (grisés, non cliquables) lorsque la condition n'est pas remplie. Un exemple pourrait être que votre élément de menu agit sur la sélection actuelle de GameObjects, que vous pouvez vérifier dans l'élément de menu du validateur.

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

Pour qu'un élément de menu du validateur fonctionne, vous devez créer deux fonctions statiques, à la fois avec l'attribut MenuItem et le même nom (la touche de raccourci importe peu). La différence entre les deux est que vous les marquez comme une fonction de validation ou non en passant un paramètre booléen.

Vous pouvez également définir l'ordre des éléments du menu en ajoutant une priorité. La priorité est définie par un entier que vous passez en troisième paramètre. Plus le nombre est petit, plus le chiffre est élevé dans la liste. Vous pouvez ajouter un séparateur entre deux éléments de menu en vous assurant qu'il y a au moins 10 chiffres entre la priorité des éléments du 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
}

Si vous avez une liste de menus combinant des éléments prioritaires et non hiérarchisés, les éléments non prioritaires seront séparés des éléments prioritaires.

Ensuite, vous ajoutez un élément de menu au menu contextuel d'un composant déjà existant. Vous devez démarrer le nom du MenuItem avec CONTEXT (sensible à la casse) et faire en sorte que votre fonction prenne un paramètre MenuCommand.

L'extrait suivant ajoute un élément de menu contextuel au composant Caméra.

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

Qui ressemble à ceci

Élément de menu contextuel de la caméra

Le paramètre MenuCommand vous donne accès à la valeur du composant et à toute donnée utilisateur envoyée avec celui-ci.

Vous pouvez également ajouter un élément de menu contextuel à vos propres composants en utilisant l'attribut ContextMenu. Cet attribut ne prend qu'un nom, aucune validation ou priorité et doit faire partie d'une méthode non statique.

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

Qui ressemble à ceci

Élément de menu contextuel personnalisé

Vous pouvez également ajouter des éléments de menu contextuel aux champs de votre propre composant. Ces éléments de menu apparaissent lorsque vous cliquez sur le champ auquel ils appartiennent et que vous pouvez exécuter des méthodes que vous avez définies dans ce composant. De cette façon, vous pouvez ajouter par exemple des valeurs par défaut ou la date actuelle, comme indiqué ci-dessous.

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

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

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

Qui ressemble à ceci

entrer la description de l'image ici

Gizmos

Les gizmos sont utilisés pour dessiner des formes dans la vue de la scène. Vous pouvez utiliser ces formes pour dessiner des informations supplémentaires sur vos GameObjects, par exemple le frustum qu'ils possèdent ou la plage de détection.

Voici deux exemples sur la façon de procéder

Un exemple

Cet exemple utilise les méthodes OnDrawGizmos et 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() );
    }
}

Dans cet exemple, nous avons deux méthodes pour dessiner des gizmos, l'une qui dessine lorsque l'objet est actif (OnDrawGizmos) et l'autre lorsque l'objet est sélectionné dans la hiérarchie (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;
}

D'abord, nous sauvegardons la matrice et la couleur du gizmo parce que nous allons le changer et que nous voulons le rétablir lorsque nous n'avons plus d'effet sur le dessin du gizmo.

Ensuite, nous voulons dessiner le tronc de notre objet, cependant, nous devons changer la matrice des Gizmos pour qu'elle corresponde à la position, à la rotation et à l'échelle. Nous avons également mis la couleur des Gizmos au rouge pour souligner le frustum. Lorsque cela est fait, nous pouvons appeler Gizmos.DrawFrustum pour dessiner le frustum dans la vue de la scène.

Lorsque nous avons fini de dessiner ce que nous voulons dessiner, nous réinitialisons la matrice et la couleur des Gizmos.

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

Nous voulons également dessiner une plage de détection lorsque nous sélectionnons notre objet GameObject. Cela se fait via la classe Handles , car la classe Gizmos ne possède aucune méthode pour les disques.

L'utilisation de cette forme de dessin donne des résultats dans la sortie indiquée ci-dessous.

Exemple deux

Cet exemple utilise l'attribut 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() );
        }
    }
}

Cette méthode vous permet de séparer les appels de gizmo de votre script. La plupart utilise le même code que l'autre exemple, à l'exception de deux choses.

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

Vous devez utiliser l'attribut DrawGizmo qui prend l'énumération GizmoType comme premier paramètre et Type comme second paramètre. Le Type doit être le type que vous souhaitez utiliser pour dessiner le gizmo.

La méthode de dessin du gizmo doit être statique, publique ou non publique et peut être nommée comme vous le souhaitez. Le premier paramètre est le type, qui doit correspondre au type transmis en tant que second paramètre dans l'attribut, et le second paramètre est l'énumération GizmoType qui décrit l'état actuel de votre objet.

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

L'autre différence est que pour vérifier quel est le GizmoType de l'objet, vous devez faire une vérification AND sur le paramètre et le type que vous voulez.

Résultat

Non séléctionné

exemple non sélectionné

Choisi

exemple un sélectionné

Fenêtre de l'éditeur

Pourquoi une fenêtre d'édition?

Comme vous l'avez peut-être vu, vous pouvez faire beaucoup de choses dans un inspecteur personnalisé (si vous ne savez pas ce qu'est un inspecteur personnalisé, consultez l'exemple ici: http://www.riptutorial.com/unity3d/topic/2506 / extend-the-editor Mais, à un moment donné, vous souhaiterez peut-être implémenter un panneau de configuration ou une palette de ressources personnalisée, dans laquelle vous utiliserez une fenêtre EditorWindow . (généralement à travers la barre supérieure), les tabuler, etc.

Créer un éditeur de baseWindow

Exemple simple

La création d'une fenêtre d'édition personnalisée est assez simple. Tout ce que vous avez à faire est d'étendre la classe EditorWindow et d'utiliser les méthodes Init () et OnGUI (). Voici un exemple simple:

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

Les 3 points importants sont:

  1. N'oubliez pas d'étendre EditorWindow
  2. Utilisez Init () comme indiqué dans l'exemple. EditorWindow.GetWindow vérifie si une CustomWindow est déjà créée. Sinon, cela créera une nouvelle instance. En utilisant ceci vous vous assurez que vous n'avez pas plusieurs instances de votre fenêtre en même temps
  3. Utilisez OnGUI () comme d'habitude pour afficher des informations dans votre fenêtre

Le résultat final ressemblera à ceci:

Éditeur personnalisé simpleWindow

Aller plus loin

Bien sûr, vous voudrez probablement gérer ou modifier certains actifs en utilisant cette EditorWindow. Voici un exemple d'utilisation de la classe Selection (pour obtenir la sélection active) et modification des propriétés d'actif sélectionnées via SerializedObject et 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();
        }
    }

Voici le résultat:
Fenêtre de l'éditeur personnalisé AnimationClip

Sujets avancés

Vous pouvez faire des choses vraiment avancées dans l'éditeur, et la classe EditorWindow est parfaite pour afficher une grande quantité d'informations. La plupart des ressources avancées de Unity Asset Store (telles que NodeCanvas ou PlayMaker) utilisent EditorWindow pour afficher les vues personnalisées.

Dessin dans le SceneView

Une chose intéressante à faire avec un EditorWindow est d'afficher des informations directement dans votre SceneView. De cette façon, vous pouvez créer un éditeur de carte / monde entièrement personnalisé, par exemple, en utilisant votre EditorWindow personnalisée en tant que palette de ressources et en écoutant les clics dans SceneView pour instancier de nouveaux objets. Voici un exemple :

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

Cela affichera la barre d'outils directement dans votre SceneView Interface utilisateur SceneView de EditorWindow

Voici un aperçu de la distance que vous pouvez parcourir:

Éditeur de carte Editeur de fenêtre



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow