38

[Туториал] Простой и удобный пул объектов для Unity3D

Урок рассчитан на новичков, у профи я догадываюсь уже написаны свои супер пулы на все случаи жизни =)


Итак, вкратце - зачем же нужен пул и что это такое?
Пул нужен для оптимизации в тех играх, где часто создаются и уничтожаются какие-нибудь объекты. Например, пули или враги.

При создании нового объекта, программа пробегается по оперативной памяти, выискивая свободное местечко куда бы этот объект запихнуть. Это долго. А потом надо еще записать в найденное место байты этого объекта, запустить конструктор, всякие события в движке, происходящие при создании объекта, и так далее. Всё это жрёт большое количество процессорного времени и может приводить к тормозам. Особенно на слабых компьютерах и на телефонах.

При частом удалении объектов тоже могут быть лаги, связанные с работой сборщика мусора. Сборщик мусора обычно запускается когда приложению нужно больше памяти, чем оно уже занимало раньше. Сборщик пробегается по уже занятой памяти, и пытается определить, какие объекты уже не будут использоваться в игре, потому что ни одному другому объекту они больше не нужны. Это занимает очень много процессорного времени.

Вот так выглядит сборщик мусора в мобильной стрелялке без пулов:

Так чем же нам поможет пул?
Очень просто - вместо уничтожения, объекты будут только деактивироваться, и записываться в пул, оставаясь таким образом в оперативной памяти. А вместо создания новых объектов, будут браться уже созданные объекты из пула, и только активироваться!

Дальше опишу свою реализацию пула на юнити, универсальную и простую в использовании:

Для начала инициализация:

Метод PoolManager.init() надо будет дёрнуть в начале игры, передав в параметр трансформ того геймобжекта, который точно не будет уничтожаться. В этот геймобжект будут класться все деактивированные объекты, чтобы случайно не удалить их по ходу игры, вызвав Destroy() изначального родительского геймобжекта =)

Про словарь (Dictionary) будет дальше.

Функция получения объекта из пула:

В параметр PoolManager.getGameObjectFromPool() надо передать префаб, по которому создаётся объект.

Все операции доставания из пула или укладывания в него происходят по имени префаба.

Строчка if(!poolsDictionary.ContainsKey(prefab.name)) проверяет, был ли уже в пуле объект от этого префаба, и если нет, то далее создаётся хранилище (LinkedList) для объектов которые создаются по этому префабу.


Строчка  if (poolsDictionary[prefab.name].Count > 0) проверяет, есть ли сейчас в хранилище деактивированные объекты от этого префаба. Если да, то объект берётся из хранилища, делается активным (SetActive()) и возвращается как результат работы функции.

Если же хранилище пустует, то создаётся новый объект через GameObject.Instantiate().
Ему присваивается имя, префаба, которое потом будет использоваться для возвращения в пул, и функция возвращает новый объект.

Функция возврата объекта в пул:

Тут всё просто, берётся имя объекта который мы хотим вернуть в пул, по этому имени в словаре находится хранилище, объект добавляется туда, переносится в безопасный parent и деактивируется.

Всё, пул готов!

Реализация очень простая, но у неё есть пара ограничений:
1) Нельзя делать префабы с одинаковыми названиями, лежащие в разных папках. В этом пуле они перемешаются потому что тут всё играется от названия префаба.
2) И соответственно нельзя динамически менять имя пулируемого геймобжекта, потому что оно должно быть как у префаба.

Но благодаря этим небольшим ограничениям, не надо возиться со всякими дополнительными компонентами типа IPoolable и т.д., мой пул очень прост в использовании. Просто вместо GameObject.Instantiate(prefab) вызываешь PoolManager.getFromPool(prefab), А вместо GameObject.Destroy(gameObject) PoolManager.putToPool(gameObject)

p.s. И конечно надо не забыть после взятия из пула проставить правильного родителя если это нужно, и прочие переменные вроде hp и так далее вернуть к исходным значениям.

Правила сообщества

ОБЩИЕ ПРАВИЛА:

- Уважайте чужой труд и используйте конструктивную критику

- Не занимайтесь саморекламой, пишите качественные и интересные посты

- Никакой политики


СТОИТ ПУБЛИКОВАТЬ:

- Посты о Вашей игре с историей её разработки и описанием полученного опыта

- Обучающие материалы, туториалы

- Интервью с опытными разработчиками

- Анонсы бесплатных мероприятий для разработчиков и истории их посещения;
- Ваши работы, если Вы художник/композитор и хотите поделиться ими на безвозмездной основе

НЕ СТОИТ ПУБЛИКОВАТЬ:

- Посты, содержащие только вопрос или просьбу помочь
- Посты, содержащие только идею игры

