[Add] Agent Task
This commit is contained in:
+216
@@ -0,0 +1,216 @@
|
||||
# Задача: 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<T>`.
|
||||
|
||||
Если используется собственная реализация, ожидаемая сигнатура подписки:
|
||||
|
||||
```csharp
|
||||
IDisposable Subscribe(Action<T> cb, bool invokeImmediately = true)
|
||||
```
|
||||
|
||||
Требования:
|
||||
|
||||
- каждая подписка должна корректно диспозиться;
|
||||
- нельзя использовать голые `event Action` без отписки.
|
||||
|
||||
### Async
|
||||
|
||||
Все async-операции должны быть реализованы только через UniTask и `CancellationToken`.
|
||||
|
||||
Запрещено использовать `async void`, кроме Unity-колбэков.
|
||||
|
||||
### Dependency Injection
|
||||
|
||||
Использовать VContainer.
|
||||
|
||||
Все сервисы регистрируются как интерфейсы:
|
||||
|
||||
```csharp
|
||||
builder.Register<Impl>(Lifetime.Singleton).As<IInterface>();
|
||||
```
|
||||
|
||||
Запрещено использовать:
|
||||
|
||||
- `FindObjectOfType`;
|
||||
- `Singleton.Instance`;
|
||||
- `static`-хранилища состояния.
|
||||
|
||||
### UI
|
||||
|
||||
Нужно сделать базовую пару View/ViewModel:
|
||||
|
||||
```csharp
|
||||
public class UIView : MonoBehaviour
|
||||
{
|
||||
public virtual void Initialize() { }
|
||||
public virtual void Release() { }
|
||||
}
|
||||
|
||||
public class UIView<TVm> : 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<TEnum>`;
|
||||
- `StatesController<TEnum>`.
|
||||
|
||||
В `StatesController<TEnum>` должен быть метод:
|
||||
|
||||
```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<float> 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 не использовался, почему было принято такое решение.
|
||||
|
||||
## Что не требуется
|
||||
|
||||
Не нужно тратить время на:
|
||||
|
||||
- красивый арт;
|
||||
- сложные анимации;
|
||||
- звук;
|
||||
- мобильную сборку.
|
||||
|
||||
Главное: архитектурные слои, стиль кода, корректная работа жизненного цикла и понимание собственных решений.
|
||||
Reference in New Issue
Block a user