Editor
NGUITransformInspector.cs
//----------------------------------------------
// NGUI: Next-Gen UI kit
// Copyright 2011-2013 Tasharen Entertainment
// Multi-objects editing support added by
// Bardelot 'Cripple' Alexandre / Graphicstream.
//----------------------------------------------
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Transform))]
[CanEditMultipleObjects]
public clast NGUITransformInspector : Editor
{
SerializedProperty _rotProp;
SerializedProperty _posProp;
SerializedProperty _sclProp;
#region Helpers structs/enum
// Enumeration of axes.
public enum Vector3Axe
{
None = 0,
X = 1,
Y = 2,
Z = 4,
All = X | Y | Z
}
///
/// Describes a Vector update.
/// We track axes modification using Set#() methods and update a final vector on modified axes only.
/// We use this because of Multi-editing Objects. We want to update the axes one by one.
///
public struct Vector3Update
{
public Vector3Axe UpdatedAxes;
public Vector3 Value;
///
/// Force the update of the vector on all Axes using the given value.
///
public Vector3 ForceSet(Vector3 newValue)
{
UpdatedAxes = Vector3Axe.All;
Value = newValue;
return Value;
}
///
/// Sets the value of the Vector on modified axes.
///
public Vector3 Set(Vector3 newValue)
{
SetX(newValue.x);
SetY(newValue.y);
SetZ(newValue.z);
return Value;
}
///
/// Sets the value of the X axe.
///
public Vector3 SetX(float x)
{
if (x != Value.x)
{
Value.x = x;
UpdatedAxes |= Vector3Axe.X;
}
return Value;
}
///
/// Sets the value of the Y axe.
///
public Vector3 SetY(float y)
{
if (y != Value.y)
{
Value.y = y;
UpdatedAxes |= Vector3Axe.Y;
}
return Value;
}
///
/// Sets the value of the Z axe.
///
public Vector3 SetZ(float z)
{
if (z != Value.z)
{
Value.z = z;
UpdatedAxes |= Vector3Axe.Z;
}
return Value;
}
///
/// Checks if an axe has been modified.
///
public bool IsAxeUpdated(Vector3Axe axe)
{
return (UpdatedAxes & axe) == axe;
}
///
/// Validates the current Vector.
///
public Vector3Update Validate()
{
Vector3 vector = NGUITransformInspector.Validate(Value);
Set(vector);
return this;
}
///
/// Gets the vector updated on modified axes only.
///
public Vector3 GetUpdatedVector3(Vector3 vector)
{
if (UpdatedAxes == Vector3Axe.All) return Value;
if (IsAxeUpdated(Vector3Axe.X)) vector.x = Value.x;
if (IsAxeUpdated(Vector3Axe.Y)) vector.y = Value.y;
if (IsAxeUpdated(Vector3Axe.Z)) vector.z = Value.z;
return vector;
}
}
#endregion
void OnEnable()
{
_rotProp = serializedObject.FindProperty("m_LocalRotation");
_posProp = serializedObject.FindProperty("m_LocalPosition");
_sclProp = serializedObject.FindProperty("m_LocalScale");
}
///
/// Draw the inspector widget.
///
public override void OnInspectorGUI()
{
Transform trans = target as Transform;
EditorGUIUtility.LookLikeControls(15f);
bool forceValue = false; // Need this in unity 3.5 to fix a weird bug with GUI.changed
Vector3Update pos;
Vector3Update rot;
Vector3Update scale;
Vector3Axe axe;
serializedObject.Update();
// Position
EditorGUILayout.BeginHorizontal();
{
Vector3Update forcePos = new Vector3Update() { UpdatedAxes = Vector3Axe.None };
axe = GetMultipleValuesAxes(_posProp);
if (DrawButton("P", "Reset Position", axe != Vector3Axe.None || IsResetPositionValid(trans), 20f))
{
NGUIEditorTools.RegisterUndo("Reset Position", serializedObject.targetObjects);
forcePos.ForceSet(Vector3.zero);
trans.localPosition = Vector3.zero;
forceValue = true;
}
pos = DrawVector3(trans.localPosition, axe);
if (forcePos.UpdatedAxes == Vector3Axe.All)
{
pos = forcePos;
}
}
EditorGUILayout.EndHorizontal();
// Rotation
EditorGUILayout.BeginHorizontal();
{
Vector3Update forceRot = new Vector3Update() { UpdatedAxes = Vector3Axe.None };
axe = GetMultipleValuesAxes(_rotProp);
if (DrawButton("R", "Reset Rotation", axe != Vector3Axe.None || IsResetRotationValid(trans), 20f))
{
NGUIEditorTools.RegisterUndo("Reset Rotation", serializedObject.targetObjects);
forceRot.ForceSet(Vector3.zero);
trans.localEulerAngles = Vector3.zero;
forceValue = true;
}
rot = DrawVector3(trans.localEulerAngles, axe);
if (forceRot.UpdatedAxes == Vector3Axe.All)
{
rot = forceRot;
}
}
EditorGUILayout.EndHorizontal();
// Scale
EditorGUILayout.BeginHorizontal();
{
Vector3Update forceScale = new Vector3Update() { UpdatedAxes = Vector3Axe.None };
axe = GetMultipleValuesAxes(_sclProp);
if (DrawButton("S", "Reset Scale", axe != Vector3Axe.None || IsResetScaleValid(trans), 20f))
{
NGUIEditorTools.RegisterUndo("Reset Scale", serializedObject.targetObjects);
forceScale.ForceSet(Vector3.one);
trans.localScale = Vector3.one;
forceValue = true;
}
scale = DrawVector3(trans.localScale, axe);
if (forceScale.UpdatedAxes == Vector3Axe.All)
{
scale = forceScale;
}
}
EditorGUILayout.EndHorizontal();
// If something changes, set the transform values for each object selected.
// Apply changes on modified axes only.
if (GUI.changed || forceValue)
{
NGUIEditorTools.RegisterUndo("Transform Change", serializedObject.targetObjects);
pos.Validate();
rot.Validate();
scale.Validate();
foreach (Object obj in serializedObject.targetObjects)
{
trans = obj as Transform;
if (trans != null)
{
trans.localPosition = pos.GetUpdatedVector3(trans.localPosition);
trans.localEulerAngles = rot.GetUpdatedVector3(trans.localEulerAngles);
trans.localScale = scale.GetUpdatedVector3(trans.localScale);
}
}
}
}
///
/// Helper function that draws a button in an enabled or disabled state.
///
static bool DrawButton(string satle, string tooltip, bool enabled, float width)
{
if (enabled)
{
// Draw a regular button
return GUILayout.Button(new GUIContent(satle, tooltip), GUILayout.Width(width));
}
else
{
// Button should be disabled -- draw it darkened and ignore its return value
Color color = GUI.color;
GUI.color = new Color(1f, 1f, 1f, 0.25f);
GUILayout.Button(new GUIContent(satle, tooltip), GUILayout.Width(width));
GUI.color = color;
return false;
}
}
///
/// Helper function that draws a field of 3 floats.
///
static Vector3Update DrawVector3(Vector3 value, Vector3Axe multipleValueAxes)
{
GUILayoutOption opt = GUILayout.MinWidth(30f);
Vector3Update update = new Vector3Update() { Value = value };
update.SetX(DrawFloatField("X", value.x, ((multipleValueAxes & Vector3Axe.X) == Vector3Axe.None), opt));
update.SetY(DrawFloatField("Y", value.y, ((multipleValueAxes & Vector3Axe.Y) == Vector3Axe.None), opt));
update.SetZ(DrawFloatField("Z", value.z, ((multipleValueAxes & Vector3Axe.Z) == Vector3Axe.None), opt));
return update;
}
static float DrawFloatField(string name, float value, bool show, GUILayoutOption opt)
{
float result = value;
if (show)
{
result = EditorGUILayout.FloatField(name, value, opt);
}
else
{
if (!float.TryParse(EditorGUILayout.TextField(name, "-", opt), out result))
{
result = value;
}
}
return result;
}
///
/// Helper function that determines whether its worth it to show the reset position button.
///
static bool IsResetPositionValid(Transform targetTransform)
{
Vector3 v = targetTransform.localPosition;
return (v.x != 0f || v.y != 0f || v.z != 0f);
}
///
/// Helper function that determines whether its worth it to show the reset rotation button.
///
static bool IsResetRotationValid(Transform targetTransform)
{
Vector3 v = targetTransform.localEulerAngles;
return (v.x != 0f || v.y != 0f || v.z != 0f);
}
///
/// Helper function that determines whether its worth it to show the reset scale button.
///
static bool IsResetScaleValid(Transform targetTransform)
{
Vector3 v = targetTransform.localScale;
return (v.x != 1f || v.y != 1f || v.z != 1f);
}
///
/// Helper function that removes not-a-number values from the vector.
///
static Vector3 Validate(Vector3 vector)
{
vector.x = float.IsNaN(vector.x) ? 0f : vector.x;
vector.y = float.IsNaN(vector.y) ? 0f : vector.y;
vector.z = float.IsNaN(vector.z) ? 0f : vector.z;
return vector;
}
///
/// Gets the axes of a Vector3 which have multiple values.
///
Vector3Axe GetMultipleValuesAxes(SerializedProperty property)
{
Vector3Axe axes = Vector3Axe.None;
if (!property.hasMultipleDifferentValues)
{
return Vector3Axe.None;
}
// We know that we have at least one serialized object when this is called.
Vector3 current = GetVector(property, serializedObject.targetObjects[0] as Transform);
Vector3 next;
// We check that the value of the axe are all the same.
foreach (Object obj in serializedObject.targetObjects)
{
next = GetVector(property, obj as Transform);
if (next.x != current.x) axes |= Vector3Axe.X;
if (next.y != current.y) axes |= Vector3Axe.Y;
if (next.z != current.z) axes |= Vector3Axe.Z;
if (axes == Vector3Axe.All) return axes;
}
return axes;
}
///
/// Gets the vector of a transform (scale, position or eulerAngles) corresponding to a given property.
///
public Vector3 GetVector(SerializedProperty property, Transform transform)
{
if (property == _rotProp) return transform.localEulerAngles;
if (property == _posProp) return transform.localPosition;
if (property == _sclProp) return transform.localScale;
return Vector3.zero;
}
}