Для исследования один из выбранных архитектурных подходов — это стандартный юнитевский Monobehaviour, вкратце расскажу про этот подход, система построена как конструктор, задача разработчика при создании игрового объекта, это наделять этот объект свойствами, просто подключая к нему нужные так называемые компоненты. Идеально! Бери и строй, что тебе нужно. Но, как всегда, есть нюанс, во‑первых, любой из этих объектов способен влиять на игровой цикл (вы буквально одной строкой кода можете выключить игру просто написав её в методе Update любого GameObjectа), и, во‑вторых, большое количество таких объектов приводит к потере производительности. Казалось бы, не делай плохо делай хорошо. Избегай «тонких» мест и всё. С этой мыслью я приступил к выполнению задач.
Второй архитектурный подход, который я выбрал для сравнения — это Zenject. Это фреймворк, реализующий принцип Dependency Injection, что в переводе означает «внедрение зависимостей». Звучит умно, но по сути — это про то, как аккуратно передавать нужные объекты в другие классы, не создавая их вручную внутри. Zenject помогает сделать проект более структурированным: вы заранее описываете, какие зависимости у каких компонентов есть, и фреймворк сам всё «связывает». Это как если бы все детали конструктора сами вставлялись туда, где они нужны — без ручной сборки. При помощи этого подхода можно избавиться от проблем Monobehaviour и в теории повысить производительность и скорость разработки. Но чтобы понять, как оно всё работает, нужно немного погрузиться. Концепция не самая простая, и на старте создаёт ощущение, что ты больше пишешь конфигурацию, чем саму игру.
Почему именно Zenject? Ведь на рынке большое количество различных фреймворков позволяющих использовать подход внедрения зависимостей. Всё достаточно просто, открыв 5ть вакансий Unity разработчик в требуемых или желательных навыках в 3 будет присутствовать умение работать с Zenject. Ну и название у него сложно произносимое =)
Прежде чем приступить к исследованию нужно было разработать одну полноценную игры два раза с разными архитектурными подходами. Для объективного исследования были взяты следующие метрики, время реализации сравнительной механики, проанализировано количество строчек кода для функционирования полной механики в игре, FPS‑ (количество кадров в секунду) во время работы самой механики, количество используемой оперативной памяти то «уборщик мусора» в памяти. Проще говоря: когда, ты создаёшь объекты они занимают место в оперативной памяти. Когда эти объекты больше не нужны, их нужно удалить, чтобы не засорять память. Вот этим и занимается Garbage Collector — он находит и очищает неиспользуемые объекты, освобождая место. После нужно было собрать и проанализировать полученные результаты при помощи представленных инструментов.
Для исследования в моей игре присутствует ряд систем различных механик, которые в достаточной мере помогут сравнить нужные показатели

Показатель |
MonoBehaviour |
Zenject |
Время реализации |
5–6 дней |
10–14 дней |
Объём кода |
100 строк |
200 строк |
Частота кадров (FPS) |
150–130 |
150–130 |
Использование памяти (Memory Usage) |
1600 МБ |
1700 МБ |
Число вызовов Garbage Collector |
~3,04 ms |
~2,90 ms |
Поддерживаемость |
Низкая |
Высокая |
Модульность |
Низкая |
Высокая |
Тестируемость |
Средняя |
Высокая |
Связанность (Coupling) |
Высокая |
Средняя |
Как видно из таблицы, реализация через MonoBehaviour заняла от 5 до 6 дней и потребовала около 100 строк кода. Реализация той же системы с использованием Zenject оказалась в два раза более трудоёмкой — от 10 до 14 дней и около 200 строк кода. Это объясняется необходимостью предварительной настройки связей между объектами и написанием дополнительных конфигурационных классов.
По производительности существенной разницы не наблюдается: FPS находится в пределах 130–150 кадров в секунду в обеих реализациях, но Zenject немного выигрывает по числу вызовов сборщика мусора — 2,90 миллисекунды против 3,04.
Однако архитектурные характеристики значительно различаются. В подходе MonoBehaviour отмечается низкий уровень поддерживаемости, модульности и высокая связанность между компонентами. Тогда как Zenject демонстрирует высокую модульность и тестируемость, а также более низкую связанность, что говорит о большей гибкости архитектуры.

Показатель |
MonoBehaviour |
Zenject |
Время реализации |
10–15 дней |
16–21 дней |
Объём кода |
205 строк |
305 строк |
Частота кадров (FPS) |
150–60 |
150–100 |
Использование памяти (Memory Usage) |
1620 МБ |
1750 МБ |
Число вызовов Garbage Collector |
~3,04 ms |
~2,80 ms |
Поддерживаемость |
Низкая |
Высокая |
Модульность |
Очень низкая |
Высокая |
Тестируемость |
Средняя |
Высокая |
Связанность (Coupling) |
Высокая |
Средняя |
На данной системе ситуация практически идентичная, но разрыв между временем реализации сокращается, также при тестировании производительности данной системы провалы в FPS уменьшились.

