Как посадить дерево
EVALLЯ взялся за переработку старой игры «Ферма», сохранив графику, музыку и геймплей. Исходный код был написан без использования современных подходов, что привело к множеству проблем: обилие синглтонов, хаотичная логика в методах Awake/Start/OnEnable и сопутствующие ошибки и гонки инициализаций.

Главная цель в разработке — избавиться от этих проблем и получить новый опыт в разработке. Я хочу создать чистый, масштабируемый и легко поддерживаемый код, используя современные инструменты и архитектурные паттерны.
Предыстория:
На первый взгляд, задача казалась простой: переноси игру по частям в новый проект вот и все. Я решил начать с самого "главного" - создания карты. В старой игре статическая часть была выполнена из GameObject'от с компонентом SpriteRenderer и батчилась нормально, вот интерактивные элементы карты - деревья и камни, не батчились! Причина была в том, что они анимированы Animator'ом и состояли из 3х спрайтов, каждый !
Шаг первый - создание минимальной архитектуры
Импортировал в проект UniTask, так как мне не нравятся корутины. R3 - так как буду его изучать в этом проекте. Фраемворк Reflex - в качестве DI . Создал точку входа в приложение, загрузчик сцен, отдельный, переиспользуемый компонент экрана загрузки, которому можно передавать шаги (Func<UniTask> Task, string Description), все настроил и забиндил в контейнер Reflex’a.
Шаг второй - создание статической части карты
В Unity я уже несколько лет, но так вышло, что с 2D-проектами вообще не работал. Поэтому, когда дело дошло до карты, я решил не искать лёгких путей и сразу познакомиться с инструментом Tilemap.
Я решил, что эта часть карты не будет загружаться через Addressables, так что просто оставил её на сцене как обычный префаб. В плане оптимизации, конечно, есть куда расти. Из-за "кривых" спрайтов получилось довольно много лишних треугольников, да и всю карту можно отрисовывать за один проход, а не за четыре, как у меня сейчас. Но для начала, и это уже отличный результат!

Шаг третий - дополнительные сервисы
- Инициализатор сервисов
Я создал OrderedInitializerBase, который асинхронно запускает сервисы по заданным приоритетам из InitializationOrderSettings. Сервисы с одинаковым приоритетом стартуют параллельно благодаря UniTask.WhenAll, а те, что не указаны в конфиге, получают дефолтный порядок (999). Это решение интегрируется с DI-контейнером Reflex и работает с любым сервисом, реализующим IOrderedInitializable.
- Менеджер токенов
Реализовал CancellationTokenManager для управления асинхронными операциями. Он предоставляет токены для жизненных циклов приложения, сцен и игровых объектов, автоматически отменяя их при выходе или смене сцены. Это предотвращает утечки ресурсов и упрощает контроль задач.
- Система состояний
Представлена как GameStateProcessor и GameStateService для управления состояниями игры (загрузка, геймплей, инициализация). Каждое состояние (IGameState) поддерживает асинхронные переходы с токенами отмены, принимает параметры на вход, а изменения отслеживаются через реактивное свойство CurrentStateRx на базе R3.
- Логирование
Адаптировал логгер от Reflex, добавив уровни (Development, Info, Warning, Error) и цветное форматирование в консоли Unity. Это помогает быстро находить проблемы и отслеживать работу системы.

- Загрузка сцен
Переработал SceneLoader, чтобы он сначала загружал промежуточную пустую сцену (BootScene), а затем целевую. Он также автоматически обновляет настройки build settings через SceneLoadingConfig
- Структура проекта
Я разбил проект на сборки (Core, Gameplay, Common), чтобы поддерживать модульность и общий код выделить в Common.
Время Сажать Деревья!
Но, как оказалось, и здесь не всё так просто:
– Где именно их "сажать"?
– Как правильно загрузить спрайты деревьев?
– Как обойтись без использования аниматоров, но сохранить движение?
– Какой способ сортировки слоёв выбрать?
И это лишь малая часть вопросов, на которые у меня пока нет ответов.
P.S.: На все ушла почти неделя ! А дерево так и не выросло на моей карте 🥲Сажать деревья сложно 😁