Как убирать пустоту из листа, оставшуюся после уничтожения объектов, в него входящих?

Имеются объекты в листе под названием Group.
public List<GameObject> Group = new List<GameObject>();

Соответственно, когда один из объектов уничтожается:
Destroy(gameObject);
на его месте в группе появляется Missing, а в консоли - некритичные ошибки.

Каким образом можно убирать уничтоженные объекты из Group, чтобы лист становился меньше?

Например, имеется 2 объекта. Первый объект уничтожается. Второй объект должен встать на место первого, а лист уменьшиться в размере на один слот.

Пробовал RemoveAt и Remove:
controller.Group.Remove(gameObject);
Destroy(gameObject);

Где controller - отдельный скрипт с листами, к которому обращается уничтожаемый объект.
Но появляется критическая ошибка. Есть идеи?
  • Вопрос задан
  • 1517 просмотров
Решения вопроса 3
MrMureno
@MrMureno Куратор тега Unity
VR for all
Давайте попробую пояснить что не так.
foreach (GameObject tmp in Group)
{
}

вот тут вы запускаете перебор всех элементов.
но потом вы МЕНЯЕТЕ КОЛИЧЕСТВО элементов в коллекции.
Group.Remove(tmp);

и вот нумератор проходит по индексам, а они уже не совпадают.(после старта не обновляются).

В итоге если мы уничтожаем (не удаляем) объект по ссылке , через Destroy(gameObject);
остается список из ссылок на null;

Если же удаляем объекты из коллекции во время похода по коллекции - получаем ошибку с индексами.

как решить.
или сделать через for
for(int i =0; i <list.Count;i++)
{
Destroy(list[i]);
list.RemoveAt(i);
i--; // сместили индекс,если было удаление, так как размер коллекции уменьшился


другой вариант - просто после цикла с Destroy(gameObject);
почистить список list.Clear(); (если все зараз всегда удаляются)

Главное надеюсь, чтоб суть проблемы ясна стала.
Ответ написан
Комментировать
mindtester
@mindtester Куратор тега C#
http://iczin.su/hexagram_48
1 - при использовании foreach ни сама коллекция (лист, массив, перечисление, не важно) ни ее элементы, не подлежат изменению. только чтение. это правило языка C# (но за Mono не отвечаю)

2 - про использовании for обходите коллекцию с конца, а не сначала, таким образом не будет проблем с индексами (хотя можно и while использовать для обхода с головы, но на for думаю быстрее поймете суть). пример
for(int i = Group.Count - 1; i >= 0; i--)
{
  Destroy(list[i]);
  list.RemoveAt(i); // о размере списка не заморачиваемся вообще
}


3 - Unity поддерживает LINQ? там можно было бы еще элегантнее. но LINQ всегда проигрывает по производительности, которая в играх обычно важна. тем не менее
Group.RemoveAll(tmp => tmp is MissingReferenceException);
// хотя надо проверить совместимость

upd вообще то выходит Destroy не делаем. тогда теряется вся элегантность.. ну или надо подольше подумать ))

ps ну тогда уж и while это примерно так
var i = 0;
while (i < Group.Count)
  if (Group[i] is MissingReferenceException)
  {
    Destroy(Group[i]);
    Group.RemoveAt(i);
  }
  else
    i++;

ну и хардкорное применение for приктически так же выглядит
for (var i = 0; i < Group.Count;) // да да, инкримент просто не пишем и получается аналог while
  if (Group[i] is MissingReferenceException)
  {
    Destroy(Group[i]);
    Group.RemoveAt(i);
  }
  else
    i++;
Ответ написан
Комментировать
milssky
@milssky
Координатор племени фиолетовых обезьянок
Такая же проблема была описана тут: Что может быть MissingReferenceException: The object of type 'Object'?
Решение было в комментах от vicsint:
Для тех кто окажется тут. Если вы хотите, чтобы ненужные клоны удалялись Destroеm и процесс не прерывался, то необходимо гэйм обжиг удалить из компонентов главной сцены в юнити в левом верхнем меню.(при этом gameObject должен лежать в отдельной папке проекта и быть назначен оттуда на скрипт)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы