using UnityEngine; using System.Collections.Generic; namespace UnityEngine.UI { [AddComponentMenu("Layout/Grid Layout Group", 152)] public class GridLayoutGroup : LayoutGroup { public enum Corner { UpperLeft = 0, UpperRight = 1, LowerLeft = 2, LowerRight = 3 } public enum Axis { Horizontal = 0, Vertical = 1 } public enum Constraint { Flexible = 0, FixedColumnCount = 1, FixedRowCount = 2 } [SerializeField] protected Corner m_StartCorner = Corner.UpperLeft; public Corner startCorner { get { return m_StartCorner; } set { SetProperty(ref m_StartCorner, value); } } [SerializeField] protected Axis m_StartAxis = Axis.Horizontal; public Axis startAxis { get { return m_StartAxis; } set { SetProperty(ref m_StartAxis, value); } } [SerializeField] protected Vector2 m_CellSize = new Vector2(100, 100); public Vector2 cellSize { get { return m_CellSize; } set { SetProperty(ref m_CellSize, value); } } [SerializeField] protected Vector2 m_Spacing = Vector2.zero; public Vector2 spacing { get { return m_Spacing; } set { SetProperty(ref m_Spacing, value); } } [SerializeField] protected Constraint m_Constraint = Constraint.Flexible; public Constraint constraint { get { return m_Constraint; } set { SetProperty(ref m_Constraint, value); } } [SerializeField] protected int m_ConstraintCount = 2; public int constraintCount { get { return m_ConstraintCount; } set { SetProperty(ref m_ConstraintCount, Mathf.Max(1, value)); } } protected GridLayoutGroup() {} #if UNITY_EDITOR protected override void OnValidate() { base.OnValidate(); constraintCount = constraintCount; } #endif public override void CalculateLayoutInputHorizontal() { base.CalculateLayoutInputHorizontal(); int minColumns = 0; int preferredColumns = 0; if (m_Constraint == Constraint.FixedColumnCount) { minColumns = preferredColumns = m_ConstraintCount; } else if (m_Constraint == Constraint.FixedRowCount) { minColumns = preferredColumns = Mathf.CeilToInt(rectChildren.Count / (float)m_ConstraintCount - 0.001f); } else { minColumns = 1; preferredColumns = Mathf.CeilToInt(Mathf.Sqrt(rectChildren.Count)); } SetLayoutInputForAxis( padding.horizontal + (cellSize.x + spacing.x) * minColumns - spacing.x, padding.horizontal + (cellSize.x + spacing.x) * preferredColumns - spacing.x, -1, 0); } public override void CalculateLayoutInputVertical() { int minRows = 0; if (m_Constraint == Constraint.FixedColumnCount) { minRows = Mathf.CeilToInt(rectChildren.Count / (float)m_ConstraintCount - 0.001f); } else if (m_Constraint == Constraint.FixedRowCount) { minRows = m_ConstraintCount; } else { float width = rectTransform.rect.size.x; int cellCountX = Mathf.Max(1, Mathf.FloorToInt((width - padding.horizontal + spacing.x + 0.001f) / (cellSize.x + spacing.x))); minRows = Mathf.CeilToInt(rectChildren.Count / (float)cellCountX); } float minSpace = padding.vertical + (cellSize.y + spacing.y) * minRows - spacing.y; SetLayoutInputForAxis(minSpace, minSpace, -1, 1); } public override void SetLayoutHorizontal() { SetCellsAlongAxis(0); } public override void SetLayoutVertical() { SetCellsAlongAxis(1); } private void SetCellsAlongAxis(int axis) { // Normally a Layout Controller should only set horizontal values when invoked for the horizontal axis // and only vertical values when invoked for the vertical axis. // However, in this case we set both the horizontal and vertical position when invoked for the vertical axis. // Since we only set the horizontal position and not the size, it shouldn't affect children's layout, // and thus shouldn't break the rule that all horizontal layout must be calculated before all vertical layout. if (axis == 0) { // Only set the sizes when invoked for horizontal axis, not the positions. for (int i = 0; i < rectChildren.Count; i++) { RectTransform rect = rectChildren[i]; m_Tracker.Add(this, rect, DrivenTransformProperties.Anchors | DrivenTransformProperties.AnchoredPosition | DrivenTransformProperties.SizeDelta); rect.anchorMin = Vector2.up; rect.anchorMax = Vector2.up; rect.sizeDelta = cellSize; } return; } float width = rectTransform.rect.size.x; float height = rectTransform.rect.size.y; int cellCountX = 1; int cellCountY = 1; if (m_Constraint == Constraint.FixedColumnCount) { cellCountX = m_ConstraintCount; cellCountY = Mathf.CeilToInt(rectChildren.Count / (float)cellCountX - 0.001f); } else if (m_Constraint == Constraint.FixedRowCount) { cellCountY = m_ConstraintCount; cellCountX = Mathf.CeilToInt(rectChildren.Count / (float)cellCountY - 0.001f); } else { if (cellSize.x + spacing.x <= 0) cellCountX = int.MaxValue; else cellCountX = Mathf.Max(1, Mathf.FloorToInt((width - padding.horizontal + spacing.x + 0.001f) / (cellSize.x + spacing.x))); if (cellSize.y + spacing.y <= 0) cellCountY = int.MaxValue; else cellCountY = Mathf.Max(1, Mathf.FloorToInt((height - padding.vertical + spacing.y + 0.001f) / (cellSize.y + spacing.y))); } int cornerX = (int)startCorner % 2; int cornerY = (int)startCorner / 2; int cellsPerMainAxis, actualCellCountX, actualCellCountY; if (startAxis == Axis.Horizontal) { cellsPerMainAxis = cellCountX; actualCellCountX = Mathf.Clamp(cellCountX, 1, rectChildren.Count); actualCellCountY = Mathf.Clamp(cellCountY, 1, Mathf.CeilToInt(rectChildren.Count / (float)cellsPerMainAxis)); } else { cellsPerMainAxis = cellCountY; actualCellCountY = Mathf.Clamp(cellCountY, 1, rectChildren.Count); actualCellCountX = Mathf.Clamp(cellCountX, 1, Mathf.CeilToInt(rectChildren.Count / (float)cellsPerMainAxis)); } Vector2 requiredSpace = new Vector2( actualCellCountX * cellSize.x + (actualCellCountX - 1) * spacing.x, actualCellCountY * cellSize.y + (actualCellCountY - 1) * spacing.y ); Vector2 startOffset = new Vector2( GetStartOffset(0, requiredSpace.x), GetStartOffset(1, requiredSpace.y) ); for (int i = 0; i < rectChildren.Count; i++) { int positionX; int positionY; if (startAxis == Axis.Horizontal) { positionX = i % cellsPerMainAxis; positionY = i / cellsPerMainAxis; } else { positionX = i / cellsPerMainAxis; positionY = i % cellsPerMainAxis; } if (cornerX == 1) positionX = actualCellCountX - 1 - positionX; if (cornerY == 1) positionY = actualCellCountY - 1 - positionY; SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (cellSize[0] + spacing[0]) * positionX, cellSize[0]); SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (cellSize[1] + spacing[1]) * positionY, cellSize[1]); } } } }