UnityEditor 实现如 BoxCollider 的编辑功能
CastShapeBase.cs
using System.Collections;
using UnityEngine;
public abstract class CastShapeBase : MonoBehaviour {
#if UNITY_EDITOR
protected readonly Color m_gizomsColor = Color.cyan;
protected virtual void Reset () {
}
protected virtual void OnDrawGizmosSelected () {
}
#endif
}
CastBox.cs
using System.Collections;
using UnityEngine;
public class CastBox : CastShapeBase {
public Vector3 center;
public Vector3 size = Vector3.one;
#if UNITY_EDITOR
protected override void Reset () {
center = Vector3.zero;
size = Vector3.one;
}
protected override void OnDrawGizmosSelected () {
Matrix4x4 gizmosMatrixRecord = Gizmos.matrix;
Color gizmosColorRecord = Gizmos.color;
Gizmos.color = m_gizomsColor;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireCube(center, size);
Gizmos.color = gizmosColorRecord;
Gizmos.matrix = gizmosMatrixRecord;
}
#endif
}
CastBoxEditor.cs 放置在名为 Editor 的文件夹下
#if UNITY_EDITOR
using System.Collections;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
[EditorTool("Edit Cast Shape", typeof(CastBox))]
public class CastBoxTool : CastShapeTool<CastBox> {
private readonly BoxBoundsHandle m_boundsHandle = new BoxBoundsHandle();
protected override PrimitiveBoundsHandle boundsHandle {
get { return m_boundsHandle; }
}
protected override void CopyColliderPropertiesToHandle (CastBox castShape) {
m_boundsHandle.center = TransformColliderCenterToHandleSpace(castShape.transform, castShape.center);
m_boundsHandle.size = Vector3.Scale(castShape.size, castShape.transform.lossyScale);
}
protected override void CopyHandlePropertiesToCollider (CastBox castShape) {
castShape.center = TransformHandleCenterToColliderSpace(castShape.transform, m_boundsHandle.center);
Vector3 size = Vector3.Scale(m_boundsHandle.size, InvertScaleVector(castShape.transform.lossyScale));
size = new Vector3(Mathf.Abs(size.x), Mathf.Abs(size.y), Mathf.Abs(size.z));
castShape.size = size;
}
}
[CustomEditor(typeof(CastBox))]
[CanEditMultipleObjects]
public class CastBoxEditor : Editor {
private SerializedProperty m_script;
private SerializedProperty m_center;
private SerializedProperty m_size;
private void OnEnable () {
m_script = serializedObject.FindProperty("m_Script");
m_center = serializedObject.FindProperty("center");
m_size = serializedObject.FindProperty("size");
}
public override void OnInspectorGUI () {
serializedObject.Update();
GUI.enabled = false;
EditorGUILayout.PropertyField(m_script);
GUI.enabled = true;
EditorGUILayout.EditorToolbarForTarget(EditorGUIUtility.TrTempContent("Edit Shape"), target);
EditorGUILayout.PropertyField(m_center);
EditorGUILayout.PropertyField(m_size);
serializedObject.ApplyModifiedProperties();
}
}
#endif
CastShapeTool.cs 放置在名为 Editor 的文件夹下
#if UNITY_EDITOR
using System.Collections;
using System.Reflection;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
public abstract class CastShapeTool<T> : EditorTool where T : CastShapeBase {
protected readonly Color m_handleEnableColor = Color.cyan;
protected readonly Color m_handleDisableColor = new Color(0f, 0.7f, 0.7f);
public override GUIContent toolbarIcon {
get {
PropertyInfo propertyInfo = typeof(PrimitiveBoundsHandle).GetProperty("editModeButton", BindingFlags.NonPublic | BindingFlags.Static);
return (GUIContent)propertyInfo.GetValue(null);
}
}
protected abstract PrimitiveBoundsHandle boundsHandle { get; }
protected abstract void CopyColliderPropertiesToHandle (T castShape);
protected abstract void CopyHandlePropertiesToCollider (T castShape);
protected Vector3 InvertScaleVector (Vector3 scaleVector) {
for (int axis = 0; axis < 3; ++axis)
scaleVector[axis] = scaleVector[axis] == 0f ? 0f : 1f / scaleVector[axis];
return scaleVector;
}
public override void OnToolGUI (EditorWindow window) {
foreach (var obj in targets) {
if (!(obj is T castShape) || Mathf.Approximately(castShape.transform.lossyScale.sqrMagnitude, 0f))
continue;
// collider matrix is center multiplied by transform's matrix with custom postmultiplied lossy scale matrix
using (new Handles.DrawingScope(Matrix4x4.TRS(castShape.transform.position, castShape.transform.rotation, Vector3.one))) {
CopyColliderPropertiesToHandle(castShape);
boundsHandle.SetColor(castShape.enabled ? m_handleEnableColor : m_handleDisableColor);
EditorGUI.BeginChangeCheck();
boundsHandle.DrawHandle();
if (EditorGUI.EndChangeCheck()) {
Undo.RecordObject(castShape, string.Format("Modify {0}", ObjectNames.NicifyVariableName(target.GetType().Name)));
CopyHandlePropertiesToCollider(castShape);
}
}
}
}
protected static Vector3 TransformColliderCenterToHandleSpace (Transform colliderTransform, Vector3 colliderCenter) {
return Handles.inverseMatrix * (colliderTransform.localToWorldMatrix * colliderCenter);
}
protected static Vector3 TransformHandleCenterToColliderSpace (Transform colliderTransform, Vector3 handleCenter) {
return colliderTransform.localToWorldMatrix.inverse * (Handles.matrix * handleCenter);
}
}
#endif