Исправьте "FlowLayoutGroup" в Unity3D, в соответствии с HorizontalLayoutGroup и т. Д.
Скажем, вы хотите обычный флеш левый поток в 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.HorizontLayoutGroup и UI.VerticalLayoutGroup, можно создать
FlowLayoutGroup
который выложен влево, в блок. (И действительно, он должен расширяться, и так далее, блок по мере необходимости... точно так же, как HorizontalLayoutGroup
ведет себя).
Казалось бы, вам придется начать с HorizontalOrVerticalLayoutGroup
и работать оттуда.
Как можно это сделать (если он еще не существует)?
1 ответ
До сих пор я придумал это:
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
- Добавьте элемент пользовательского интерфейса (кнопки, изображения и т. Д.) В качестве дочернего элемента Panel.
- добавлять
ContentSizeFitter
компонент для детей и установитьHorizontal Fit
а такжеVertical Fit
свойства предпочтительного размера - добавлять
Layout Element
компонент для ребенка и установитьPreferred Width
а такжеPreferred Height
ценности. Эти значения будут контролировать размер элемента пользовательского интерфейса. Вы также можете использоватьMin Width
а такжеMin Height
вместо. - Добавьте столько элементов, сколько вы хотите, и примените ту же процедуру, чтобы получить желаемый размер.
Вот как это выглядит в окне инспектора:
Протестировано с кнопками разных размеров и работает хорошо.
НОТА:
- Я модифицировал
GridLayoutGroup
класс из кода пользовательского интерфейса Unity, чтобы получить желаемое поведение. Как это происходит изLayoutGroup
который контролирует детейRectTransform
свойства. Нам нужно использоватьContentSizeFitter
а такжеLayoutElement
на детей, чтобы изменить их размер. - Это работает только для горизонтального потока, начиная с верхнего левого в отличие
GridLayout
что позволяет начать с вертикали и начать с других углов. Я не считаю это ограничением, так как это только поведение, которое можно ожидать от Flow Layout Group. - Я также добавил репозиторий на GithHub на случай, если кто-то захочет внести в него свой вклад.
Спасибо!
В компиляцию "UnityUIExtensions" входит также созданная сообществом FlowLayoutGroup. Он претерпел некоторые изменения и участие пользователей в процессе обновления.
https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls/FlowLayoutGroup
Установка
Рекомендуется использовать UPM для Unity 2019+
Боковое примечание: сам проект бесплатный, как указано на его главной странице.
взнос не является обязательным. Активы / код всегда будут БЕСПЛАТНЫМИ