Не корректная робота transform.childCount

Емм... Я если честно вообще в изумлении и ни черта не понимаю. У меня есть gameObject "Content", к которому прикреплён GridLayoutGroup и ContentSizeFilter. Этот "Content" хранит в себе список дочерних элементов с которыми будет взаимодействовать игрок. И получается то что динамически будет меняться размер(имеется ввиду transform.childCount) этого Content. Мне нужно программно отслежывать эти изменения дабы делать другие действия.

    public class ObserverChildCount : MonoBehaviour
{
    [SerializeField] private Host host;
    private int lastCount;
    private bool isLoad;

    private void Update()
    {
        if (lastCount != transform.childCount && isLoad)
        {
            Debug.Log($"Send. lastCount {lastCount} | childCount {transform.childCount}");
            //HandlerSendItemInBase.Instance.SetHostForSend(host);
            lastCount = transform.childCount;
        }
    }

    public void SetIsLoad(bool state)
    {
        if (state)
        {
            Debug.Log($"{host} | lastCount - {lastCount} ||| childCount - {transform.childCount}");
            lastCount = transform.childCount;
        }
        isLoad = state;
    }
}

Этим как раз и занимается этот скрипт. Update проверяет были изменения. isLoad - флаг что бы начать наблюдение за изменением количества. Если флаг false то идёт изначальное заполнение "Content" по этому условие в Update не срабатывает. Когда прошёл Instantiate всех дочерних элементов флаг переходит в значение true и lastCount приравнивается к текущему значению. По сути прошла настройка всего для корректной роботы условия в Update. Но случается следущее.

Player | lastCount - 1 ||| childCount - 2

Это данные приходят с SetIsLoad. Соответственно следующая строчка коду приравнивает lastCount к childCount. НО. Дальше

Send. lastCount 2 | childCount 1

Данные первого прохождения цикла после SetIsLoad. Сейчас дальше сложно, постараюсь описать максимально понятно. Прошу понять и помочь. gameObject "Content" активируется по нажатию кнопки. При активации у него проверяется childCount и если там есть что то то удаляется. Дабы данные были свежими. И isLoad как раз выполняет роль регулировщика(что нужно делать и когда). В SetIsLoad в нужный момент обновляет lastCount, но данные в него приходят не корректные. На самом деле там не 2 а 1. Дело в том что когда я активирую второй раз Content то все дочерние элементы удаляются но childCount не становится 0 а когда идёт вызов SetIsLoad(true) то к не обнулившемуся childCount добавляется count новых дочерних элементов. По этому в lastCount записывается 2 а не 1. Дальше хуже. После SetIsLoad едёт первый запуск Update. isLoad - true, что даёт уже право на срабатывание условия. lastCount у нас соответственно неверный а childCount уже обновился и стал 1(то есть правильным) и идёт ложное срабатывание. Я не могу понять, почему????

lastCount = transform.childCount;

В SetIsLoad срабатывает не правильно. На момент когда вызывается SetIsLoad(true) нету никаких действий по поводу дочерних элементов. Все данные уже загружены. Но childCount не обновляется.


Ответы (1 шт):

Автор решения: RT29a

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

  1. Если вы используете обычный Destroy(), то он является отложенным, и не выполняется сразу же.
  2. Если вы удаляете дочерние объекты, использую цикл похожий на этот, то можете получить странное поведение, когда обрабатываться будет только каждый второй объект. Так реализуется IEnumerator в классе Transform(как и во многих других кстати).
    foreach (Transform child in transform)
    {
        child.parent = null;
    }
    

Попробуйте удалять элементы следующим способом:

using System.Linq;

foreach (Transform child in observerChildCountInstance.transform.Cast<Transform>().ToArray())
{
    Destroy(child.gameObject); // это вызовет отложенное удаление
    child.SetParent(null); // а это удалит его из списка дочерних объектов прямо сейчас
}

Этот же цикл можно переписать используя DestroyImmediate, но это не так показательно. Так же на основе этих знаний, можно написать реализацию и без Linq, например перебирая элементы с конца, но это уже тема для другого вопроса.

→ Ссылка