Unity: размер родителя соответствует размеру дочерних элементов

У меня есть пустой холст, который имеет несколько объектов в качестве дочерних. По ходу игры к родителю добавляются новые дочерние элементы. Я хочу, чтобы родитель соответствовал размеру своих детей.

Я уже много читал на эту тему, но каждый раз натыкался на ContentSizeFitter, который у меня к сожалению не работает, так как ему нужна группа раскладок, которой у меня нет, т.к. дети не имеют порядка и дико размещен.

Я использую Unity 2018.3.4f1 Personal.

Родитель без размера:

Unity: размер родителя соответствует размеру дочерних элементов

Размер родителя (красный: новый, зеленый: старый):

Unity: размер родителя соответствует размеру дочерних элементов

Структура:

Canvas
 |- Empty
     |- Child 1
     |- Child 2
     |- Child X

4 вершины будут определять размер родителя: самая левая, самая правая, самая верхняя и самая нижняя. Имея их, можно измерить центр и размер родителя. Вычислите эти вершины в методе Update, и все готово.

Daniel 06.02.2019 23:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
5 287
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я думаю, вы можете найти самую левую и самую правую границу, а затем добавить разницу этого значения как sizeDelta родителя RectTransform.

Это должно исправить размер (повторите с верхом и низом для Y).

Для позиции вы можете получить Верхний левый угол с самой левой границей и верхней границей в Vector2, затем вы устанавливаете этот вектор как anchoredPosition для родительского RectTransform.

Обратите внимание, что стержень родителя должен быть в верхнем левом углу, чтобы это работало.

Этот код только что-то подошел, но недостаточно хорош.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FitParentToChildren : MonoBehaviour
{
    #region INTERNAL

    public RectTransform parent;
    public RectTransform[] children;

    public float minX = 100000;
    public float maxX = -100000;
    public float minY = 100000;
    public float maxY = -100000;

    #endregion

    #region INITIALIZATION

    void Awake()
    {
        parent = GetComponent<RectTransform>();
        children = GetComponentsInChildren<RectTransform>();
    }

    void Start()
    {
        SetMinMaxValues();
        FitToChildren();
    }

    #endregion

    #region BEHAVIOURS

    private void FitToChildren()
    {
        parent.sizeDelta = GetNewRect();
        parent.anchoredPosition = GetTopLeftCornerPositon();
    }

    private void SetMinMaxValues()
    {
        for (int i = 1; i < children.Length; i++)
        {
            float tempMinX = children[i].anchoredPosition.x - children[i].sizeDelta.x / 2;
            if (tempMinX < minX) minX = tempMinX;

            float tempMaxX = children[i].anchoredPosition.x + children[i].sizeDelta.x / 2;
            if (tempMaxX > maxX) maxX = tempMaxX;

            float tempMinY = children[i].anchoredPosition.y - children[i].sizeDelta.y / 2;
            if (tempMinY < minY) minY = tempMinY;

            float tempMaxY = children[i].anchoredPosition.y + children[i].sizeDelta.y / 2;
            if (tempMaxY > maxY) maxY = tempMaxY;
        }
    }

    private Vector2 GetNewRect()
    {
        return new Vector2 (maxX - minX, maxY - minY);
    }

    private Vector2 GetTopLeftCornerPositon()
    {
        return new Vector2 (minX, maxY);
    }

    #endregion
}

ГИФ ЗДЕСЬ

Большое спасибо, ваш код не сработал, но он определенно помог мне и дал мне несколько хороших идей.

Timisorean 07.02.2019 19:55

У меня было мало времени, чтобы помочь, но это что-то XD

Horothenic 08.02.2019 21:14

Что-то вроде этого должно работать. Он перебирает все дочерние элементы объекта по вашему выбору, затем находит границы рендерера этих объектов и обновляет минимальное и максимальное значения по мере продвижения. В конце функции Start() переменные min и max содержат координаты углов прямоугольника, определяющего границы.

public class NewBehaviourScript : MonoBehaviour
{
    void Start ()
    {
        Vector2 min = new Vector2 (0, 0);
        Vector2 max = new Vector2 (0, 0);

        GetParentBounds (gameObject, out min, out max);
        Debug.Log (min);
        Debug.Log (max);

    }

