Have script which runs in [ExecuteAlways] mode. It is mainly a function called at Start() and OnValidate() to update the position of objects based on changes in an Editor. 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. Putting OnHierarchyChange() with UpdateRing() doesn't seem to do anything. 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...
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();
}
}
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
.
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
!
Update
is only called when something in the Scene changed.
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
#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.
In case you need the Update
for something else you could alternatively also either do
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? 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
.