using Sirenix.OdinInspector; using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; using System.Linq; public class TileChildren : MonoBehaviour { public Renderer plane; public float spacing = 0.1f; public float sizeThreshold = 1.5f; // 区分大小件的阈值 public bool forceSquareLayout = true; // 是否启用正方形平铺 [ContextMenu("平铺所有部件")] [Button("平铺所有部件")] public void TileAllMoveItems() { if (plane == null) return; MoveItem[] moveItems = GetComponentsInChildren(); if (moveItems.Length == 0) return; Vector3 planeCenter = plane.bounds.center; Vector3 right = plane.transform.right; Vector3 forward = plane.transform.forward; Vector3 up = plane.transform.up; Quaternion commonRotation = Quaternion.LookRotation(forward, up); // --- 收集每个 MoveItem 的整体 Bounds --- List<(Transform t, Bounds b)> validItems = new List<(Transform, Bounds)>(); foreach (var item in moveItems) { Bounds? bounds = GetCombinedBounds(item.transform); if (bounds.HasValue) { validItems.Add((item.transform, bounds.Value)); } else { Debug.LogWarning($"物体 {item.name} 没有找到任何 Renderer,无法参与排布"); } } if (validItems.Count == 0) return; Dictionary newPositions = new Dictionary(); Bounds totalBounds = new Bounds(Vector3.zero, Vector3.zero); bool hasBounds = false; if (forceSquareLayout) { // --- 正方形排布 --- int count = validItems.Count; int cols = Mathf.CeilToInt(Mathf.Sqrt(count)); int rows = Mathf.CeilToInt(count / (float)cols); float currentZ = 0; int index = 0; for (int r = 0; r < rows; r++) { float currentX = 0; float rowMaxZ = 0; for (int c = 0; c < cols; c++) { if (index >= count) break; var (t, b) = validItems[index]; Vector3 size = b.size; Vector3 localPos = right * (currentX + size.x / 2) + forward * (currentZ + size.z / 2); newPositions[t] = localPos; currentX += size.x + spacing; rowMaxZ = Mathf.Max(rowMaxZ, size.z); if (!hasBounds) { totalBounds = new Bounds(localPos, Vector3.zero); hasBounds = true; } else totalBounds.Encapsulate(localPos); index++; } currentZ += rowMaxZ + spacing; } } else { // --- 分组平铺模式(大件、小件分开) --- List> groups = new List>() { new List<(Transform, Bounds)>(), // 大件 new List<(Transform, Bounds)>() // 小件 }; foreach (var (t, b) in validItems) { float size = b.size.magnitude; if (size < sizeThreshold) groups[1].Add((t, b)); else groups[0].Add((t, b)); } float groupOffsetZ = 0; foreach (var group in groups) { if (group.Count == 0) continue; float currentX = 0; float rowMaxZ = 0; foreach (var (t, b) in group) { Vector3 size = b.size; Vector3 localPos = right * (currentX + size.x / 2) + forward * (groupOffsetZ + size.z / 2); newPositions[t] = localPos; currentX += size.x + spacing; rowMaxZ = Mathf.Max(rowMaxZ, size.z); if (!hasBounds) { totalBounds = new Bounds(localPos, Vector3.zero); hasBounds = true; } else totalBounds.Encapsulate(localPos); } groupOffsetZ += rowMaxZ + spacing; } } // --- 计算整体居中 --- Vector3 offsetToCenter = planeCenter - totalBounds.center; // --- 应用 --- foreach (var kv in newPositions) { kv.Key.position = kv.Value + offsetToCenter; kv.Key.rotation = commonRotation; } } /// /// 获取一个物体及其所有子物体的合并 Bounds /// Bounds? GetCombinedBounds(Transform root) { Renderer[] renderers = root.GetComponentsInChildren(); if (renderers.Length == 0) return null; Bounds combined = renderers[0].bounds; for (int i = 1; i < renderers.Length; i++) { combined.Encapsulate(renderers[i].bounds); } return combined; } } //#if UNITY_EDITOR //[CustomEditor(typeof(TileChildrenByPlane))] //public class TileChildrenByPlaneEditor : Editor //{ // public override void OnInspectorGUI() // { // DrawDefaultInspector(); // TileChildrenByPlane script = (TileChildrenByPlane)target; // if (GUILayout.Button("平铺所有 MoveItem")) // { // script.TileAllMoveItems(); // } // } //} //#endif