Last active
May 27, 2024 20:06
-
-
Save Democide/d1e4516aacef3c3f88a3cdf39a7e558f to your computer and use it in GitHub Desktop.
Skewed Grid Layout Group - Unity UI Extension
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Skewed Grid Layout Group | |
/// =============================== | |
/// | |
/// Written by Martin Nerurkar | |
/// - http://www.martin.nerurkar.de | |
/// - http://www.sharkbombs.com | |
/// | |
/// You are free to moddify and use this code in your own projects | |
/// You are not allowed to remove this copyright notice | |
/// You are not allowed to resell this code, even if modified | |
using UnityEngine; | |
using System.Collections.Generic; | |
namespace UnityEngine.UI { | |
[AddComponentMenu("Layout/Skewed Grid Layout Group", 152)] | |
public class SkewedGridLayoutGroup : GridLayoutGroup { | |
[SerializeField] | |
protected Axis m_skewAxis = Axis.Horizontal; | |
public Axis skewAxis { get { return m_skewAxis; } set { SetProperty(ref m_skewAxis, value); } } | |
[SerializeField] | |
protected bool m_useMaxSkewDistance = false; | |
public bool useMaxSkewDistance { get { return m_useMaxSkewDistance; } set { SetProperty(ref m_useMaxSkewDistance, value); } } | |
[SerializeField] | |
protected Vector2 m_maxSkewDistance = Vector2.zero; | |
public Vector2 maxSkewDistance { get { return m_maxSkewDistance; } set { SetProperty(ref m_maxSkewDistance, value); } } | |
protected SkewedGridLayoutGroup() { } | |
public override void SetLayoutHorizontal() { | |
SetCellsAlongAxis(0); | |
} | |
public override void SetLayoutVertical() { | |
SetCellsAlongAxis(1); | |
} | |
// Based on SetCellsAlongAxis from GridLayoutGroup with appropriate changes | |
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; | |
float paddedWidth = width - padding.horizontal; | |
float paddedHeight = height - padding.vertical; | |
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((paddedWidth + spacing.x + 0.001f) / (cellSize.x + spacing.x))); | |
if (cellSize.y + spacing.y <= 0) | |
cellCountY = int.MaxValue; | |
else | |
cellCountY = Mathf.Max(1, Mathf.FloorToInt((paddedHeight + 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 remainingSpacePerAxisStep = new Vector2( | |
(paddedWidth - requiredSpace.x) / Mathf.Max(1, actualCellCountY - 1), | |
(paddedHeight - requiredSpace.y) / Mathf.Max(1, actualCellCountX - 1) | |
); | |
if (useMaxSkewDistance) { | |
remainingSpacePerAxisStep = new Vector2( | |
Mathf.Min(maxSkewDistance.x, remainingSpacePerAxisStep.x), | |
Mathf.Min(maxSkewDistance.y, remainingSpacePerAxisStep.y) | |
); | |
} | |
Vector2 usedSpace = new Vector2( | |
(skewAxis == Axis.Horizontal ? (requiredSpace.x + remainingSpacePerAxisStep.x * (actualCellCountY - 1)) : requiredSpace.x), | |
(skewAxis == Axis.Vertical ? (requiredSpace.y + remainingSpacePerAxisStep.y * (actualCellCountX - 1)) : requiredSpace.y) | |
); | |
Vector2 startOffset = new Vector2( | |
GetStartOffset(0, usedSpace.x), | |
GetStartOffset(1, usedSpace.y) | |
); | |
int positionX; | |
int positionY; | |
float skewX; | |
float skewY; | |
for (int i = 0; i < rectChildren.Count; i++) { | |
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; | |
skewX = (skewAxis == Axis.Horizontal ? remainingSpacePerAxisStep.x * positionY : 0f); | |
skewY = (skewAxis == Axis.Vertical ? remainingSpacePerAxisStep.y * positionX : 0f); | |
SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (cellSize[0] + spacing[0]) * positionX + skewX, cellSize[0]); | |
SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (cellSize[1] + spacing[1]) * positionY + skewY, cellSize[1]); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Skewed Grid Layout Group | |
/// =============================== | |
/// | |
/// Written by Martin Nerurkar | |
/// - http://www.martin.nerurkar.de | |
/// - http://www.sharkbombs.com | |
/// | |
/// You are free to moddify and use this code in your own projects | |
/// You are not allowed to remove this copyright notice | |
/// You are not allowed to resell this code, even if modified | |
using UnityEngine; | |
using UnityEngine.UI; | |
using UnityEditorInternal; | |
using UnityEditor.AnimatedValues; | |
namespace UnityEditor.UI { | |
[CustomEditor(typeof(SkewedGridLayoutGroup), true)] | |
[CanEditMultipleObjects] | |
public class SkewedGridLayoutGroupEditor : GridLayoutGroupEditor { | |
SerializedProperty m_skewAxis; | |
SerializedProperty m_useMaxSkewDistance; | |
SerializedProperty m_maxSkewDistance; | |
protected override void OnEnable() { | |
base.OnEnable(); | |
m_skewAxis = serializedObject.FindProperty("m_skewAxis"); | |
m_useMaxSkewDistance = serializedObject.FindProperty("m_useMaxSkewDistance"); | |
m_maxSkewDistance = serializedObject.FindProperty("m_maxSkewDistance"); | |
} | |
public override void OnInspectorGUI() { | |
base.OnInspectorGUI(); | |
serializedObject.Update(); | |
EditorGUILayout.PropertyField(m_skewAxis, true); | |
EditorGUILayout.PropertyField(m_useMaxSkewDistance, true); | |
bool wasEnabled = GUI.enabled; | |
GUI.enabled = wasEnabled && m_useMaxSkewDistance.boolValue; | |
EditorGUILayout.PropertyField(m_maxSkewDistance, true); | |
GUI.enabled = wasEnabled; | |
serializedObject.ApplyModifiedProperties(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment