根据Horizo​​ntalLayoutGroup等修正Unity3D中的"FlowLayoutGroup"

Fat*_*tie 12 unity-game-engine unity3d-ui

假设你想在Unity UI中使用普通的刷新左流.例子:

在此输入图像描述

在此输入图像描述

事实上,为了回答这个问题,我已经实现了冲洗左流"艰难的方式".在Unity autolayout中设置一个"垂直行组",在顶层附加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....
    }
}
Run Code Online (Sandbox Code Playgroud)

FattieFlow将完全重新流动它向左冲(通过操纵线条,艰难的方式).这是一个脚本,演示等:艰难的方式.

但这是一个糟糕的解决方案.

理想情况下,从 UI.Horizo​​ntalLayoutGroupUI.VerticalLayoutGroup 开始应该可以创建

FlowLayoutGroup

它放置,向左冲,进入一个块.(事实上​​,它应该根据需要扩展,依此类推......就像HorizontalLayoutGroup行为一样).

你似乎必须HorizontalOrVerticalLayoutGroup从那里开始并在那里工作.

如何将一个做到这一点(如果它不存在)?

Uma*_*r M 22

到目前为止,我已经想出了这个:

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;
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如何使用

  1. 将此脚本附加到面板,就像使用其他布局组(如GridViewLayout)一样
  2. 添加UI元素(按钮,图像等)作为Panel的子元素.
  3. ContentSizeFitter向子项添加组件,Horizontal Fit并将" Vertical Fit属性" 设置为" 首选大小"
  4. Layout Element组件添加到子级,设置Preferred WidthPreferred Height值.这些值将控制UI元素的大小.您也可以使用Min WidthMin Height不是.
  5. 添加任意数量的元素并应用相同的过程来获得所需的大小.

这是它在检查器窗口中的样子:

预习

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

这就是它的样子

注意 :

  • GridLayoutGroup从Unity UI代码修改了类以获得所需的行为.因为它来源于LayoutGroup控制儿童的RectTransform属性.我们需要使用 ContentSizeFitterLayoutElement儿童改变他们的大小.
  • 它仅适用于从左上角开始的水平流动,不同于GridLayout它允许从垂直开始并从其他角落开始.我不认为这是一个限制,因为这只是Flow Layout Group可以预期的行为.
  • 我还在GithHub上添加了一个Repository,以防任何人想要为它做出贡献.

谢谢!


Wap*_*ull 5

“UnityUIExtensions”编译中还包含社区制作的FlowLayoutGroup。它在更新过程中经历了一些修订和用户贡献。

https://github.com/Unity-UI-Extensions/com.unity.uiextensions

正如其主页所述,该项目本身是免费的。

贡献是可选的。资产/代码将始终保持免费

截图

最简单的形式是这样的,有一个问题是您需要在每个子项上使用 LayoutElement 来手动控制项目的宽度/高度。

在此输入图像描述

作为一个挑逗,自动尺寸调整解决方案是可能的,但到目前为止我无法找到开箱即用的解决方案,这需要我自己的自定义脚本(所示的 LayoutElementFilter 组件)这是一个相当复杂的过程,我将把它作为读者的家庭作业来获取它上班。

在此输入图像描述

安装

A.直接下载(更简单)

只需下载整个存储库或从 github 克隆/下载 zip,然后将整个文件夹放在类似“ YourProject/Packages/com.unity.uiextensions Where effective package.jsonwill sat at”的位置中YourProject/Packages/com.unity.uiextensions/package.json

B. 使用 git/UPM

阅读本指南,转到 Unity 内的 PackageManager 并选择从 GIT 添加。