Тестовое задание
Привет! Спасибо, что нашёл время. Ниже — короткое тестовое на ~1,5 часа.
Внутри — 2 варианта, выбери один. Они равнозначны по сложности; выбирай тот, что ближе по душе. Делать нужно с нуля: создаёшь Unity-проект сам, базовые абстракции пишешь сам (это часть оценки).
Результат надо отправить до завтра 19:00 по Мск в Telegram @mattnastya.
Это домашний формат, поэтому не требуем идеальной точности по времени и жестких доказательств потраченного времени.
Если ты потратил больше/меньше 1,5 часов — это нормально, просто укажи это честно.
Для нас важнее прозрачность, чем попытка сделать идеально любой ценой.
Желаю удачи!
Технические требования (общие, обязательны)
- Unity 2022 LTS+, C#.
- Разрешено подключать: VContainer, UniTask, UniRx / R3 или своя реактивщина — на твой выбор. Опционально Odin Inspector, DOTween.
ReactiveValue<T> с сигнатурой IDisposable Subscribe(Action<T> cb, bool invokeImmediately = true). Главное: каждая подписка должна корректно диспозиться, никаких голых event Action без отписки.
CancellationToken. async void запрещён везде, кроме Unity-колбэков.
builder.Register<Impl>(Lifetime.Singleton).As<IInterface>();
FindObjectOfType, Singleton.Instance, static хранилищ состояния.
UIView : MonoBehaviour { Initialize(); Release(); } + UIView<TVm> : UIView where TVm : IUIViewModel. VM — обычный C#-класс, не MonoBehaviour. Логика — в VM, View держит только Unity-ссылки и биндинги.
Service : IService с асинхронным жизненным циклом:
UniTask InitializeAsync(CancellationToken)
UniTask ReleaseAsync(CancellationToken)
InitializeAsync и гасятся через CancellationTokenSource.Cancel() в ReleaseAsync.
ScriptableObject с суффиксом *Settings, регистрируются в LifetimeScope через [SerializeField] + builder.RegisterInstance(_settings).
Что сдать
-
GitHub-репозиторий (или zip без
Library/,Temp/,obj/). -
README.md: как запустить + 5–10 строк «что бы я доделал, будь у меня ещё 2 часа». -
SELF_NOTES.md— обязательный документ про твои собственные решения. Напиши своими словами: — Какие идеи ты рассмотрел и почему выбрал именно эту реализацию (а не альтернативы).
— Какие места в коде ты придумал и написал сам, без AI, — и почему именно их.
— Что в коде ты понимаешь до последней строки, а что осталось «магией» (это нормально, но честно об этом скажи).
— Если бы кто-то завтра пришёл и спросил «почему здесь именно так?» — твой ответ на 2–3 ключевых решения. -
AI_LOG.md— какие промпты использовал, где AI ошибся, что переписал руками. Если AI не использовал — напиши, почему.
Чего мы НЕ ждём
Красивого арта, анимаций, звука, мобильной сборки. Главное — слои, стиль и твоё понимание собственного кода.
Выбери вариант
Вариант A — «Boot Flow» state machine из 3 стейтов
Сделай загрузочный поток приложения через свою стейт-машину.
-
1Базы стейт-машиныСвои
IState,IStatesController<TEnum>,StatesController<TEnum>с методомUniTask EnterStateAsync(TEnum code, CancellationToken ct). Контракт: сначалаawait currentState.ExitAsync(ct), потомawait newState.EnterAsync(ct).CancellationTokenпробрасывается насквозь и реально отменяет внутренние ожидания. -
2Три стейтаSplashState — показывает лого, ждёт 1 с через
UniTask.Delay(..., ct), переходит дальше.LoadState — имитирует загрузку (5 шагов по 200 мс), на стейте лежитReactiveValue<float> Progress(0..1), который обновляется после каждого шага.MenuState — показываетMenuUIViewс одной кнопкой «Restart», по клику возвращает вLoadState. -
3UI
LoadingUIViewподписан наLoadState.Progressи двигает прогресс-бар (DOTween или ручной Lerp — без разницы). ПриExitAsyncвсе подписки чисто диспозятся, никаких NRE при повторном входе.
Вариант B — «Energy & Regen» реактивный сервис + UI
Сделай систему энергии для мобильной игры.
-
1
EnergySettings(ScriptableObject)MaxEnergy(int),RegenSeconds(float, сек на 1 единицу). Создать ассет и заинжектить вLifetimeScope. -
2
IEnergyService/EnergyService : ServiceПоля:IReadOnlyReactiveValue<int> Current,IReadOnlyReactiveValue<float> SecondsToNext(доля до следующей единицы, 0..1). Методы:bool TrySpend(int amount). Регенерация — фоновая UniTask-петля внутри сервиса, стартует вInitializeAsync, корректно гасится вReleaseAsyncчерез свойCancellationTokenSource.ЕслиCurrent == Max, петля «спит» эффективно (не крутитDelay(0)в while). -
3UI
EnergyBarUIView<EnergyBarUIViewModel>показывает: цифруcurrent / max, прогресс-барImage.fillAmount = SecondsToNext, кнопку «Потратить 10». Биндинги — черезReactiveValue.Subscribe(...), отписки — вRelease().Запрещено обновлять UI черезUpdate().