内容将会持续更新,有错误的地方欢迎指正,谢谢!
|
拥有更好的学习体验 —— 不断努力,不断进步,不断探索 |
|
助力快速掌握 BoxCollider 编辑器扩展 为初学者节省宝贵的学习时间,避免困惑! |
前言:
之前在项目中想对一个对象做射线交互,然后给这个对象添加碰撞盒,发现这个对象下面还有很多子对
象,添加的碰撞盒不能完全包含这个对象及它下面的所有子对象,所以对BoxCollider进行了一个编辑器扩
展,实现当点击一个按钮时可以让这个BoxCollider包含该对象及其子对象。
TechX 教程效果:

文章目录
一、查找BoxCollider编辑器扩展类
1、安装.net反编译工具.NET Reflector
反编译工具.NET Reflector可以去它的官网进行下载。
网上也有很多破解教程,可以自行去百度进行搜索,对于大家来说也是比较简单的,这里就不再详细讲解。
安装好后是这样的:

我这里安装的是.NET Reflector11.1版本。
2、反编译UnityEditor.dll
打开.NET Reflector软件,选择UnityEditor.dll集合进行反编译

在搜索框中输入BoxCollider进行搜索,可以看到有一个BoxColliderEditor类,
查看右侧代码有CustomEditor(typeof(BoxCollider)),我们知道CustomEditor类
为BoxCollider的编辑器类。

