根据 HorizontalLayoutGroup 等,在 Unity3D 中更正 'FlowLayoutGroup'
Correct 'FlowLayoutGroup' in Unity3D, as per HorizontalLayoutGroup etc
假设您想要在 Unity UI 中使用普通 齐平左流 。示例:
事实上,为了回答 ,我已经“艰难地”实现了左对齐流。在Unity自动布局中设置“垂直行组”,在顶层附加FattieFlow,
public class FattieFlow : MonoBehaviour
{
public GameObject modelRow;
public GameObject modelItem;
public void Flow()
{
screen = GetComponent<RectTransform>().rect.width;
// move downwards any which need to be moved downwards
int row = 0;
while (row < transform.childCount) // (dynamic)
{
if (transform.GetChild(row).gameObject.activeSelf) FlowRow(row);
++row;
}
// et cetera....
}
}
FattieFlow
将完全重新流向左对齐(通过操纵线条,困难的方式)。这是脚本、演示等:.
但这是一个糟糕的解决方案。
理想情况下,从
UI.HorizontalLayoutGroup and UI.VerticalLayoutGroup
应该可以创建
FlowLayoutGroup
它向左齐平布置成一个块。 (实际上它应该根据需要扩展等等,块......完全按照 HorizontalLayoutGroup
的行为)。
您似乎必须从 HorizontalOrVerticalLayoutGroup
开始,然后从那里开始工作。
如何做到这一点(如果已经存在)?
到目前为止我想到了这个:
FlowLayoutGroup
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
[AddComponentMenu("Layout/Flow Layout Group", 153)]
public class FlowLayoutGroup : LayoutGroup
{
public enum Corner {
UpperLeft = 0,
UpperRight = 1,
LowerLeft = 2,
LowerRight = 3
}
public enum Constraint {
Flexible = 0,
FixedColumnCount = 1,
FixedRowCount = 2
}
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); }
}
protected FlowLayoutGroup()
{}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
}
#endif
public override void CalculateLayoutInputHorizontal()
{
base.CalculateLayoutInputHorizontal();
int minColumns = 0;
int preferredColumns = 0;
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;
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);
minRows = 1;
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);
}
int cellsPerMainAxis, actualCellCountX, actualCellCountY;
int positionX;
int positionY;
float totalWidth = 0;
float totalHeight = 0;
float lastMaxHeight = 0;
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.
float width = rectTransform.rect.size.x;
float height = rectTransform.rect.size.y;
int cellCountX = 1;
int cellCountY = 1;
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)));
cellsPerMainAxis = cellCountX;
actualCellCountX = Mathf.Clamp(cellCountX, 1, rectChildren.Count);
actualCellCountY = Mathf.Clamp(cellCountY, 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)
);
totalWidth = 0;
totalHeight = 0;
Vector2 currentSpacing = Vector2.zero;
for (int i = 0; i < rectChildren.Count; i++)
{
SetChildAlongAxis(rectChildren[i], 0, startOffset.x + totalWidth /*+ currentSpacing[0]*/, rectChildren[i].rect.size.x);
SetChildAlongAxis(rectChildren[i], 1, startOffset.y + totalHeight /*+ currentSpacing[1]*/, rectChildren[i].rect.size.y);
currentSpacing = spacing;
totalWidth += rectChildren[i].rect.width + currentSpacing[0];
if (rectChildren[i].rect.height > lastMaxHeight)
{
lastMaxHeight = rectChildren[i].rect.height;
}
if (i<rectChildren.Count-1)
{
if (totalWidth + rectChildren[i+1].rect.width + currentSpacing[0] > width )
{
totalWidth = 0;
totalHeight += lastMaxHeight + currentSpacing[1];
lastMaxHeight = 0;
}
}
}
}
}
使用方法
- 将此脚本附加到您的面板,就像您使用其他布局组(如 GridViewLayout)一样
- 添加 UI 元素(按钮、图像等)作为面板的 child。
- 将
ContentSizeFitter
组件添加到 children 并将 Horizontal Fit
和 Vertical Fit
属性设置为 Preferred Size
- 将
Layout Element
组件添加到 child 并设置 Preferred Width
和 Preferred Height
值。这些值将控制 UI 元素的大小。您也可以改用 Min Width
和 Min Height
。
- 根据需要添加任意数量的元素并应用相同的过程以获得所需的大小。
这是检查器中的样子 window :
使用不同尺寸的按钮进行测试,效果很好。
注意:
- 我从 Unity UI 代码修改了
GridLayoutGroup
class 以获得所需的代码
行为。因为它是从 LayoutGroup
派生的,它控制
child仁的RectTransform
属性。我们需要使用
ContentSizeFitter
和 LayoutElement
在 children 上改变他们的
尺寸。
- 它只适用于从左上角开始的水平流,不像
GridLayout
允许垂直开始,也可以从其他角开始。我不认为这是一个限制,因为这只是 Flow Layout Group. 可以预期的行为
- 我还添加了一个 Repository on GithHub 以防有人想为此做出贡献。
谢谢!
还有community-made FlowLayoutGroup包含在“UnityUIExtensions”编译中。它在更新过程中经历了一些修改和用户贡献。
https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls/FlowLayoutGroup
安装
Recommended to use UPM for Unity 2019+
旁注:项目本身是免费的,如其main page所述。
contribution is optional. The assets / code will always remain FREE
假设您想要在 Unity UI 中使用普通 齐平左流 。示例:
事实上,为了回答
public class FattieFlow : MonoBehaviour
{
public GameObject modelRow;
public GameObject modelItem;
public void Flow()
{
screen = GetComponent<RectTransform>().rect.width;
// move downwards any which need to be moved downwards
int row = 0;
while (row < transform.childCount) // (dynamic)
{
if (transform.GetChild(row).gameObject.activeSelf) FlowRow(row);
++row;
}
// et cetera....
}
}
FattieFlow
将完全重新流向左对齐(通过操纵线条,困难的方式)。这是脚本、演示等:
但这是一个糟糕的解决方案。
理想情况下,从 UI.HorizontalLayoutGroup and UI.VerticalLayoutGroup 应该可以创建
FlowLayoutGroup
它向左齐平布置成一个块。 (实际上它应该根据需要扩展等等,块......完全按照 HorizontalLayoutGroup
的行为)。
您似乎必须从 HorizontalOrVerticalLayoutGroup
开始,然后从那里开始工作。
如何做到这一点(如果已经存在)?
到目前为止我想到了这个:
FlowLayoutGroup
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
[AddComponentMenu("Layout/Flow Layout Group", 153)]
public class FlowLayoutGroup : LayoutGroup
{
public enum Corner {
UpperLeft = 0,
UpperRight = 1,
LowerLeft = 2,
LowerRight = 3
}
public enum Constraint {
Flexible = 0,
FixedColumnCount = 1,
FixedRowCount = 2
}
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); }
}
protected FlowLayoutGroup()
{}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
}
#endif
public override void CalculateLayoutInputHorizontal()
{
base.CalculateLayoutInputHorizontal();
int minColumns = 0;
int preferredColumns = 0;
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;
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);
minRows = 1;
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);
}
int cellsPerMainAxis, actualCellCountX, actualCellCountY;
int positionX;
int positionY;
float totalWidth = 0;
float totalHeight = 0;
float lastMaxHeight = 0;
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.
float width = rectTransform.rect.size.x;
float height = rectTransform.rect.size.y;
int cellCountX = 1;
int cellCountY = 1;
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)));
cellsPerMainAxis = cellCountX;
actualCellCountX = Mathf.Clamp(cellCountX, 1, rectChildren.Count);
actualCellCountY = Mathf.Clamp(cellCountY, 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)
);
totalWidth = 0;
totalHeight = 0;
Vector2 currentSpacing = Vector2.zero;
for (int i = 0; i < rectChildren.Count; i++)
{
SetChildAlongAxis(rectChildren[i], 0, startOffset.x + totalWidth /*+ currentSpacing[0]*/, rectChildren[i].rect.size.x);
SetChildAlongAxis(rectChildren[i], 1, startOffset.y + totalHeight /*+ currentSpacing[1]*/, rectChildren[i].rect.size.y);
currentSpacing = spacing;
totalWidth += rectChildren[i].rect.width + currentSpacing[0];
if (rectChildren[i].rect.height > lastMaxHeight)
{
lastMaxHeight = rectChildren[i].rect.height;
}
if (i<rectChildren.Count-1)
{
if (totalWidth + rectChildren[i+1].rect.width + currentSpacing[0] > width )
{
totalWidth = 0;
totalHeight += lastMaxHeight + currentSpacing[1];
lastMaxHeight = 0;
}
}
}
}
}
使用方法
- 将此脚本附加到您的面板,就像您使用其他布局组(如 GridViewLayout)一样
- 添加 UI 元素(按钮、图像等)作为面板的 child。
- 将
ContentSizeFitter
组件添加到 children 并将Horizontal Fit
和Vertical Fit
属性设置为 Preferred Size - 将
Layout Element
组件添加到 child 并设置Preferred Width
和Preferred Height
值。这些值将控制 UI 元素的大小。您也可以改用Min Width
和Min Height
。 - 根据需要添加任意数量的元素并应用相同的过程以获得所需的大小。
这是检查器中的样子 window :
使用不同尺寸的按钮进行测试,效果很好。
注意:
- 我从 Unity UI 代码修改了
GridLayoutGroup
class 以获得所需的代码 行为。因为它是从LayoutGroup
派生的,它控制 child仁的RectTransform
属性。我们需要使用ContentSizeFitter
和LayoutElement
在 children 上改变他们的 尺寸。 - 它只适用于从左上角开始的水平流,不像
GridLayout
允许垂直开始,也可以从其他角开始。我不认为这是一个限制,因为这只是 Flow Layout Group. 可以预期的行为
- 我还添加了一个 Repository on GithHub 以防有人想为此做出贡献。
谢谢!
还有community-made FlowLayoutGroup包含在“UnityUIExtensions”编译中。它在更新过程中经历了一些修改和用户贡献。
https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls/FlowLayoutGroup
安装
Recommended to use UPM for Unity 2019+
旁注:项目本身是免费的,如其main page所述。
contribution is optional. The assets / code will always remain FREE