using UnityEngine; using UnityEditor; /// /// Elements底对齐工具 - 将Elements下所有物体底对齐到Y=0平面 /// 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}", "确定"); } } /// /// 获取物体的世界空间包围盒(包括所有子物体和渲染器) /// private static Bounds GetObjectBounds(Transform obj) { Renderer[] renderers = obj.GetComponentsInChildren(); if (renderers.Length == 0) { // 如果没有渲染器,使用碰撞体或transform位置 Collider[] colliders = obj.GetComponentsInChildren(); 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; } } /// /// 简单的输入对话框(用于自定义高度) /// 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(); 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(); } }