根据 Horizo​​ntalLayoutGroup 等,在 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;
                }
            }
        }
    }
}

使用方法

  1. 将此脚本附加到您的面板,就像您使用其他布局组(如 GridViewLayout)一样
  2. 添加 UI 元素(按钮、图像等)作为面板的 child。
  3. ContentSizeFitter 组件添加到 children 并将 Horizontal FitVertical Fit 属性设置为 Preferred Size
  4. Layout Element 组件添加到 child 并设置 Preferred WidthPreferred Height 值。这些值将控制 UI 元素的大小。您也可以改用 Min WidthMin Height
  5. 根据需要添加任意数量的元素并应用相同的过程以获得所需的大小。

这是检查器中的样子 window :

使用不同尺寸的按钮进行测试,效果很好。

注意:

  • 我从 Unity UI 代码修改了 GridLayoutGroup class 以获得所需的代码 行为。因为它是从 LayoutGroup 派生的,它控制 child仁的RectTransform属性。我们需要使用 ContentSizeFitterLayoutElement 在 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