根据Horizo​​ntalLayoutGroup等,在Unity3D中更正"FlowLayoutGroup" [英] Correct 'FlowLayoutGroup' in Unity3D, as per HorizontalLayoutGroup etc

查看:169
本文介绍了根据Horizo​​ntalLayoutGroup等,在Unity3D中更正"FlowLayoutGroup"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您要在Unity UI中进行普通的向左冲洗.例子:

Say you want ordinary flush left flow in Unity UI. Examples:

实际上,回答这个问题我已经实现了困难的方式"向左冲水.列出垂直的行组".在Unity自动版式中,在顶部附加FattieFlow,

In fact, to answer THIS question I already achieved flush left flow "the hard way". Set out a "vertical group of rows" in Unity autolayout, attach FattieFlow at the top level,

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将完全重排其向左冲(通过手动操作线).这是脚本,演示等:困难的方式.

FattieFlow will completely re-flow it flush-left (by manipulating the lines, the hard way). Here's a script, demo, etc: the hard way.

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

But that's a poor solution.

理想情况下,从 UI.Horizo​​ntalLayoutGroup

Ideally, starting with UI.HorizontalLayoutGroup and UI.VerticalLayoutGroup it should be possible to create

布局,向左平移到一个块中. (实际上,它应该根据需要扩展,依此类推……完全符合HorizontalLayoutGroup的行为.)

which lays out, flush left, into a block. (And indeed it should expand, and so on, the block as required ... exactly as HorizontalLayoutGroup behaves).

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

It would seem that you'd have to start with HorizontalOrVerticalLayoutGroup and work from there.

这会怎么做(如果它不存在已经存在)?

How would one do this (if it does not already exist)?

推荐答案

到目前为止,我已经提出了这个建议:

So far I have came up with this:

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元素(按钮,图像等)添加为Panel的子元素.
  3. 向子级添加ContentSizeFitter组件,并将Horizontal FitVertical Fit属性设置为首选大小
  4. Layout Element组件添加到子项并设置Preferred WidthPreferred Height值.这些值将控制UI元素的大小.您还可以改用Min WidthMin Height.
  5. 根据需要添加任意数量的元素,并应用相同的步骤以获得所需的大小.
  1. Attach this script to your panel just like you would use other layout groups like GridViewLayout
  2. Add UI element (Buttons, Images etc) as child of Panel.
  3. Add ContentSizeFitter component to children and set Horizontal Fit and Vertical Fit properties to Preferred Size
  4. Add Layout Element component to child and set Preferred Width and Preferred Height values. These values will control size of UI Element. You can also use Min Width and Min Height instead.
  5. Add as many elements as you want and apply same procedure to get desired size.

这是在检查器窗口中的外观:

This is how it looks like in inspector window :

使用不同大小的按钮进行了测试,效果很好.

Tested with buttons of different sizes and it works well.

注意:

  • I modified GridLayoutGroup class from Unity UI code to get desired behaviour. As it is derived from LayoutGroup which controls children's RectTransform properties. We need to use ContentSizeFitter and LayoutElement on children to change their size.
  • It only works for horizontal flow starting from top left unlike GridLayout which allows to start vertical and start from other corners as well. I don't consider it a limitation as this is only behaviour that can be expected from a Flow Layout Group.
  • I have also added a Repository on GithHub in case anyone wants to contribute to it.

谢谢!

这篇关于根据Horizo​​ntalLayoutGroup等,在Unity3D中更正"FlowLayoutGroup"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