Показатель |
MonoBehaviour |
Zenject |
Время реализации |
13–17 дней |
18 -21 дней |
Объём кода |
270 строк |
380 строк |
Частота кадров (FPS) |
150–60 |
150–60 |
Использование памяти (Memory Usage) |
1620 МБ |
1750 МБ |
Число вызовов Garbage Collector |
~3,04 ms |
~2,90 ms |
Поддерживаемость |
Низкая |
Высокая |
Модульность |
Низкая |
Высокая |
Тестируемость |
Средняя |
Высокая |
Связанность (Coupling) |
Высокая |
Низкая |
Заключительная система — это постройка столов и формирование очереди из посетителей. Эта механика оказалась наиболее трудоёмкой: при использовании MonoBehaviour на её реализацию ушло 17 дней, а при использовании Zenject — 21 день. Разрыв между реализациями значительно сократился. По остальным параметрам ситуация без особых изменений
Также архитектурные различия становятся ещё более заметными. Zenject продолжает демонстрировать высокую модульность, тестируемость и низкую связанность компонентов, в отличие от классического подхода MonoBehaviour, где наблюдается высокая связанность и низкая поддерживаемость.
Для большей наглядности вашему вниманию представляю графики по каждому параметру.

По ним хорошо видно устойчивую ситуацию отработки сборщика мусора, также сравнивая время разработки и FPS по всем этим параметрам Zenject наглядно выигрывает у MonoBehaviour. Это подтверждает, что Zenject, несмотря на большие трудозатраты, позволяет создавать более устойчивую и масштабируемую архитектуру, особенно при работе со сложными системами.
После проведённого исследования осмелюсь сделать вывод, об этих двух подходах.
MonoBehaviour. Он даёт очень быстрый старт: никаких дополнительных настроек, просто создаёшь скрипт — и можно сразу писать логику. Всё работает «из коробки», редактор Unity отлично с этим справляется: всё на виду, все поля можно задать прямо через инспектор. Это особенно удобно, когда опыта немного.
Казалось бы — чего ещё нужно? Но со временем начали проявляться проблемы. Чем больше становился проект, тем больше ощущалась высокая связанность между объектами: меняешь поведение одного класса — и внезапно ломается всё остальное. Повторно использовать код сложно, ведь каждый скрипт часто напрямую зависит от других компонентов. Это приводит к тому, что даже простые изменения начинают отнимать слишком много времени.
Кроме того, без чёткой архитектуры логика начинает «расползаться» по проекту. Читать и понимать такой код становится тяжело, особенно если прошло немного времени и ты сам уже подзабыл, что там делал. Поэтому, несмотря на удобный старт, MonoBehaviour начинает сдерживать развитие проекта по мере его роста.
При работе с Zenject самое первое, что бросается в глаза — это порядок. Всё становится модульным: хочешь поменять поведение объекта — просто заменяешь нужный компонент, не трогая остальное. Такая чистая архитектура делает код не только более читаемым, но и легче поддерживаемым.
Ещё одно преимущество — тестируемость. Благодаря тому, что зависимости не создаются внутри классов, а внедряются снаружи, можно легко подставить заглушки или альтернативные реализации. Это особенно важно, если проект растёт и появляется необходимость в автоматизированных тестах.
Однако всё это не бесплатно. Zenject требует времени на погружение. Понять, как работают внедрение зависимостей, инверсии управления и как всё это связывается через контейнер — на старте не просто. Нельзя просто взять и «написать логику». Нужно сначала продумать, где что будет находиться, прописать установщики, фабрики и зарегистрировать все зависимости. Даже интерфейс приходится разделять на View и ViewModel, чтобы сохранить архитектурную чистоту.
Из‑за этого Zenject, конечно, проигрывает в скорости прототипирования. Когда нужно быстро что‑то накидать и попробовать — он тормозит процесс. Но если смотреть в долгосрочной перспективе, особенно на крупные проекты, он определённо даёт преимущество за счёт масштабируемости и удобства сопровождения кода.
В ходе исследования я пришёл к важному выводу: выбор архитектурного подхода напрямую зависит от двух ключевых факторов — масштаба проекта и опытности разработчика.
Если проект небольшой, и вы хотите просто собрать метрики с интересующей вас темы на тех же «Я играх», а команда — это один‑два человека с ограниченным опытом, то MonoBehaviour даёт все инструменты для того, чтобы просто взять и начать. Он интуитивно понятен, требует минимум подготовки и позволяет сконцентрироваться на самой игре, а не на архитектуре.
Но если вдруг сложится такая ситуация, что ваш прототип «выстрелил» и проекту нужно расти, появляется всё больше логики, взаимодействий, новых систем — MonoBehaviour быстро теряет управляемость. В таких случаях Zenject показывает свою силу. Он требует большего понимания принципов построения архитектуры, но зато позволяет не бояться, что проект развалится от одного неосторожного изменения.
От сюда следует, что поставленная гипотеза, полностью подтверждается, более сложная архитектура упрощает разработку
Хотя, если говорить прямо, то оптимального подхода «на все случаи» нет. MonoBehaviour отлично подходит для обучения, прототипов и небольших проектов. А Zenject — для случаев, когда важна структура, масштабируемость и долгосрочная поддержка.
Добавлю еще немного… не важно какой архитектурой вы пользуетесь, вы можете написать игру одним монолитом разделенный большим количеством условий, главное что вы это сделаете и после этого у вас появится опыт, когда приступите ко второй игре вы учтете предыдущий опыт и получите новые проблемы, но это будет уже другой опыт так и продолжайте, спасибо что дочитали до конца.
Комментарии (14)
hello_my_name_is_dany
23.06.2025 22:07DI-библиотеки просто решают проблему инъекции зависимостей, чтобы руками не прописывать, архитектура тут может быть любая, а про неё ни слова
Grooofy Автор
23.06.2025 22:07Тут соглашусь, но библиотека всё же немного требовательна к архитектуре на которой она написана. Может есть идеи какие архитектуры сравнить?
kostyakc
23.06.2025 22:07Zenject не единственное и не всегда лучшее решение. Мне нравится VContainer и по производительности, и по функционалу
eonyanov
23.06.2025 22:07Очень субъективно, ни слова про код, просто описываешь свои ощущения. Это как если бы ты сравнил строительство деревянного дома одним топором или набором инструментов. Понятное дело у топора функционал меньше и займет дольше. Но в итоге и там и там дом из брёвен.
nenuacho
23.06.2025 22:07Зенжект - древнее тормозное зло и карго культ
Частота кадров (FPS)
Замерять FPS после внедрения зенжекта странная мысль. Каким образом он должен повлиять на это?Число вызовов Garbage Collector
Их вообще не должно быть после старта, ни с зенжектом, ни с монобехами. Если они есть - лучше углубиться в практики написания производительного кода, а не в SOLID и прочую ересь
IQuant
23.06.2025 22:07От сюда следует, что поставленная гипотеза, полностью подтверждается, более сложная архитектура упрощает разработку
Если более сложная архитектура упрощает разработку, то, по идее, на разработку должно быть затрачено меньше времени, не так ли? Но по вашим данным получается как раз наоборот - более простая архитектура требует меньше времени, а следовательно - упрощает разработку.
Не ясно, почему у вас именно такой вывод.
Fen1kz
23.06.2025 22:07Как человек который буквально прошлую неделю мучительно выбирал между VContainer, Reflex или просто сделать всё на ГО, я негодую и ставлю статье минус.
Никакой конкретики, есть какие-то абстрактные метрики фиг пойми откуда. Ощущение что статью вообще писал ИИ, мусолится одна и та же мысль в разных обертках.
Кстати я выбрал всё-таки ГО, потому что обучающих материалов по ioc для юнити примерно нет, а то что находится - это вот такие статьи уровня "используйте ioc потому что модульность высокая". При этом на форумах только и слышно, что юнити уже реализует di через инспектор.
iamAlexKim
23.06.2025 22:07Имхо есть самый трушный обучающий материал - исходники игр на юнити. Слава богу разработчики инди шедевров предоставили их исходники и просто положили их в папку с игрой, и вертели они этот solid и di каргокульт на своём синглтоне ;)
А по поводу статьи - действительно, метрики ради метрик, без единой детали реализации. Сомневаюсь что автор использовал GO-less подход через DrawInstanced или вообще hybrid/pure ecs, т.к. там одного бойлерплейта для рендера было бы в раза 3 больше.
DrRen7
Интересно, но не понятно, сравнивать Zenject и MonoBehaviour, это как сравнивать мышку и windows) Как я понял вы написали игру просто на GO потом переписали с использованием SOLID и DI(Zenject)?
Grooofy Автор
Доброго времени суток, немного не так. Я всё писал на С#, в первом случаем писал всё только при помощи инструментов Unity и наследования от MonoBehaviour пытался придерживаться SOLID, ну а во втором DI. Можно подробней почему это бессмысленно сравнивать ?
DrRen7
Ну просто это не корректно, используя Zenject вы же не перестаете использовать MonoBehaviour? Всё равно надо где то поддерживать жизненный цикл unity плюс скорее всего вы создает объекты на них прикреплены скрипты с MonoBehaviour. А Zenject ну он просто регистрирует и выдает интерфейсы по запросу, и архитектурного подхода он не несет. Примеров не хватает) По мне все видится что GetComponent заменился на [Inject]