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

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

问题描述

假设您希望在 Unity UI 中使用普通的左移.示例:

事实上,为了回答

用不同大小的按钮测试过,效果很好.

注意:

  • 我修改了 Unity UI 代码中的 GridLayoutGroup 类以获得所需行为.因为它是从 LayoutGroup 派生出来的,它控制孩子的 RectTransform 属性.我们需要使用ContentSizeFitterLayoutElement 在孩子上改变他们的大小.
  • 它只适用于从左上角开始的水平流,不像 GridLayout 允许从垂直开始并从其他角开始.我不认为这是一种限制,因为这只是流布局组可以预期的行为.
  • 我还添加了一个 GithHub 上的存储库,以防有人想为此做出贡献.

谢谢!

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

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 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.

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

FlowLayoutGroup

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

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;
                }
            }
        }
    }
}

How to Use

  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.

NOTE :

  • 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.

Thanks!

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

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