Danro-Jump/GUIDES/applied_usage/Zenject_UniTask_Init.md
Danil Iarosh 839a68aef1 Improve VFX, UI overlays, session statistics, COM reliability, audio mixer, and expand architecture guides
Подробный список изменений:
- Перенесены эффекты частиц из C#-кода в готовые префабы-ассеты (Vfx Prefabs) в папке Assets/Resources/Vfx/ с автоматической генерацией материалов в Editor.
- Исправлены ошибки duration на играющих системах частиц и розовые текстуры в GameVfxService.cs.
- Исправлена навигация и фокус кнопок на оверлеях ContinueGameOverlay и PrizeResultOverlay с автоматическим подсвечиванием кнопок лимонно-зеленым цветом при фокусе и проигрыванием звука.
- Добавлена проверка настроек в GameplayHudController.cs для скрытия ContinueGameOverlay, если продолжение игры отключено.
- Расширен сбор статистики: создана структура GameplaySessionRecord, добавлена история до 50 раундов в PrizeSessionStateService и JsonPrizeSessionStateRepository, выведен расчет средних показателей и список 5 последних раундов в сервисной статистике.
- Повышена надежность COM-транспорта: добавлен sleep (80мс) в Dispose для graceful shutdown потоков, реализован цикл переподключения до 10 попыток при обрыве связи.
- Интегрированы настройки громкости с микшером: изменен шаг изменения громкости на 5% (0.05) для удобства, добавлен статус-чек и логи в ApplyServiceVolumes, расширены юнит-тесты на GetFloat параметры.
- Созданы новые архитектурные гайды по аппаратному COM-транспорту и призовой экономике автомата в папке GUIDES/architecture/.
- Оптимизирован и обновлен RAG-индекс проекта.
2026-07-03 15:04:08 +03:00

5.9 KiB
Raw Blame History

Комбинированный пример: Zenject + UniTask + Addressables для старта игры

В современных проектах старт игры — это не просто SceneManager.LoadScene(). Это цепочка асинхронных операций: проверка обновлений, загрузка баз данных (баланс, цены), авторизация, инициализация железа (как в Danro Jump) и только затем показ меню.

Этот гайд показывает, как объединить 3 главные библиотеки проекта в одну элегантную систему загрузки.

1. Интерфейс Загрузочной Задачи (Task)

Для начала, сделаем систему расширяемой. Вместо жестко закодированных вызовов, создадим интерфейс IStartupTask.

using Cysharp.Threading.Tasks;

public interface IStartupTask
{
    // Вес задачи для прогресс-бара (например, 10%)
    float ProgressWeight { get; }
    
    // Выполнение задачи с возможностью отмены
    UniTask ExecuteAsync(System.Threading.CancellationToken ct);
}

2. Реализация задач (Примеры)

Задача 1: Загрузка конфигураций (Addressables)

using UnityEngine.AddressableAssets;

public class LoadConfigTask : IStartupTask
{
    public float ProgressWeight => 30f;
    private readonly ConfigManager configManager;

    // Внедряем зависимость!
    public LoadConfigTask(ConfigManager configManager) => this.configManager = configManager;

    public async UniTask ExecuteAsync(System.Threading.CancellationToken ct)
    {
        // Загружаем глобальные настройки из Addressables
        var config = await Addressables.LoadAssetAsync<GameConfig>("GlobalConfig")
            .WithCancellation(ct); // Привязываем токен отмены
        
        configManager.Initialize(config);
    }
}

Задача 2: Подключение оборудования (COM-порт / UniTask)

public class HardwareInitTask : IStartupTask
{
    public float ProgressWeight => 40f;
    private readonly ComSystem comSystem;

    public HardwareInitTask(ComSystem comSystem) => this.comSystem = comSystem;

    public async UniTask ExecuteAsync(System.Threading.CancellationToken ct)
    {
        // Инициализация железа с таймаутом в 5 секунд!
        try 
        {
            await comSystem.InitializeHardwareAsync().Timeout(System.TimeSpan.FromSeconds(5));
        }
        catch (System.TimeoutException)
        {
            UnityEngine.Debug.LogError("Железо не ответило вовремя!");
            // Показать ошибку на экране
        }
    }
}

3. Регистрация задач в Zenject (Installer)

В BootstrapInstaller (привязанном к первой пустой сцене) мы регистрируем все наши задачи.

public class BootstrapInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        // Биндим сам менеджер загрузки
        Container.BindInterfacesAndSelfTo<AppBootstrapper>().AsSingle();

        // Биндим задачи как список. Zenject соберет их все в один List<IStartupTask>
        Container.Bind<IStartupTask>().To<LoadConfigTask>().AsCached();
        Container.Bind<IStartupTask>().To<HardwareInitTask>().AsCached();
    }
}

4. Исполнитель (AppBootstrapper)

Этот класс запустится автоматически благодаря интерфейсу IInitializable.

using System.Collections.Generic;
using Zenject;
using Cysharp.Threading.Tasks;

public class AppBootstrapper : IInitializable, System.IDisposable
{
    private readonly List<IStartupTask> tasks;
    private readonly ISceneFlowService sceneFlow;
    private System.Threading.CancellationTokenSource cts;

    // Zenject автоматически передаст все классы, которые мы привязали к IStartupTask
    public AppBootstrapper(List<IStartupTask> tasks, ISceneFlowService sceneFlow)
    {
        this.tasks = tasks;
        this.sceneFlow = sceneFlow;
    }

    public void Initialize()
    {
        cts = new System.Threading.CancellationTokenSource();
        RunStartupFlowAsync(cts.Token).Forget();
    }

    private async UniTaskVoid RunStartupFlowAsync(System.Threading.CancellationToken ct)
    {
        float totalProgress = 0f;

        foreach (var task in tasks)
        {
            // Выполняем задачу последовательно
            await task.ExecuteAsync(ct);
            
            // Здесь можно вызывать сигнал для обновления UI прогресс-бара
            totalProgress += task.ProgressWeight;
            UnityEngine.Debug.Log($"Progress: {totalProgress}%");
        }

        // 4. Когда все задачи выполнены — переходим в меню!
        sceneFlow.OpenMainMenu();
    }

    public void Dispose()
    {
        cts?.Cancel();
        cts?.Dispose();
    }
}

Итог: Мы получили масштабируемую, потокобезопасную (в рамках UniTask) и легко тестируемую систему инициализации. Чтобы добавить новую логику (например, проверку лицензии), нужно просто создать новый класс IStartupTask и забиндить его в инсталлере в 1 строчку кода! Никаких гигантских God-объектов.