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 bool _isDirty = true; //优化新增:脏标记与计时器 初始设为 true,确保第一帧会排版
private float _checkTimer = 0f;
private const float CHECK_INTERVAL = 0.1f; // 100ms 检查间隔
///
/// 供外部或子元素调用,标记当前网格需要重新排版
///
public void MarkAsDirty()
{
_isDirty = true;
}
// 尺寸或子节点数量发生变化时,同样只打上脏标记
private void OnRectTransformDimensionsChange() => MarkAsDirty();
private void OnTransformChildrenChanged() => MarkAsDirty();
private void Update()
{
#if UNITY_EDITOR
// 编辑器非运行状态下(Edit Mode)Update 帧率不稳定,为了方便拖拽预览,依然有脏标记就立即更新
if (!Application.isPlaying)
{
if (_isDirty)
{
UpdateLayout();
_isDirty = false;
}
return;
}
#endif
// 运行时(Play Mode)每 100ms 轮询一次
_checkTimer += Time.deltaTime;
if (_checkTimer >= CHECK_INTERVAL)
{
if (_isDirty)
{
UpdateLayout();
_isDirty = false; // 排版完成后,清除标志位
}
_checkTimer = 0f; // 重置计时器
}
}
public void UpdateLayout()
{
if (rows <= 0 || transform.childCount == 0) return;
List flowChildren = new List();
List fixedChildren = new List();
// 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();
if (cell != null && cell.isFixedPosition)
fixedChildren.Add(child);
else
flowChildren.Add(child);
}
// 2. 排序流动物体 (按 sortIndex)
flowChildren.Sort((a, b) =>
{
var cellA = a.GetComponent();
var cellB = b.GetComponent();
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();
var cellB = b.GetComponent();
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();
}
// 分支 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++; // 占位成功,坑位后移
}
}
}
///
/// 将子物体放置到指定的网格索引位置
///
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);
}
}