Режим редактора Unity - Как я могу заставить OnHierarchyChange перерисовать сцену, когда дочерние элементы добавляются / удаляются к объекту, на котором находится скрипт? - PullRequest
0 голосов
/ 28 января 2020

Есть скрипт, который работает в режиме [ExecuteAlways]. В основном это функции, вызываемые в Start () и OnValidate () для обновления положения объектов на основе изменений в редакторе. Все это прекрасно работает.

Когда объект добавляется в качестве дочернего объекта к объекту со скриптом в окне Hierarchy, я хочу вызвать UpdateRing () и интегрировать его в кольцо. Установка OnHierarchyChange () с UpdateRing (), похоже, ничего не делает. В других вопросах OnHierarchyChange () помещается в файл редактора, но я не знаю, как я могу поместить OnHierarchyChange () в файл редактора и вызвать UpdateRing () ... или, если это то, что я должен сделать ...

Код GameObject:

using UnityEngine;
using System;
using System.ComponentModel;

[Serializable]
[ExecuteAlways]
public class ObjectsRing : MonoBehaviour
{
//public float radius = { get { return m_Radius; } set { m_Radius = value; } }
[Range(0f, 100f)]
public float radius = 10;

[Range(0f,360f)]
public float beginAngle = 0f;

[Range(0f,360f)]
public float endAngle = 360f;

public bool flip = false;

public enum orientationList {[Description("X-up")] Xup, [Description("Y-up")] Yup, [Description("Z-up")] Zup};

public orientationList orientation;    

// Start is called before the first frame update
void Start()
{
    UpdateRing();
}

// OnValidate is called when fields are changed in an Editor
void OnValidate()
{
    UpdateRing();       
}

// OnHierarchyChange is called when changes are made in the Hierarchy pane. 
void OnHierarchyChange()
{
    UpdateRing();  
}

private void UpdateRing()
{
    //Input error handling
    if (endAngle < beginAngle)
    {
        float tempAngle = beginAngle; 
        beginAngle = endAngle;
        endAngle = tempAngle;
    }

    // Attach mesh, rotate object and add material
    float objectAngle = (endAngle - beginAngle) / (transform.childCount);
    float rotation = beginAngle;
    for (int cnt = 0; cnt < transform.childCount; cnt++)
    {
        // Translate and rotate each object
        transform.GetChild(cnt).GetComponent<Transform>().localPosition = new Vector3(radius, 0, 0);
        // transform.GetChild(cnt).GetComponent<Transform>().rotation = Quaternion.Euler(0, rotation, 0);
        rotation = beginAngle + cnt * objectAngle;
        transform.GetChild(cnt).RotateAround(transform.position, new Vector3(0,1,0), rotation);
        transform.GetChild(cnt).LookAt(transform.position);
        if (flip)
            {
            transform.GetChild(cnt).Rotate(new Vector3(0,180,0));
        }
            switch (orientation)
            {
                case orientationList.Xup:
                {
                    transform.GetChild(cnt).Rotate(new Vector3(0,0,0));
                    break;                
                }         
                case orientationList.Yup:
                {
                    transform.GetChild(cnt).Rotate(new Vector3(90,0,0));
                    break;                
                }         
                case orientationList.Zup:
                {
                    transform.GetChild(cnt).Rotate(new Vector3(0,0,90));
                    break;                
                }                  
            }
        }
    }
}

Код редактора:

using UnityEditor;

[CustomEditor(typeof(ObjectsRing)), CanEditMultipleObjects]
public class ObjectsRingEditor : Editor
{
    private SerializedProperty radiusProperty;
    private SerializedProperty beginAngleProperty;
    private SerializedProperty endAngleProperty;
    private SerializedProperty flipProperty;
    private SerializedProperty orientationProperty;

