Unity 编辑器模式 - 当 children 是 added/deleted 到 object 脚本打开时,如何让 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?
有在 [ExecuteAlways] 模式下运行的脚本。它主要是在 Start() 和 OnValidate() 调用的函数,用于根据编辑器中的更改更新 objects 的位置。这一切都很好。
当使用层次结构中的脚本将 object 作为 child 添加到 object 时 window 我希望调用 UpdateRing() 并集成它入环。将 OnHierarchyChange() 与 UpdateRing() 放在一起似乎没有任何作用。在其他问题中,OnHierarchyChange() 被放在编辑器文件中,但我不知道如何将 OnHierarchyChange() 放在编辑器文件中并调用 UpdateRing() ......或者如果那是我应该做的......
游戏对象代码:
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();
}
}
OnHierarchyChange
属于 EditorWindow
class,因此只能在从 EditorWindow
.
派生的脚本中使用
通过在 Monobehaviour
脚本中使用,您只是在脚本中创建一个名为 OnHierarchyChange
的新方法,该方法与 Unity message EditorWindow.OnHierarchyChange
。
看看:https://docs.unity3d.com/ScriptReference/EditorApplication-hierarchyChanged.html
我能够稍微修改示例代码以在具有 [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
}
}
或者,您可以将编辑器代码修改为 EditorWindow
并使用 EditorWindow.OnHeirarchyChange
,但您需要打开 window 才能执行。
放...
void OnHierarchyChanged()
{
UpdateRing();
}
void Update()
{
EditorApplication.hierarchyChanged += OnHierarchyChanged;
}
...在 ObjectRing class 中,它会在不考虑选择的情况下立即更新层次结构。
不确定这是最好的方法...但它有效。
正如其他答案中已经提到的那样,OnHierarchyChange
是 EditorWindow
的消息,Unity 只会在这种类型的 class 中调用,而不会在 MonoBehaviour
中调用.
不过,解决方法其实很简单!
如果标记您的 class [ExecuteAllways]
or [ExecuteInEditMode]
MonoBehaviour
class 中的方法,如果场景中的任何内容发生变化,就会调用 Update
!
Update
is only called when something in the Scene changed.
更改层次结构中的某些内容意味着场景中的某些内容也会更改。
所以你可以简单地把它放在我 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();
}
...
}
旁注
实际上,自定义 Editor
脚本的用途是什么?我没有看到它添加任何默认情况下不会在检查器中绘制的内容...
[Serializable]
在 MonoBehaviour
类型的 class 上是多余的。
有在 [ExecuteAlways] 模式下运行的脚本。它主要是在 Start() 和 OnValidate() 调用的函数,用于根据编辑器中的更改更新 objects 的位置。这一切都很好。
当使用层次结构中的脚本将 object 作为 child 添加到 object 时 window 我希望调用 UpdateRing() 并集成它入环。将 OnHierarchyChange() 与 UpdateRing() 放在一起似乎没有任何作用。在其他问题中,OnHierarchyChange() 被放在编辑器文件中,但我不知道如何将 OnHierarchyChange() 放在编辑器文件中并调用 UpdateRing() ......或者如果那是我应该做的......
游戏对象代码:
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();
}
}
OnHierarchyChange
属于 EditorWindow
class,因此只能在从 EditorWindow
.
通过在 Monobehaviour
脚本中使用,您只是在脚本中创建一个名为 OnHierarchyChange
的新方法,该方法与 Unity message EditorWindow.OnHierarchyChange
。
看看:https://docs.unity3d.com/ScriptReference/EditorApplication-hierarchyChanged.html
我能够稍微修改示例代码以在具有 [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
}
}
或者,您可以将编辑器代码修改为 EditorWindow
并使用 EditorWindow.OnHeirarchyChange
,但您需要打开 window 才能执行。
放...
void OnHierarchyChanged()
{
UpdateRing();
}
void Update()
{
EditorApplication.hierarchyChanged += OnHierarchyChanged;
}
...在 ObjectRing class 中,它会在不考虑选择的情况下立即更新层次结构。
不确定这是最好的方法...但它有效。
正如其他答案中已经提到的那样,OnHierarchyChange
是 EditorWindow
的消息,Unity 只会在这种类型的 class 中调用,而不会在 MonoBehaviour
中调用.
不过,解决方法其实很简单!
如果标记您的 class [ExecuteAllways]
or [ExecuteInEditMode]
MonoBehaviour
class 中的方法,如果场景中的任何内容发生变化,就会调用 Update
!
Update
is only called when something in the Scene changed.
更改层次结构中的某些内容意味着场景中的某些内容也会更改。
所以你可以简单地把它放在我 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();
}
...
}
旁注
实际上,自定义
Editor
脚本的用途是什么?我没有看到它添加任何默认情况下不会在检查器中绘制的内容...[Serializable]
在MonoBehaviour
类型的 class 上是多余的。