Дополнить интерфейс Unity, Custom Inspector

230
20 октября 2018, 13:40

Появилось требование к дополнению интерфейса самого unity, и хотелось бы узнать, это вообще возможно или нет.

Подробнее:
Есть проект который использует движок для создания схем помещений(их дизайна и тп), т.е. воссоздают магазины, квартиры и тп. Дизайнерам хотелось бы иметь какие-то функции или информацию(площадь помещения, тип объекта(при наведении), сколько объектов всего на сцене и т.п), которые отсутствуют в дефолтной версии. Возможно ли дописать это скриптами или как аддон?

Например: При наведении на объект, в редакторе, я могу видеть его Transform, Mesh Render и т.п, но хотел бы еще увидеть, например, Options, где было бы иия объекта, id и тп. Всем спасибо)

Answer 1

Данный пример иллюстрирует решение конкретной задачи - кастомный инспектор для дебаг-компонента, предназначенного для показа расстояния от выделенного объекта до окружающих его объектов. По аналогии можно решить любую другую задачу, но, подчеркиваю, я не использовал в данном примере какие-то сложные концепты, например, откат к предыдущей "версии" значений компонента через ctrl+z, это и прочие полезные вещи - тема отдельного вопроса.

Итак, приступим:

Сама "логика" дебага, который будет отрисовывать отрезок от выделенного объекта ко всем другим на основе заданного радиуса. Лично я не знаю какого-то рационального способа сделать это без использования физики, так что ее я и использую. Вроде все достаточно просто, если возникнут вопросы - дополню.

public class DebugBehaviour : MonoBehaviour {
    public float surroundingsCheckRadius = 10f;
    public bool drawDistanceToNearestObjects = false;
    public bool drawDistanceLabel = false;
    public Color debugInfoColor = Color.red;
    public void DisplayDistanceToSurroundings() {
        Gizmos.color = debugInfoColor;
        foreach (var col in Physics.OverlapSphere(transform.position, surroundingsCheckRadius)) {
            Gizmos.DrawLine(transform.position, col.transform.position);
            if (drawDistanceLabel) {
                DrawColoredLabel(col.transform.position, Vector3.Distance(transform.position, col.transform.position).ToString());
            }
        }
    }
    private void OnDrawGizmosSelected() {
        if (drawDistanceToNearestObjects) {
            DisplayDistanceToSurroundings();
        }
    }
    private void DrawColoredLabel(Vector3 pos, string text) {
        GUIStyle style = new GUIStyle();
        style.normal.textColor = debugInfoColor;
        Handles.Label(pos, text, style);
    }
}

Кастомный инспектор для нашего компонента. Из, возможно, новых вещей, стоит отметить атрибуты CustomEditor/MenuItem. Первый говорит Unity, для какого компонента нужно использовать этот GUI, второй добавляет в тулбар Unity новые варианты, путь к которым в параметре атрибута и прописывается, и вызывает данную функцию, в данном случае просто добавляет и удаляет компонент, такой подход для "не программистов" будет удобнее.

Также стоит отметить, что наследуемся мы от Editor и переопределяем OnInspectorGUI - тот метод, который и рисует окна инспектора. В нем важно первым делом получить ссылку на сам объект, для которого мы этот GUI и рисуем, а дальше просто используем API для создания, в данном случае, цветового поля, парочки bool`ов и float для радиуса. using UnityEditor;

[CustomEditor(typeof(DebugBehaviour))]
public class DebugBehaviourEditor : Editor {
    [MenuItem("Tools/Add Personal Debugger")]
    public static void AddDebugBehaviour() {
        foreach(var go in Selection.gameObjects) {
            if (!go.GetComponent<DebugBehaviour>()) {
                go.AddComponent<DebugBehaviour>();
            }
        }
    }
    [MenuItem("Tools/Remove Personal Debugger")]
    public static void RemoveDebugBehaviour() {
        foreach (var go in Selection.gameObjects) {
            DestroyImmediate(go.GetComponent<DebugBehaviour>());
        }
    }
    DebugBehaviour myTarget;
    public override void OnInspectorGUI() {
        myTarget = target as DebugBehaviour;
        myTarget.debugInfoColor = EditorGUILayout.ColorField("Debug color", myTarget.debugInfoColor);
        myTarget.drawDistanceToNearestObjects = 
            EditorGUILayout.ToggleLeft("Draw distances", myTarget.drawDistanceToNearestObjects);
        if (myTarget.drawDistanceToNearestObjects) {
            myTarget.surroundingsCheckRadius =
                EditorGUILayout.FloatField("Distance check radius", myTarget.surroundingsCheckRadius);
            myTarget.drawDistanceLabel =
                EditorGUILayout.Toggle("Draw distance text", myTarget.drawDistanceLabel);
        }
    }
}

Разумеется, это самый простой пример, но довольно показательный, как быстро и просто можно написать свой инспектор, дальше - документация и форум, о таких вещах можно вечно говорить, а стэк - далеко не форум :)

Примеры работы:

Update

Использование EditorWindow вместо инспектора компоненты.

Принцип работы почти такой же, за исключение создания окна - это нужно делать явно через GetWindow<EditorWindow>. Также нужно понимать, что "из коробки" Unity обновляет GUI окна при фокусе (фактически вызывает EditorWindow.Repaint()), а нам нужно отслеживать обновление выделения объектов, для чего мы собственноручно помещаем EditorWindow.Repaint() в OnSelectionChange() - колбек, зависящий как раз от изменения выделения в редакторе.

using UnityEngine;
using UnityEditor;
public class DebugBehaviourEditorWindow : EditorWindow {
    [MenuItem("Tools/Debug Behaviour Window")]
    public static void ShowWindow() {
        GetWindow<DebugBehaviourEditorWindow>("Debug Behaviour Window");
    }
    private void OnGUI() {
        if (Selection.gameObjects.Length != 0) {
            foreach (var go in Selection.gameObjects) {
                var reqComp = go.GetComponent<DebugBehaviour>();
                // проверяем, что наш шкаф из выделения действительно шкаф
                // ну и что-то делаем с ним
                if (reqComp) {
                    // на скорую руку (криво) выводим какие-то данные про наш шкаф
                    GUILayout.Label(go.name + " check radius is: " + reqComp.surroundingsCheckRadius.ToString());
                }
            }
        }
    }
    public void OnSelectionChange() {
        Repaint();
    }
}

Данное окно ничем не отличается в встроенных окон Unity - инспектора, ассет стора и т.д. - оно так же хорошо перемещается мышкой и прикрепляется куда угодно в редакторе, а внутренняя реализация безгранична (в разумных пределах).

READ ALSO
С# ssh клиент добавить логин и пароль &ldquo;su&rdquo;

С# ssh клиент добавить логин и пароль “su”

Хочу создать консольное приложение, которые бы подключается к Linux серверу, делает бэкап и выгружает все это в exelПроблема встала на авторизации...

198
Не обновляется изображение в Image

Не обновляется изображение в Image

Есть <Image x:Name="Zoomage" Height="100" Width="150" RenderTransformOrigin="05,0

218
Миграция на Microsoft.EntityFrameworkCore.SqlServer и JetBrains Rider

Миграция на Microsoft.EntityFrameworkCore.SqlServer и JetBrains Rider

Нужно что бы работало вот так First EF Core Console Application

223
Переопределение метода c#

Переопределение метода c#

Вопрос слегка глуповат, но в то же время я не нашел ответа на него

228