    public void OnEnable()
    {
        radiusProperty = serializedObject.FindProperty("radius");
        beginAngleProperty = serializedObject.FindProperty("beginAngle");
        endAngleProperty = serializedObject.FindProperty("endAngle");
        flipProperty = serializedObject.FindProperty("flip");
        orientationProperty = serializedObject.FindProperty("orientation");
    }


    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        radiusProperty.floatValue = EditorGUILayout.Slider ("Radius", radiusProperty.floatValue, 0, 100);
        beginAngleProperty.floatValue = EditorGUILayout.Slider ("Begin Angle", beginAngleProperty.floatValue, 0, 360);
        endAngleProperty.floatValue = EditorGUILayout.Slider ("End Angle", endAngleProperty.floatValue, 0, 360);
        flipProperty.boolValue = EditorGUILayout.Toggle ("Flip", flipProperty.boolValue);
        orientationProperty.enumValueIndex = EditorGUILayout.Popup ("Orientation", orientationProperty.enumValueIndex, orientationProperty.enumDisplayNames);

        serializedObject.ApplyModifiedProperties();
        EditorApplication.update.Invoke();
    }
}

Ответы [ 3 ]

2 голосов
/ 29 января 2020

OnHierarchyChange принадлежит классу EditorWindow и поэтому будет работать только в сценариях, полученных из EditorWindow.

Используя сценарий Monobehaviour, вы просто создание в вашем скрипте нового метода с именем OnHierarchyChange, который не связан с сообщением Unity EditorWindow.OnHierarchyChange.

Взгляните на: https://docs.unity3d.com/ScriptReference/EditorApplication-hierarchyChanged.html

Мне удалось немного изменить пример кода, чтобы он выполнялся в атрибуте Monobehaviour с атрибутом [ExecuteAlways].

using UnityEditor;
using UnityEngine;

[ExecuteAlways]
public class HierarchyMonitor : MonoBehaviour
{
    static HierarchyMonitor()
    {
        EditorApplication.hierarchyChanged += OnHierarchyChanged;
    }

    static void OnHierarchyChanged()
    {
        Debug.Log("Heirarchy Has changed");
        //do you ring update here
    }
}

В качестве альтернативы вы можете изменить код вашего редактора на EditorWindow и использовать EditorWindow.OnHeirarchyChange, но тогда вам потребуется открытое окно, чтобы оно могло выполняться.

1 голос
/ 29 января 2020

Как уже упоминалось в других ответах OnHierarchyChange является сообщением EditorWindow и будет вызываться Unity только в этом типе класса, а не в MonoBehaviour.


Однако решение на самом деле довольно простое!

Если вы пометите свой класс [ExecuteAllways] или [ExecuteInEditMode] метод в MonoBehaviour классы, вызываемые, если что-либо в Сцене изменяется просто Update!

Update вызывается только тогда, когда что-то в Сцене изменилось.

Изменение чего-либо в иерархии подразумевает, что что-то в сцене также изменяется.

Таким образом, вы можете просто указать это I Update и только запретить его запуск в приложении для сборки

#if UNITY_EDITOR
    private void Update()
    {
        UpdateRing();
    }
#endif

препроцессор #if UNITY_EDIROR будет следить за тем, чтобы эта часть была удалена в сборке, избегая накладных расходов при вызове Update полностью.

В случае, если вам нужен Update для чего-то другого, вы также можете либо сделать

    private void Update()
    {
#if UNITY_EDITOR
        UpdateRing();
#endif

        ...
    }

или также

private void Update()
{
    if(Application.isEditor)
    {
        UpdateRing();
    }

    ...
}

Sidenotes

  • На самом деле для чего нужен пользовательский скрипт Editor? Я не вижу добавления чего-либо, что не было бы нарисовано в Инспекторе по умолчанию в любом случае ...

  • [Serializable] избыточно для класса типа MonoBehaviour.

0 голосов
/ 29 января 2020

Поместите ...

void OnHierarchyChanged()
    { 
        UpdateRing();
    } 

void Update()
    {
        EditorApplication.hierarchyChanged += OnHierarchyChanged;
    } 

... в класс ObjectRing, и он немедленно обновляет изменения иерархии без учета выбора.

Не уверен, что это лучший способ ... но он работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...