반응형
커스텀 에디터 스크립트
NGUI를 사용하던 지원하는 기능 중, 트랜스폼 컴포넌트를 커스텀해주는 기능이 있습니다. 단순하게 'P', 'R', 'S' 버튼을 눌러주는 것만으로 초기화 처리를 해주는 기능이 있는데, 이 기능을 개인적으로 사용하고자 작성했습니다.
스크립트
에디터 스크립트로써, 반드시 'Editor'라고 명시된 폴더 안에 생성하셔야합니다.
TransformInspector.cs
using UnityEngine;
using UnityEditor;
[CanEditMultipleObjects]
[CustomEditor(typeof(Transform), true)]
public class TransformInspector : Editor
{
[System.Flags]
enum Axes : byte
{
None = 0,
X = 1 << 1,
Y = 1 << 2,
Z = 1 << 3,
All = X | Y | Z,
}
Transform mTransform;
SerializedProperty mPosition;
SerializedProperty mRotation;
SerializedProperty mScale;
private void OnEnable()
{
mTransform = serializedObject.targetObject as Transform;
mPosition = serializedObject.FindProperty("m_LocalPosition");
mRotation = serializedObject.FindProperty("m_LocalRotation");
mScale = serializedObject.FindProperty("m_LocalScale");
}
public override void OnInspectorGUI()
{
EditorGUIUtility.labelWidth = 15f;
serializedObject.Update();
DrawPosition();
DrawRotation();
DrawScale();
serializedObject.ApplyModifiedProperties();
}
/// <summary>
/// Position 그리기
/// </summary>
private void DrawPosition()
{
GUILayout.BeginHorizontal();
bool reset = GUILayout.Button("P", GUILayout.Width(20f));
EditorGUILayout.PropertyField(mPosition.FindPropertyRelative("x"));
EditorGUILayout.PropertyField(mPosition.FindPropertyRelative("y"));
EditorGUILayout.PropertyField(mPosition.FindPropertyRelative("z"));
if (reset)
{
mPosition.vector3Value = Vector3.zero;
CancelTextFocus();
}
GUILayout.EndHorizontal();
}
/// <summary>
/// Rotation 그리기
/// </summary>
private void DrawRotation()
{
GUILayout.BeginHorizontal();
bool reset = GUILayout.Button("R", GUILayout.Width(20f));
Vector3 visible = mTransform.localEulerAngles;
visible.x = WrapAngle(visible.x);
visible.y = WrapAngle(visible.y);
visible.z = WrapAngle(visible.z);
// 선택한 여러 개체 중 서로 다른 값의 축이 존재하는지 체크
Axes diff = CheckDifference(mRotation);
// 축을 수정했는지 판단하는데 사용하는 변수
Axes changed = Axes.None;
float newX = FloatField("X", visible.x, (diff & Axes.X) != 0);
if (newX != visible.x) changed |= Axes.X;
float newY = FloatField("Y", visible.y, (diff & Axes.Y) != 0);
if (newY != visible.y) changed |= Axes.Y;
float newZ = FloatField("Z", visible.z, (diff & Axes.Z) != 0);
if (newZ != visible.z) changed |= Axes.Z;
if (changed != Axes.None)
{
Undo.RecordObjects(serializedObject.targetObjects, "Rotation");
foreach (Object obj in serializedObject.targetObjects)
{
Transform t = obj as Transform;
Vector3 v = t.localEulerAngles;
if ((changed & Axes.X) != 0) v.x = newX;
if ((changed & Axes.Y) != 0) v.y = newY;
if ((changed & Axes.Z) != 0) v.z = newZ;
t.localEulerAngles = v;
}
}
if (reset)
{
mRotation.quaternionValue = Quaternion.identity;
CancelTextFocus();
}
GUILayout.EndHorizontal();
}
/// <summary>
/// 회전값 보정해서 반환
/// </summary>
/// <param name="angle"></param>
/// <returns></returns>
private float WrapAngle(float angle)
{
while (angle > 180f) angle -= 360f;
while (angle < -180f) angle += 360f;
return angle;
}
/// <summary>
/// 다른 값이 존재하는지 체크
/// </summary>
/// <param name="rotation"></param>
/// <returns></returns>
private Axes CheckDifference(SerializedProperty rotation)
{
Axes axes = Axes.None;
// 여러개 선택이 되었는지
if (rotation.hasMultipleDifferentValues)
{
// 기준값
Vector3 original = rotation.quaternionValue.eulerAngles;
// 다른 값이 존재하는지 체크
foreach (Object obj in serializedObject.targetObjects)
{
Transform t = obj as Transform;
Vector3 other = t.localEulerAngles;
if (Mathf.Abs(other.x - original.x) > 0.0001f) axes |= Axes.X;
if (Mathf.Abs(other.y - original.y) > 0.0001f) axes |= Axes.Y;
if (Mathf.Abs(other.z - original.z) > 0.0001f) axes |= Axes.Z;
if (axes == Axes.All) break;
}
}
return axes;
}
/// <summary>
/// 커스텀 FloatField
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="hidden"></param>
/// <returns></returns>
private float FloatField(string name, float value, bool hidden)
{
if (hidden)
{
float newValue = value;
GUI.color = new Color(0.75f, 0.75f, 0.75f);
GUI.changed = false;
float.TryParse(EditorGUILayout.TextField(name, "─"), out newValue);
GUI.color = Color.white;
if (GUI.changed) return newValue;
}
else
{
return EditorGUILayout.FloatField(name, value);
}
return value;
}
/// <summary>
/// Sale 그리기
/// </summary>
private void DrawScale()
{
GUILayout.BeginHorizontal();
bool reset = GUILayout.Button("S", GUILayout.Width(20f));
EditorGUILayout.PropertyField(mScale.FindPropertyRelative("x"));
EditorGUILayout.PropertyField(mScale.FindPropertyRelative("y"));
EditorGUILayout.PropertyField(mScale.FindPropertyRelative("z"));
if (reset)
{
mScale.vector3Value = Vector3.one;
CancelTextFocus();
}
GUILayout.EndHorizontal();
}
private void CancelTextFocus() => GUIUtility.keyboardControl = 0;
}
마무리
단순하게 버튼을 누르는 것만으로 초기화 처리가 되니 편하게 사용할 수 있습니다.
참고 : NGUI
반응형
'Utils' 카테고리의 다른 글
Unity, 에셋 FileID 가져오기 (0) | 2023.05.14 |
---|---|
Unity, 박스 콜라이더 기즈모 (0) | 2023.05.11 |
NGUI, BBCode 태그 제거하기 (Replace) (4) | 2023.04.08 |
NGUI, SafeArea를 통한 아이폰 노치 영역 대응하기 (0) | 2023.03.30 |
Unity, 작업 중 Editor가 점점 느려지는 경우 (3) | 2023.03.18 |
댓글