Position and Scale are working as expected - but Rotation … I have no idea how to do this.
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
namespace JL
{
[CustomEditor(typeof(Transform))]
[CanEditMultipleObjects]
public class TransformInspector : Editor
{
public override VisualElement CreateInspectorGUI()
{
VisualElement root = new VisualElement();
Transform transform = target as Transform;
SerializedObject serializedObject = new SerializedObject(transform);
SerializedProperty posProp = serializedObject.FindProperty("m_LocalPosition");
SerializedProperty rotProp = serializedObject.FindProperty("m_LocalRotation");
SerializedProperty scaleProp = serializedObject.FindProperty("m_LocalScale");
Vector3Field position = new Vector3Field("Position");
position.BindProperty(posProp);
root.Add(position);
//Vector3Field rotation = new Vector3Field("Rotation");
//rotation.BindProperty(rotProp);
//root.Add(rotation);
//TransformUtils.SetInspectorRotation(transform, rotProp.vector3Value);
//Vector3 eulerRot = TransformUtils.GetInspectorRotation(transform);
//Quaternion quaternionRot = rotProp.quaternionValue;
Vector3Field scale = new Vector3Field("Scale");
scale.BindProperty(scaleProp);
root.Add(scale);
return root;
}
}
}
The big problem is, I don’t get an event when the User rotates the Object with the RotationGizmo - there is no “OnValueChanged”-Event in a SerializedProperty or SerializedObject - how would I know this happend without an event?
Is there a way to modify the binding, so it converts from Vector3 to Quaternion back and forth?
How to solve this? Has anyone made a custom Transform Inspector with UI Elements? I found nothing like this on the forums.
Ok that was actually easy xD
No idea why I struggled for two hours, just to find the solution 5min after asking the forum …
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
namespace JL
{
[CustomEditor(typeof(Transform))]
[CanEditMultipleObjects]
public class TransformInspector : Editor
{
Transform transform;
Vector3Field rotationField;
public override VisualElement CreateInspectorGUI()
{
VisualElement root = new VisualElement();
transform = target as Transform;
SerializedObject serializedObject = new SerializedObject(transform);
SerializedProperty posProp = serializedObject.FindProperty("m_LocalPosition");
SerializedProperty rotProp = serializedObject.FindProperty("m_LocalRotation");
SerializedProperty scaleProp = serializedObject.FindProperty("m_LocalScale");
Vector3Field position = new Vector3Field("Position");
position.BindProperty(posProp);
root.Add(position);
rotationField = new Vector3Field("Rotation");
//rotationField.BindProperty(rotProp);
root.Add(rotationField);
rotationField.value = TransformUtils.GetInspectorRotation(transform);
rotationField.RegisterValueChangedCallback(eventData =>
{
Undo.RecordObject(transform, "ChangeRotation");
TransformUtils.SetInspectorRotation(transform, eventData.newValue);
});
Vector3Field scale = new Vector3Field("Scale");
scale.BindProperty(scaleProp);
root.Add(scale);
return root;
}
private void OnSceneGUI()
{
rotationField.SetValueWithoutNotify(TransformUtils.GetInspectorRotation(transform));
}
}
}
Ok actually it was way, way more complicated than that thanks to multi object editing.
[CanEditMultipleObjects]
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
namespace JL
{
[CustomEditor(typeof(Transform))]
[CanEditMultipleObjects]
public class CustomTransformInspector : Editor
{
Vector3Field positionField;
Vector3Field rotationField;
Vector3Field scaleField;
struct MixedHelper
{
public bool x;
public bool y;
public bool z;
}
public override VisualElement CreateInspectorGUI()
{
VisualElement root = new VisualElement();
positionField = new Vector3Field("Position");
root.Add(positionField);
positionField.RegisterValueChangedCallback(eventData =>
{
foreach (GameObject selected in Selection.gameObjects)
{
Undo.RecordObject(selected.transform, "ChangePosition");
selected.transform.localPosition = eventData.newValue;
}
});
rotationField = new Vector3Field("Rotation");
root.Add(rotationField);
rotationField.RegisterValueChangedCallback(eventData =>
{
foreach (GameObject selected in Selection.gameObjects)
{
Undo.RecordObject(selected.transform, "ChangeRotation");
TransformUtils.SetInspectorRotation(selected.transform, eventData.newValue);
}
});
scaleField = new Vector3Field("Scale");
root.Add(scaleField);
scaleField.RegisterValueChangedCallback(eventData =>
{
foreach (GameObject selected in Selection.gameObjects)
{
Undo.RecordObject(selected.transform, "ChangeScale");
selected.transform.localScale = eventData.newValue;
}
});
OnSceneGUI();
Label label = new Label("It's working");
root.Add(label);
return root;
}
private void OnSceneGUI()
{
Vector3 displayPos;
Vector3 displayRot;
Vector3 displayScale;
if (!Selection.activeGameObject || !Selection.activeGameObject.transform) return;
Transform mainT = Selection.activeGameObject.transform;
displayPos = mainT.localPosition;
displayRot = TransformUtils.GetInspectorRotation(mainT);
displayScale = mainT.localScale;
MixedHelper posMixed = new MixedHelper();
MixedHelper rotMixed = new MixedHelper();
MixedHelper scaleMixed = new MixedHelper();
foreach (GameObject selected in Selection.gameObjects)
{
Transform t = selected.transform;
if (displayPos.x != t.localPosition.x) posMixed.x = true;
if (displayPos.y != t.localPosition.y) posMixed.y = true;
if (displayPos.z != t.localPosition.z) posMixed.z = true;
Vector3 rotation = TransformUtils.GetInspectorRotation(t);
if (displayRot.x != rotation.x) rotMixed.x = true;
if (displayRot.y != rotation.y) rotMixed.y = true;
if (displayRot.z != rotation.z) rotMixed.z = true;
if (displayScale.x != t.localScale.x) scaleMixed.x = true;
if (displayScale.y != t.localScale.y) scaleMixed.y = true;
if (displayScale.z != t.localScale.z) scaleMixed.z = true;
}
positionField.SetValueWithoutNotify(displayPos);
rotationField.SetValueWithoutNotify(displayRot);
scaleField.SetValueWithoutNotify(displayScale);
XYZisMixed(positionField, posMixed);
XYZisMixed(rotationField, rotMixed);
XYZisMixed(scaleField, scaleMixed);
}
void XYZisMixed(Vector3Field vector3Field, MixedHelper mixed)
{
ShowMixed(vector3Field, "x", mixed.x);
ShowMixed(vector3Field, "y", mixed.y);
ShowMixed(vector3Field, "z", mixed.z);
}
void ShowMixed(Vector3Field vector3Field, string axis, bool mixed)
{
TextInputBaseField<float> inputText = vector3Field.Q<FloatField>($"unity-{axis}-input")
.Q<TextInputBaseField<float>>();
inputText.showMixedValue = mixed;
}
}
}
RSolids:
This is fantastic
Just a quick Note: I changed “OnSceneGUI” to “Update”, using “EditorApplication.update” like so:
private void OnEnable()
{
EditorApplication.update -= Update;
EditorApplication.update += Update;
}
private void OnDisable()
{
EditorApplication.update -= Update;
}
private void Update()
{
// not initialized
if (localPositionField == null) return;
GetPosRotScaleValues(); // original OnSceneGUI() method
}
because “OnSceneGUI” is only executed when Gizmos are turned on and we want the inspector to be independent of Gizmos rendering.
2 Likes