- Посты, единственная цель которых - набор команды для разработки игры

- Посты, не относящиеся к тематике сообщества

Подобные посты по решению администрации могут быть перемещены из сообщества в общую ленту.

ЗАПРЕЩЕНО:

- Публиковать бессодержательные посты с рекламой Вашего проекта (см. следующий пункт), а также все прочие посты, содержащие рекламу/рекламные интеграции

- Выдавать чужой труд за свой

Подобные посты будут перемещены из сообщества в общую ленту, а их авторы по решению администрации могут быть внесены в игнор-лист сообщества.


О РАЗМЕЩЕНИИ ССЫЛОК:

Ссылка на сторонний ресурс, связанный с игрой, допускается только при следующих условиях:

- Пост должен быть содержательным и интересным для пользователей, нести пользу для сообщества

- Ссылка должна размещаться непосредственно в начале или конце поста и только один раз

- Cсылка размещается в формате: "Страница игры в Steam: URL"

Вы смотрите срез комментариев. Показать все
0
Автор поста оценил этот комментарий

Что эксперты скажут? Годная реализация?

раскрыть ветку (11)
3
Автор поста оценил этот комментарий

Элементарная как кирпич, сойдет для того, чтобы грубо разбить окно, но полиция все равно среагирует.

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

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

Плюс словарь можно создать сразу, да и сразу указать "папку" на сцене где будут сохраняться все данные, т.е. функция Init лишняя.

раскрыть ветку (10)
1
Автор поста оценил этот комментарий

Получается, я один такой особенный, которому идея хранить маленькие короткоживущие объекты в словаре по текстовому ключу кажется не самой удачной даже за последние 5 минут?


Не то, чтобы я сильно против, но, имхо, убить производительность Юнити можно куда более изящно.

раскрыть ветку (6)
0
Автор поста оценил этот комментарий

Моя игра с введением этого пула тормозить перестала.)
Оверхед от создания и удаления объектов гораздо больше, чем от поиска в словаре по текстовому ключу.
По крайней мере если позиций в словаре не много, как у меня.

раскрыть ветку (4)
2
Автор поста оценил этот комментарий

Есть такая присказка: "Лучше - не значит хорошо". Да, хорошо, что перестали создавать объекты по каждому чиху, но...

Попробуй ключом сделать GetInstanceID(). Древние свитки подсказывают, что

The instance id of an object is always guaranteed to be unique.

И, на секунду, если те же свитки мне не врут (а я плюсовик - могу их неверно трактовать), Object.Instantiate со второй картинки кода

Returns a copy of the object original.

То есть опять всё работает через жопу. Вы просто отключаете сборщик мусора вместо того, чтобы переиспользовать старые пули. Проще уж сразу при загрузке уровня создавать массив из 10000 пуль и просто гонять по ним циклическим буфером, не забывая пропускать ещё живые.

раскрыть ветку (2)
0
Автор поста оценил этот комментарий

Окей, вчитался, был не очень прав, про ту часть с копированием. Остальные предложения актуальны, нужно сравнивать.

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Да, пулы иногда заполняются перед стартом уровня на загрузчике. Например какие-то враги могут быть только на определенных уровнях, тогда при загрузке уровня их заливается штук сто в пул, а после прохождения вычищаются.
Что до ключа, его негде хранить. Нету в геймобжекте халявного поля не несущего логики, кроме name. А чтобы добавить поле, надо делать отдельный компонент, добавлять его во все префабы, что является гемором))

0
Автор поста оценил этот комментарий

Вообще для того, чтобы снизить скорость поиска по пулу делают несколько пулов, разделяя все на эффекты\звуки\персонажи и тд.тп., а там потом еще можно разделить.

0
Автор поста оценил этот комментарий

То, что объекты висят в памяти производительность не убьет. Удалять-создавать новые объекты - одна из самых трудозатратных операций.

0
Автор поста оценил этот комментарий

Для простых игр  наверное и вообще вся эта возня с парентом лишняя. Я думаю перемещение объекта из трансформа в трансформ в юнити тоже не бесплатное, особенно если участвуют коллайдеры.
Мне это нужно было потому что у меня пулились объекты внутри других динамически создаваемых.)

раскрыть ветку (2)
0
Автор поста оценил этот комментарий

Перемещение парента не особо затратно, как по мне. Лучше так, чем удалять-создавать объекты.

Вообще для таких дел Stack<T> используют. Почему его не применил?

раскрыть ветку (1)
0
Автор поста оценил этот комментарий

Не знал про эту структуру, я не так давно в C# =)
Спасибо.

Вы смотрите срез комментариев. Чтобы написать комментарий, перейдите к общему списку

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества