简体   繁体   English

Unity 编辑器模式 - 当将子项添加/删除到脚本所在的对象时,如何让 OnHierarchyChange 重新绘制场景?

[英]Unity Editor Mode - How do I get OnHierarchyChange to repaint the scene when children are added/deleted to the object the script is on?

Have script which runs in [ExecuteAlways] mode.有在 [ExecuteAlways] 模式下运行的脚本。 It is mainly a function called at Start() and OnValidate() to update the position of objects based on changes in an Editor.它主要是在 Start() 和 OnValidate() 调用的函数,用于根据编辑器中的更改更新对象的位置。 This all works fine.这一切正常。

When an object is added as a child to the object with the script in the Hierarchy window I want UpdateRing() to be called and integrate that into the ring.当使用层次结构窗口中的脚本将对象作为子对象添加到对象时,我希望调用 UpdateRing() 并将其集成到环中。 Putting OnHierarchyChange() with UpdateRing() doesn't seem to do anything.将 OnHierarchyChange() 与 UpdateRing() 放在一起似乎没有任何作用。 In other questions OnHierarchyChange() is put in the Editor file but I don't know how I can put OnHierarchyChange() in the Editor file and call UpdateRing()...or if that is even something I should do...在其他问题中 OnHierarchyChange() 放在编辑器文件中,但我不知道如何将 OnHierarchyChange() 放在编辑器文件中并调用 UpdateRing() ......或者如果这是我应该做的事情......

GameObject Code:游戏对象代码:

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

Editor Code:编辑器代码:

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

OnHierarchyChange belongs to the EditorWindow class and as such will only work within scripts derived from EditorWindow . OnHierarchyChange属于EditorWindow类,因此只能在从EditorWindow派生的脚本中工作。

By using in a Monobehaviour script you are simply creating a new method called OnHierarchyChange within your script which is not associated with the Unity message EditorWindow.OnHierarchyChange .通过在Monobehaviour脚本中使用,您只需在脚本中创建一个名为OnHierarchyChange的新方法,该方法与 Unity消息EditorWindow.OnHierarchyChange

Take a look at: https://docs.unity3d.com/ScriptReference/EditorApplication-hierarchyChanged.html看看: https : //docs.unity3d.com/ScriptReference/EditorApplication-hierarchyChanged.html

I was able to modify the example code a bit to execute within a Monobehaviour with [ExecuteAlways] attribute.我能够稍微修改示例代码以在具有[ExecuteAlways]属性的Monobehaviour执行。

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

Alternatively you could modify your editor code to be an EditorWindow and use EditorWindow.OnHeirarchyChange but then you would need the window open to allow it to execute.或者,您可以将编辑器代码修改为EditorWindow并使用EditorWindow.OnHeirarchyChange但随后您需要打开窗口以允许它执行。

As already mentioned in other answers OnHierarchyChange is a message of EditorWindow and will only be called by Unity in this type of class, not in a MonoBehaviour .在其他的答案已经提到OnHierarchyChange是消息EditorWindow并且只能由Unity在这种类型的类,而不是在被称为MonoBehaviour


However, the solution is actually quite simple!然而,解决方法其实很简单!

If tagging your class [ExecuteAllways] or[ExecuteInEditMode] the method in MonoBehaviour classes getting called if anything in the Scene changes is simply Update !如果标记您的类[ExecuteAllways][ExecuteInEditMode]MonoBehaviour类中的方法会在场景中的任何内容发生更改时被调用,只是Update

Update is only called when something in the Scene changed.仅当场景中的某些内容发生更改时才会调用Update

Changing something in the hierarchy implies that also something in the scene is changed.更改层次结构中的某些内容意味着场景中的某些内容也发生了变化。

So you can simply put it I Update and only have to prevent it from running later in the build application所以你可以简单地把它放在 I Update并且只需要防止它在构建应用程序中运行

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

the #if UNITY_EDIROR preprocessor will make sure this part is removed in a build avoiding overhead of Update getting called entirely. #if UNITY_EDIROR预处理器将确保在构建中删除这部分,避免Update被完全调用的开销。

In case you need the Update for something else you could alternatively also either do如果您需要Update其他内容,您也可以这样做

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

        ...
    }

or also或者也

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

    ...
}

Sidenotes旁注

  • Actually what is the custom Editor script for?实际上,自定义Editor脚本是做什么用的? I don't see it adding anything that wouldn't be drawn in the Inspector by default anyway ...我没有看到它添加任何默认情况下不会在检查器中绘制的东西......

  • [Serializable] is redundant on a class of type MonoBehaviour . [Serializable]MonoBehaviour类型的类上是多余的。

Put...放...

void OnHierarchyChanged()
    { 
        UpdateRing();
    } 

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

...in the ObjectRing class and it does update immediately on changes to the hierarchy without regards the selection. ...在 ObjectRing 类中,它会在更改层次结构时立即更新,而不考虑选择。

Not sure its the best way...but it works.不确定它是最好的方法……但它有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM