# Задача: Unity / C# Middle ## Кратко Нужно реализовать загрузочный поток приложения в Unity через собственную state machine. Проект создается с нуля. Базовые абстракции, сервисы, View/ViewModel и state machine нужно написать самостоятельно, потому что это часть оценки. Ориентировочное время выполнения: около 1,5 часов. Если ушло больше или меньше времени, это нормально, но нужно честно указать это в документации. ## Технические требования - Unity 2022 LTS+. - C#. - Разрешено использовать VContainer, UniTask, UniRx/R3 или собственную реактивную реализацию. - Опционально можно использовать Odin Inspector и DOTween. ## Архитектурные требования ### Reactive Можно использовать UniRx/R3 или собственный `ReactiveValue`. Если используется собственная реализация, ожидаемая сигнатура подписки: ```csharp IDisposable Subscribe(Action cb, bool invokeImmediately = true) ``` Требования: - каждая подписка должна корректно диспозиться; - нельзя использовать голые `event Action` без отписки. ### Async Все async-операции должны быть реализованы только через UniTask и `CancellationToken`. Запрещено использовать `async void`, кроме Unity-колбэков. ### Dependency Injection Использовать VContainer. Все сервисы регистрируются как интерфейсы: ```csharp builder.Register(Lifetime.Singleton).As(); ``` Запрещено использовать: - `FindObjectOfType`; - `Singleton.Instance`; - `static`-хранилища состояния. ### UI Нужно сделать базовую пару View/ViewModel: ```csharp public class UIView : MonoBehaviour { public virtual void Initialize() { } public virtual void Release() { } } public class UIView : UIView where TVm : IUIViewModel { } ``` Требования: - ViewModel должна быть обычным C#-классом, не `MonoBehaviour`; - логика находится во ViewModel; - View хранит только Unity-ссылки и биндинги. ### Services Сервисы должны иметь асинхронный жизненный цикл: ```csharp UniTask InitializeAsync(CancellationToken ct) UniTask ReleaseAsync(CancellationToken ct) ``` Требования: - сервисы оформляются как `Service : IService`; - фоновые циклы стартуют в `InitializeAsync`; - фоновые циклы останавливаются через `CancellationTokenSource.Cancel()` в `ReleaseAsync`. ### Config Конфиги должны быть `ScriptableObject` с суффиксом `*Settings`. Регистрация в `LifetimeScope`: ```csharp [SerializeField] private SomeSettings _settings; protected override void Configure(IContainerBuilder builder) { builder.RegisterInstance(_settings); } ``` ## Задача: Boot Flow Реализовать загрузочный поток приложения через собственную state machine из трех состояний. ### 1. База state machine Нужно реализовать собственные абстракции: - `IState`; - `IStatesController`; - `StatesController`. В `StatesController` должен быть метод: ```csharp UniTask EnterStateAsync(TEnum code, CancellationToken ct) ``` Контракт перехода между состояниями: ```csharp await currentState.ExitAsync(ct); await newState.EnterAsync(ct); ``` Требования: - `CancellationToken` пробрасывается через всю цепочку вызовов; - `CancellationToken` должен реально отменять внутренние ожидания; - повторные входы в состояния не должны ломать UI и подписки. ### 2. Состояния Нужно реализовать три состояния. #### SplashState Поведение: - показывает лого; - ждет 1 секунду через `UniTask.Delay(..., ct)`; - переходит в следующее состояние. #### LoadState Поведение: - имитирует загрузку; - выполняет 5 шагов по 200 мс; - хранит `ReactiveValue Progress` со значением от `0` до `1`; - обновляет `Progress` после каждого шага. #### MenuState Поведение: - показывает `MenuUIView`; - содержит одну кнопку `Restart`; - по клику на `Restart` возвращает приложение в `LoadState`. ### 3. UI Нужно реализовать `LoadingUIView`. Требования: - `LoadingUIView` подписывается на `LoadState.Progress`; - прогресс-бар обновляется через DOTween или ручной Lerp; - при `ExitAsync` все подписки должны корректно очищаться; - при повторном входе в `LoadState` не должно быть `NullReferenceException` и дублирующихся подписок. ## Что сдать - GitHub-репозиторий или zip-архив без `Library/`, `Temp/`, `obj/`. - `README.md` с инструкцией запуска. - В `README.md` добавить 5-10 строк о том, что было бы доделано при наличии еще 2 часов. - `SELF_NOTES.md` с описанием собственных решений. - `AI_LOG.md` с описанием использования AI. Если AI не использовался, нужно написать почему. ## SELF_NOTES.md В `SELF_NOTES.md` нужно своими словами описать: - какие идеи были рассмотрены и почему выбрана текущая реализация; - какие места в коде были придуманы и написаны самостоятельно, без AI; - что в коде понятно до последней строки; - что осталось непонятным или воспринимается как магия; - ответы на 2-3 ключевых вопроса вида: почему здесь сделано именно так. ## AI_LOG.md В `AI_LOG.md` нужно описать: - какие промпты использовались; - где AI ошибся; - что было переписано руками; - если AI не использовался, почему было принято такое решение. ## Что не требуется Не нужно тратить время на: - красивый арт; - сложные анимации; - звук; - мобильную сборку. Главное: архитектурные слои, стиль кода, корректная работа жизненного цикла и понимание собственных решений.