Files
gold_dolphin/unity/Assets/Editor/ElementsAligner.cs

230 lines
7.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEditor;
/// <summary>
/// Elements底对齐工具 - 将Elements下所有物体底对齐到Y=0平面
/// </summary>
public class ElementsAligner : Editor
{
[MenuItem("Tools/Elements/底对齐到地面 (Y=0)")]
public static void AlignElementsToGround()
{
GameObject elements = GameObject.Find("Elements");
if (elements == null)
{
EditorUtility.DisplayDialog("错误", "找不到场景中的 'Elements' 物体", "确定");
return;
}
int count = 0;
Undo.SetCurrentGroupName("Align Elements to Ground");
for (int i = 0; i < elements.transform.childCount; i++)
{
Transform child = elements.transform.GetChild(i);
// 记录到Undo栈
Undo.RecordObject(child, $"Align {child.name}");
// 获取物体的包围盒
Bounds bounds = GetObjectBounds(child);
if (bounds.size == Vector3.zero)
{
Debug.LogWarning($"跳过 {child.name} - 无法计算包围盒");
continue;
}
// 计算需要移动的距离让物体底部对齐到Y=0
float bottomY = bounds.min.y;
Vector3 newPosition = child.position;
newPosition.y = child.position.y - bottomY;
child.position = newPosition;
count++;
Debug.Log($"✓ 对齐 {child.name}: 底部Y从 {bottomY:F3} 到 0");
}
if (count > 0)
{
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(
UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene()
);
EditorUtility.DisplayDialog("完成", $"已对齐 {count} 个物体到地面 (Y=0)", "确定");
Debug.Log($"完成!对齐了 {count} 个物体");
}
}
[MenuItem("Tools/Elements/底对齐到指定高度")]
public static void AlignElementsToCustomHeight()
{
string input = EditorInputDialog.Show("对齐到指定高度", "请输入目标Y坐标:", "0");
if (input == null) return; // 用户取消
if (!float.TryParse(input, out float targetY))
{
EditorUtility.DisplayDialog("错误", "请输入有效的数字", "确定");
return;
}
GameObject elements = GameObject.Find("Elements");
if (elements == null)
{
EditorUtility.DisplayDialog("错误", "找不到场景中的 'Elements' 物体", "确定");
return;
}
int count = 0;
Undo.SetCurrentGroupName("Align Elements to Custom Height");
for (int i = 0; i < elements.transform.childCount; i++)
{
Transform child = elements.transform.GetChild(i);
Undo.RecordObject(child, $"Align {child.name}");
Bounds bounds = GetObjectBounds(child);
if (bounds.size == Vector3.zero)
{
Debug.LogWarning($"跳过 {child.name} - 无法计算包围盒");
continue;
}
float bottomY = bounds.min.y;
Vector3 newPosition = child.position;
newPosition.y = child.position.y - bottomY + targetY;
child.position = newPosition;
count++;
Debug.Log($"✓ 对齐 {child.name}: 底部Y从 {bottomY:F3} 到 {targetY:F3}");
}
if (count > 0)
{
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(
UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene()
);
EditorUtility.DisplayDialog("完成", $"已对齐 {count} 个物体到 Y={targetY}", "确定");
}
}
/// <summary>
/// 获取物体的世界空间包围盒(包括所有子物体和渲染器)
/// </summary>
private static Bounds GetObjectBounds(Transform obj)
{
Renderer[] renderers = obj.GetComponentsInChildren<Renderer>();
if (renderers.Length == 0)
{
// 如果没有渲染器使用碰撞体或transform位置
Collider[] colliders = obj.GetComponentsInChildren<Collider>();
if (colliders.Length > 0)
{
Bounds bounds = colliders[0].bounds;
for (int i = 1; i < colliders.Length; i++)
{
bounds.Encapsulate(colliders[i].bounds);
}
return bounds;
}
// 都没有,返回以物体为中心的小包围盒
return new Bounds(obj.position, Vector3.one * 0.1f);
}
// 使用第一个渲染器初始化
Bounds result = renderers[0].bounds;
// 合并所有渲染器的包围盒
for (int i = 1; i < renderers.Length; i++)
{
result.Encapsulate(renderers[i].bounds);
}
return result;
}
}
/// <summary>
/// 简单的输入对话框(用于自定义高度)
/// </summary>
public class EditorInputDialog : EditorWindow
{
private string _title;
private string _message;
private string _inputText;
private bool _confirmed = false;
public static string Show(string title, string message, string defaultValue)
{
EditorInputDialog window = CreateInstance<EditorInputDialog>();
window.titleContent = new GUIContent(title);
window._title = title;
window._message = message;
window._inputText = defaultValue;
window._confirmed = false;
window.minSize = new Vector2(300, 120);
window.maxSize = new Vector2(300, 120);
window.ShowModal();
return window._confirmed ? window._inputText : null;
}
private void OnGUI()
{
GUILayout.Space(10);
GUILayout.Label(_message, EditorStyles.wordWrappedLabel);
GUILayout.Space(5);
GUI.SetNextControlName("InputField");
_inputText = GUILayout.TextField(_inputText);
if (Event.current.type == EventType.Repaint && GUI.GetNameOfFocusedControl() != "InputField")
{
EditorGUI.FocusTextInControl("InputField");
}
// 支持回车确认
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return)
{
_confirmed = true;
Close();
Event.current.Use();
}
// 支持ESC取消
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
{
_confirmed = false;
Close();
Event.current.Use();
}
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("确定", GUILayout.Width(80)))
{
_confirmed = true;
Close();
}
if (GUILayout.Button("取消", GUILayout.Width(80)))
{
_confirmed = false;
Close();
}
GUILayout.EndHorizontal();
}
}