TileChildren.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using Sirenix.OdinInspector;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEditor;
  5. using UnityEngine;
  6. using System.Linq;
  7. public class TileChildren : MonoBehaviour
  8. {
  9. public Renderer plane;
  10. public float spacing = 0.1f;
  11. public float sizeThreshold = 1.5f; // 区分大小件的阈值
  12. public bool forceSquareLayout = true; // 是否启用正方形平铺
  13. [ContextMenu("平铺所有部件")]
  14. [Button("平铺所有部件")]
  15. public void TileAllMoveItems()
  16. {
  17. if (plane == null) return;
  18. MoveItem[] moveItems = GetComponentsInChildren<MoveItem>();
  19. if (moveItems.Length == 0) return;
  20. Vector3 planeCenter = plane.bounds.center;
  21. Vector3 right = plane.transform.right;
  22. Vector3 forward = plane.transform.forward;
  23. Vector3 up = plane.transform.up;
  24. Quaternion commonRotation = Quaternion.LookRotation(forward, up);
  25. // --- 收集每个 MoveItem 的整体 Bounds ---
  26. List<(Transform t, Bounds b)> validItems = new List<(Transform, Bounds)>();
  27. foreach (var item in moveItems)
  28. {
  29. Bounds? bounds = GetCombinedBounds(item.transform);
  30. if (bounds.HasValue)
  31. {
  32. validItems.Add((item.transform, bounds.Value));
  33. }
  34. else
  35. {
  36. Debug.LogWarning($"物体 {item.name} 没有找到任何 Renderer,无法参与排布");
  37. }
  38. }
  39. if (validItems.Count == 0) return;
  40. Dictionary<Transform, Vector3> newPositions = new Dictionary<Transform, Vector3>();
  41. Bounds totalBounds = new Bounds(Vector3.zero, Vector3.zero);
  42. bool hasBounds = false;
  43. if (forceSquareLayout)
  44. {
  45. // --- 正方形排布 ---
  46. int count = validItems.Count;
  47. int cols = Mathf.CeilToInt(Mathf.Sqrt(count));
  48. int rows = Mathf.CeilToInt(count / (float)cols);
  49. float currentZ = 0;
  50. int index = 0;
  51. for (int r = 0; r < rows; r++)
  52. {
  53. float currentX = 0;
  54. float rowMaxZ = 0;
  55. for (int c = 0; c < cols; c++)
  56. {
  57. if (index >= count) break;
  58. var (t, b) = validItems[index];
  59. Vector3 size = b.size;
  60. Vector3 localPos = right * (currentX + size.x / 2) + forward * (currentZ + size.z / 2);
  61. newPositions[t] = localPos;
  62. currentX += size.x + spacing;
  63. rowMaxZ = Mathf.Max(rowMaxZ, size.z);
  64. if (!hasBounds) { totalBounds = new Bounds(localPos, Vector3.zero); hasBounds = true; }
  65. else totalBounds.Encapsulate(localPos);
  66. index++;
  67. }
  68. currentZ += rowMaxZ + spacing;
  69. }
  70. }
  71. else
  72. {
  73. // --- 分组平铺模式(大件、小件分开) ---
  74. List<List<(Transform, Bounds)>> groups = new List<List<(Transform, Bounds)>>()
  75. {
  76. new List<(Transform, Bounds)>(), // 大件
  77. new List<(Transform, Bounds)>() // 小件
  78. };
  79. foreach (var (t, b) in validItems)
  80. {
  81. float size = b.size.magnitude;
  82. if (size < sizeThreshold)
  83. groups[1].Add((t, b));
  84. else
  85. groups[0].Add((t, b));
  86. }
  87. float groupOffsetZ = 0;
  88. foreach (var group in groups)
  89. {
  90. if (group.Count == 0) continue;
  91. float currentX = 0;
  92. float rowMaxZ = 0;
  93. foreach (var (t, b) in group)
  94. {
  95. Vector3 size = b.size;
  96. Vector3 localPos = right * (currentX + size.x / 2) + forward * (groupOffsetZ + size.z / 2);
  97. newPositions[t] = localPos;
  98. currentX += size.x + spacing;
  99. rowMaxZ = Mathf.Max(rowMaxZ, size.z);
  100. if (!hasBounds) { totalBounds = new Bounds(localPos, Vector3.zero); hasBounds = true; }
  101. else totalBounds.Encapsulate(localPos);
  102. }
  103. groupOffsetZ += rowMaxZ + spacing;
  104. }
  105. }
  106. // --- 计算整体居中 ---
  107. Vector3 offsetToCenter = planeCenter - totalBounds.center;
  108. // --- 应用 ---
  109. foreach (var kv in newPositions)
  110. {
  111. kv.Key.position = kv.Value + offsetToCenter;
  112. kv.Key.rotation = commonRotation;
  113. }
  114. }
  115. /// <summary>
  116. /// 获取一个物体及其所有子物体的合并 Bounds
  117. /// </summary>
  118. Bounds? GetCombinedBounds(Transform root)
  119. {
  120. Renderer[] renderers = root.GetComponentsInChildren<Renderer>();
  121. if (renderers.Length == 0) return null;
  122. Bounds combined = renderers[0].bounds;
  123. for (int i = 1; i < renderers.Length; i++)
  124. {
  125. combined.Encapsulate(renderers[i].bounds);
  126. }
  127. return combined;
  128. }
  129. }
  130. //#if UNITY_EDITOR
  131. //[CustomEditor(typeof(TileChildrenByPlane))]
  132. //public class TileChildrenByPlaneEditor : Editor
  133. //{
  134. // public override void OnInspectorGUI()
  135. // {
  136. // DrawDefaultInspector();
  137. // TileChildrenByPlane script = (TileChildrenByPlane)target;
  138. // if (GUILayout.Button("平铺所有 MoveItem"))
  139. // {
  140. // script.TileAllMoveItems();
  141. // }
  142. // }
  143. //}
  144. //#endif