    void GetParentBounds (GameObject GO, out Vector2 pMin, out Vector2 pMax)
    {
        Transform[] ts = GO.GetComponentsInChildren<Transform> ();

        Vector2 parentMin = new Vector2 (float.MaxValue, float.MaxValue);
        Vector2 parentMax = new Vector2 (float.MinValue, float.MinValue);

        foreach (Transform t in ts) {
            Renderer rend = t.GetComponent<Renderer> ();
            if (rend != null) {
                Vector2 min = rend.bounds.min;
                Vector2 max = rend.bounds.max;
                Debug.Log (string.Format ("[{0}, {1}], [{2}, {3}]", min.x, min.y, max.x, max.y));
                parentMin.x = Mathf.Min (parentMin.x, min.x);
                parentMin.y = Mathf.Min (parentMin.y, min.y);
                parentMax.x = Mathf.Max (parentMax.x, max.x);
                parentMax.y = Mathf.Max (parentMax.y, max.y);
            }
        }

        pMin = parentMin;
        pMax = parentMax;
    }
}

Вам все еще нужно использовать эти данные, которые вы каким-то образом получили

derHugo 06.02.2019 21:51

Ваш код не работает для меня. Родитель является потомком Canvas, поэтому у него есть Canvas Renderer. А t.GetComponent<Renderer> () возвращает ноль.

Timisorean 07.02.2019 14:19

Есть ли у них сетчатые фильтры? вы можете попробовать изменить внутреннюю часть foreach следующим образом: Renderer mf = t.GetComponent<MeshFilter> (); if (mf!= null) { Vector2 min = mf.mesh.bounds.min; Vector2 max = mf.mesh.bounds.max; parentMin.x = Mathf.Min (parentMin.x, min.x); parentMin.y = Mathf.Min (parentMin.y, min.y); parentMax.x = Mathf.Max (parentMax.x, max.x); parentMax.y = Mathf.Max (parentMax.y, max.y); }

Chris H 07.02.2019 15:55

Нет, у них есть только RectTransform, CanvasRenderer, Image, Outline и коллайдер.

Timisorean 07.02.2019 18:42
Ответ принят как подходящий

Я написал небольшой кусок кода, который выполняет свою работу или делает так, как я хочу, и мне этого достаточно:

using UnityEngine;

public class SizeFitter : MonoBehaviour {
    public void CheckForChanges() {
        RectTransform children = transform.GetComponentInChildren<RectTransform>();

        float min_x, max_x, min_y, max_y;
        min_x = max_x = transform.localPosition.x;
        min_y = max_y = transform.localPosition.y;

        foreach (RectTransform child in children) {
            Vector2 scale = child.sizeDelta;
            float temp_min_x, temp_max_x, temp_min_y, temp_max_y;

            temp_min_x = child.localPosition.x - (scale.x / 2);
            temp_max_x = child.localPosition.x + (scale.x / 2);
            temp_min_y = child.localPosition.y - (scale.y / 2);
            temp_max_y = child.localPosition.y + (scale.y / 2);

            if (temp_min_x < min_x)
                min_x = temp_min_x;
            if (temp_max_x > max_x)
                max_x = temp_max_x;

            if (temp_min_y < min_y)
                min_y = temp_min_y;
            if (temp_max_y > max_y)
                max_y = temp_max_y;
        }

        GetComponent<RectTransform>().sizeDelta = new Vector2(max_x - min_x, max_y - min_y);
    }
}

Итак, вы действительно использовали мою идею, но с лучшим подходом, КРУТО!! но я призываю вас избегать использования этого в обновлении, это будет невероятно тяжелым для процессора. Вы можете избежать его создания функции, которая заставляет родителя перемещать или масштабировать или что-либо еще дочерние, поэтому он масштабируется только при изменении значений.

Horothenic 08.02.2019 21:21
Bounds bound = new Bounds();
for (int i = 0; i < children.Length; ++i)
{
    RectTransform child = children[i];
    if (child != null && child.gameObject.activeSelf)
    {
        Vector2 pos = child.localPosition;
        Vector2 min = pos - child.sizeDelta * child.pivot;
        Vector2 max = min + child.sizeDelta;
        Bounds temp = new Bounds();
        temp.SetMinMax(min, max);
        bound.Encapsulate(temp);
    }
}

Другие вопросы по теме