| New file |
| | |
| | | using UnityEngine; |
| | | using System.Collections.Generic; |
| | | |
| | | [ExecuteAlways] |
| | | [RequireComponent(typeof(RectTransform))] |
| | | public class HomeGridLayout : MonoBehaviour |
| | | { |
| | | public RectOffset padding = new RectOffset(); // 边距 |
| | | public Vector2 cellSize = new Vector2(100f, 100f); // 单元格大小 |
| | | public Vector2 spacing = new Vector2(10f, 10f); // 元素间距 |
| | | public int rows = 7; // 固定行数 |
| | | |
| | | private void OnRectTransformDimensionsChange() => UpdateLayout(); |
| | | private void OnTransformChildrenChanged() => UpdateLayout(); |
| | | |
| | | #if UNITY_EDITOR |
| | | private void Update() |
| | | { |
| | | if (!Application.isPlaying) UpdateLayout(); |
| | | } |
| | | #endif |
| | | |
| | | public void UpdateLayout() |
| | | { |
| | | if (rows <= 0 || transform.childCount == 0) return; |
| | | |
| | | List<RectTransform> flowChildren = new List<RectTransform>(); |
| | | List<RectTransform> fixedChildren = new List<RectTransform>(); |
| | | |
| | | // 1. 遍历收集并分类所有激活的子物体 |
| | | for (int i = 0; i < transform.childCount; i++) |
| | | { |
| | | RectTransform child = transform.GetChild(i) as RectTransform; |
| | | if (child == null || !child.gameObject.activeSelf) continue; |
| | | |
| | | HomeGridLayoutCell cell = child.GetComponent<HomeGridLayoutCell>(); |
| | | |
| | | if (cell != null && cell.isFixedPosition) |
| | | fixedChildren.Add(child); |
| | | else |
| | | flowChildren.Add(child); |
| | | } |
| | | |
| | | // 2. 排序流动物体 (按 sortIndex) |
| | | flowChildren.Sort((a, b) => |
| | | { |
| | | var cellA = a.GetComponent<HomeGridLayoutCell>(); |
| | | var cellB = b.GetComponent<HomeGridLayoutCell>(); |
| | | int indexA = cellA != null ? cellA.sortIndex : int.MaxValue; |
| | | int indexB = cellB != null ? cellB.sortIndex : int.MaxValue; |
| | | return indexA.CompareTo(indexB); |
| | | }); |
| | | |
| | | // 3. 排序固定物体 (优先 sortIndex,其次 subSortIndex) |
| | | fixedChildren.Sort((a, b) => |
| | | { |
| | | var cellA = a.GetComponent<HomeGridLayoutCell>(); |
| | | var cellB = b.GetComponent<HomeGridLayoutCell>(); |
| | | |
| | | int sortA = cellA != null ? cellA.sortIndex : int.MaxValue; |
| | | int sortB = cellB != null ? cellB.sortIndex : int.MaxValue; |
| | | |
| | | if (sortA != sortB) return sortA.CompareTo(sortB); |
| | | |
| | | int subA = cellA != null ? cellA.subSortIndex : int.MaxValue; |
| | | int subB = cellB != null ? cellB.subSortIndex : int.MaxValue; |
| | | return subA.CompareTo(subB); |
| | | }); |
| | | |
| | | // 4. 开始分配网格 (核心优化部分) |
| | | int currentGridIndex = 0; // 当前推演到的真实网格坑位 |
| | | int flowIndex = 0; |
| | | int fixedIndex = 0; |
| | | int totalValidChildren = flowChildren.Count + fixedChildren.Count; |
| | | |
| | | for (int i = 0; i < totalValidChildren; i++) |
| | | { |
| | | RectTransform targetChild = null; |
| | | HomeGridLayoutCell nextFixedCell = null; |
| | | |
| | | if (fixedIndex < fixedChildren.Count) |
| | | { |
| | | nextFixedCell = fixedChildren[fixedIndex].GetComponent<HomeGridLayoutCell>(); |
| | | } |
| | | |
| | | // 分支 A:固定物体已经到了它期望的网格位置(或已经被前面的元素挤到了当前位置) |
| | | if (nextFixedCell != null && nextFixedCell.sortIndex <= currentGridIndex) |
| | | { |
| | | targetChild = fixedChildren[fixedIndex]; |
| | | fixedIndex++; |
| | | } |
| | | // 分支 B:当前位置没有固定物体抢占,且还有流动物体排队,让流动物体填补空缺 |
| | | else if (flowIndex < flowChildren.Count) |
| | | { |
| | | targetChild = flowChildren[flowIndex]; |
| | | flowIndex++; |
| | | } |
| | | // 分支 C:没有流动物体填补空缺了,直接让网格索引“快进”到下一个固定物体的位置 |
| | | else if (nextFixedCell != null) |
| | | { |
| | | currentGridIndex = nextFixedCell.sortIndex; // 快进跳过中间的所有空白格子 |
| | | targetChild = fixedChildren[fixedIndex]; |
| | | fixedIndex++; |
| | | } |
| | | |
| | | // 应用计算好的网格坐标 |
| | | if (targetChild != null) |
| | | { |
| | | SetChildTransform(targetChild, currentGridIndex); |
| | | currentGridIndex++; // 占位成功,坑位后移 |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 将子物体放置到指定的网格索引位置 |
| | | /// </summary> |
| | | private void SetChildTransform(RectTransform child, int gridIndex) |
| | | { |
| | | int col = gridIndex / rows; |
| | | int row = gridIndex % rows; |
| | | |
| | | // 1. 先计算出如果轴心在右上角时的理论边缘坐标 |
| | | float edgeXPos = -padding.right - col * (cellSize.x + spacing.x); |
| | | float edgeYPos = -padding.top - row * (cellSize.y + spacing.y); |
| | | |
| | | // 2. 为了消除团队潜规则,我们将格子的轴心改回正中心 (0.5, 0.5) |
| | | // 因此实际坐标需要向左、向下再偏移半个单元格的大小 |
| | | float centerXPos = edgeXPos - (cellSize.x * 0.5f); |
| | | float centerYPos = edgeYPos - (cellSize.y * 0.5f); |
| | | |
| | | // 统一锚点为右上角 (1, 1) |
| | | child.anchorMin = new Vector2(1, 1); |
| | | child.anchorMax = new Vector2(1, 1); |
| | | |
| | | // 轴心(Pivot)改为正中心! |
| | | child.pivot = new Vector2(0.5f, 0.5f); |
| | | |
| | | child.sizeDelta = cellSize; |
| | | |
| | | // 赋予中心坐标 |
| | | child.anchoredPosition = new Vector2(centerXPos, centerYPos); |
| | | } |
| | | } |