二、创建编辑器扩展类BoxColliderExtendEditor
在Unity中创建一个编辑器类BoxColliderExtendEditor,将这个类放到Editor文件夹中
通过反射(Reflection)获取到BoxCollider的编辑器类
private IEnumerator Init()
{
Type type = Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.BoxColliderEditor", false);
yield return new WaitUntil(() => type != null);
m_Editor = Editor.CreateEditor(target, type);
}
扩展对象及其所有子对象的边界框Bound
/// <summary>
/// 扩展边界
/// </summary>
private Bounds? CalculateWorldBounds()
{
var renderers = Target.GetComponentsInChildren<MeshRenderer>(m_IncludeInactive)
.Where(r => r != null)
.ToArray();
if (renderers.Length == 0)
{
// 尝试查找SkinnedMeshRenderer
var skinnedRenderers = Target.GetComponentsInChildren<SkinnedMeshRenderer>(m_IncludeInactive)
.Where(r => r != null)
.ToArray();
if (skinnedRenderers.Length == 0)
return null;
return CalculateCombinedBounds(skinnedRenderers.Select(r => r.bounds));
}
return CalculateCombinedBounds(renderers.Select(r => r.bounds));
}
private Bounds CalculateCombinedBounds(System.Collections.Generic.IEnumerable<Bounds> boundsCollection)
{
var boundsArray = boundsCollection.ToArray();
if (boundsArray.Length == 0)
return new Bounds(Vector3.zero, Vector3.zero);
var combinedBounds = boundsArray[0];
for (int i = 1; i < boundsArray.Length; i++)
{
combinedBounds.Encapsulate(boundsArray[i]);
}
return combinedBounds;
}
下面贴出完整的代码:
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using System.Collections;
using System.Linq;
[CustomEditor(typeof(BoxCollider))]
public class ColliderExtendEditor : Editor
{
private bool m_IsFlip;
private bool m_IncludeInactive = false;
private Bounds? m_LastCalculatedBounds;
// 样式定义
private GUIStyle m_HeaderStyle;
private GUIStyle m_ButtonStyle;
private bool isApply;
private void InitializeStyles()
{
m_HeaderStyle = new GUIStyle(EditorStyles.boldLabel)
{
fontSize = 12,
alignment = TextAnchor.MiddleLeft
};
m_ButtonStyle = new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(10, 10, 5, 5),
fixedHeight = 25
};
}
private BoxCollider Target => (BoxCollider)target;
public override void OnInspectorGUI()
{
InitializeStyles();
// 绘制默认的BoxCollider编辑器
DrawDefaultInspectorFallback();
// 绘制扩展功能
DrawExtendedFeatures();
}
private void OnSceneGUI()
{
if (m_LastCalculatedBounds.HasValue && !isApply)
{
Handles.color = new Color(1, 1, 0, 0.8f);
Vector3 worldCenter = m_LastCalculatedBounds.Value.center;
Vector3 worldSize = m_LastCalculatedBounds.Value.size;
if (m_IsFlip)
{
Vector3 localCenter = Target.transform.InverseTransformPoint(worldCenter);
Vector3 localSize = Target.transform.InverseTransformDirection(worldSize);
Vector3 flipCenter = Target.transform.position + new Vector3(localCenter.z, localCenter.y, -localCenter.x);
Vector3 flipSize = new Vector3(localSize.z, localSize.y, localSize.x);
Handles.DrawWireCube(flipCenter, flipSize);
}
else
{
Handles.DrawWireCube(worldCenter, worldSize);
}
}
}
private void DrawDefaultInspectorFallback()
{
EditorGUILayout.EditorToolbarForTarget(EditorGUIUtility.TrTempContent("Edit Collider"), this);
EditorGUILayout.Space(2);
DrawPropertiesExcluding(serializedObject, "m_Script");
if (serializedObject.hasModifiedProperties)
{
serializedObject.ApplyModifiedProperties();
}
}
private void DrawExtendedFeatures()
{
EditorGUILayout.Space(15);
EditorGUILayout.LabelField("Adaptive Size Settings", m_HeaderStyle);
EditorGUILayout.Space(5);
// 设置选项
DrawSettingsSection();
EditorGUILayout.Space(10);
// 预览信息
DrawPreviewSection();
EditorGUILayout.Space(10);
// 操作按钮
DrawActionButtons();
}
private void DrawSettingsSection()
{
EditorGUILayout.BeginVertical("HelpBox");
m_IsFlip = EditorGUILayout.ToggleLeft(" Flip Axes (X↔Z)", m_IsFlip);
m_IncludeInactive = EditorGUILayout.ToggleLeft(" Include Inactive Children", m_IncludeInactive);
EditorGUILayout.EndVertical();
}
private void DrawPreviewSection()
{
if (m_LastCalculatedBounds.HasValue)
{
EditorGUILayout.BeginVertical("HelpBox");
EditorGUILayout.LabelField("Last Calculated Bounds:", EditorStyles.miniBoldLabel);
var bounds = m_LastCalculatedBounds.Value;
EditorGUILayout.LabelField($"Center: {bounds.center}", EditorStyles.miniLabel);
EditorGUILayout.LabelField($"Size: {bounds.size}", EditorStyles.miniLabel);
EditorGUILayout.LabelField($"Extents: {bounds.extents}", EditorStyles.miniLabel);
EditorGUILayout.EndVertical();
}
}
private void DrawActionButtons()
{
// 计算边界按钮
using (new EditorGUILayout.HorizontalScope())
{
GUI.color = new Color(0.2f, 0.8f, 0.2f, 1f);
if (GUILayout.Button("Calculate Bounds", m_ButtonStyle))
{
CalculateAndPreviewBounds();
}
GUI.color = Color.white;
GUI.enabled = m_LastCalculatedBounds.HasValue;
GUI.color = new Color(0.9f, 0.6f, 0.1f, 1f);
if (GUILayout.Button("Apply", m_ButtonStyle))
{
ApplyCalculatedBounds();
}
GUI.color = Color.white;
GUI.enabled = true;
}
// 快速应用按钮
EditorGUILayout.Space(5);
GUI.color = new Color(0.1f, 0.6f, 0.9f, 1f);
if (GUILayout.Button("Quick Adaptive Size", m_ButtonStyle))
{
CalculateAndApplyBounds();
}
GUI.color = Color.white;
}
private void CalculateAndPreviewBounds()
{
m_LastCalculatedBounds = CalculateWorldBounds();
if (!m_LastCalculatedBounds.HasValue)
{
EditorUtility.DisplayDialog("No Renderers Found",
"No MeshRenderers found in children. Please ensure the object has MeshRenderer components.",
"OK");
return;
}
isApply = false;
// 在Scene视图中高亮显示计算出的边界
SceneView.RepaintAll();
}
private void CalculateAndApplyBounds()
{
var bounds = CalculateWorldBounds();
if (!bounds.HasValue)
{
EditorUtility.DisplayDialog("No Renderers Found",
"No MeshRenderers found in children.",
"OK");
return;
}
ApplyBoundsToCollider(bounds.Value);
m_LastCalculatedBounds = bounds;
}
private void ApplyCalculatedBounds()
{
if (m_LastCalculatedBounds.HasValue)
{
ApplyBoundsToCollider(m_LastCalculatedBounds.Value);
}
}
private Bounds? CalculateWorldBounds()
{
var renderers = Target.GetComponentsInChildren<MeshRenderer>(m_IncludeInactive)
.Where(r => r != null)
.ToArray();
if (renderers.Length == 0)
{
// 尝试查找SkinnedMeshRenderer
var skinnedRenderers = Target.GetComponentsInChildren<SkinnedMeshRenderer>(m_IncludeInactive)
.Where(r => r != null)
.ToArray();
if (skinnedRenderers.Length == 0)
return null;
return CalculateCombinedBounds(skinnedRenderers.Select(r => r.bounds));
}
return CalculateCombinedBounds(renderers.Select(r => r.bounds));
}
private Bounds CalculateCombinedBounds(System.Collections.Generic.IEnumerable<Bounds> boundsCollection)
{
var boundsArray = boundsCollection.ToArray();
if (boundsArray.Length == 0)
return new Bounds(Vector3.zero, Vector3.zero);
var combinedBounds = boundsArray[0];
for (int i = 1; i < boundsArray.Length; i++)
{
combinedBounds.Encapsulate(boundsArray[i]);
}
return combinedBounds;
}
public void ApplyBoundsToCollider(Bounds worldBounds)
{
Undo.RecordObject(Target, "Adapt BoxCollider Size");
Vector3 localCenter = Target.transform.InverseTransformPoint(worldBounds.center);
Vector3 localSize = Target.transform.InverseTransformDirection(worldBounds.size);
if (m_IsFlip)
{
Target.center = new Vector3(localCenter.z, localCenter.y, -localCenter.x);
Target.size = new Vector3(localSize.z, localSize.y, localSize.x);
}
else
{
Target.center = localCenter;
Target.size = localSize;
}
isApply = true;
// 标记为脏对象以便保存
EditorUtility.SetDirty(Target);
Debug.Log($"BoxCollider adapted: Center={Target.center}, Size={Target.size}");
}
}
|
每一次跌倒都是一次成长 每一次努力都是一次进步 |
感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何问题或意见,或者想要了解更多关于本主题的信息,欢迎在评论区留言与我交流。我会非常乐意与大家讨论和分享更多有趣的内容。
如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。
在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!
本文介绍了如何使用.NETReflector反编译UnityEditor.dll,找到BoxCollider的编辑器类并创建自定义扩展。通过扩展,实现了点击按钮自动调整BoxCollider以包含对象及所有子对象的功能,简化了游戏物体碰撞盒的设置过程。
11万+

被折叠的 条评论
为什么被折叠?



