feat: add bootstrap architecture and common utilities for Unity project

- Add GameLifetimeScope for dependency injection with Zenject
- Implement boot flow service with entry point and interfaces
- Create boot state machine (Splash, Menu, Load states)
- Add UI views for boot screens
- Add common services base class and interface
- Implement generic state machine controller
- Add base UI view components and ViewModel interface
- Update SampleScene.unity
- Add BootSettings asset

Добавлена архитектура bootstrap и общие утилиты для Unity проекта:
- Добавлен GameLifetimeScope для внедрения зависимостей (Zenject)
- Реализован сервис потока загрузки с точкой входа и интерфейсами
- Создана машина состояний загрузки (Splash, Menu, Load состояния)
- Добавлены UI представления для экранов загрузки
- Добавлены базовые классы сервисов и интерфейс IService
- Реализован контроллер машины состояний
- Добавлены базовые компоненты UI вида и интерфейс ViewModel
- Обновлена сцена SampleScene.unity
- Добавлен ассет BootSettings
This commit is contained in:
2026-05-27 03:56:38 +07:00
parent 6c46b3043a
commit 4435a2c6b6
57 changed files with 937 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d9999999999999999999999999999999
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
using System.Threading;
using Cysharp.Threading.Tasks;
namespace QuizPleaseTest.Common.Services
{
public interface IService
{
UniTask InitializeAsync(CancellationToken ct);
UniTask ReleaseAsync(CancellationToken ct);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3333333333333333333333333333333
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+18
View File
@@ -0,0 +1,18 @@
using System.Threading;
using Cysharp.Threading.Tasks;
namespace QuizPleaseTest.Common.Services
{
public class Service : IService
{
public virtual UniTask InitializeAsync(CancellationToken ct)
{
return UniTask.CompletedTask;
}
public virtual UniTask ReleaseAsync(CancellationToken ct)
{
return UniTask.CompletedTask;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e4444444444444444444444444444444
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e1111111111111111111111111111111
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
using System.Threading;
using Cysharp.Threading.Tasks;
namespace QuizPleaseTest.Common.StateMachine
{
public interface IState
{
UniTask EnterAsync(CancellationToken ct);
UniTask ExitAsync(CancellationToken ct);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5555555555555555555555555555555
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
using System.Threading;
using Cysharp.Threading.Tasks;
namespace QuizPleaseTest.Common.StateMachine
{
public interface IStatesController<TEnum>
{
UniTask EnterStateAsync(TEnum code, CancellationToken ct);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e6666666666666666666666666666666
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
namespace QuizPleaseTest.Common.StateMachine
{
public class StatesController<TEnum> : IStatesController<TEnum>
{
private readonly IReadOnlyDictionary<TEnum, IState> _states;
private IState _currentState;
public StatesController(IReadOnlyDictionary<TEnum, IState> states)
{
_states = states;
}
public async UniTask EnterStateAsync(TEnum code, CancellationToken ct)
{
if (!_states.TryGetValue(code, out IState newState))
{
throw new InvalidOperationException($"State is not registered: {code}");
}
if (_currentState != null)
{
await _currentState.ExitAsync(ct);
}
_currentState = newState;
await _currentState.EnterAsync(ct);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e7777777777777777777777777777777
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e2222222222222222222222222222222
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+6
View File
@@ -0,0 +1,6 @@
namespace QuizPleaseTest.Common.UI
{
public interface IUIViewModel
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e8888888888888888888888888888888
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
namespace QuizPleaseTest.Common.UI
{
public class UIView<TVm> : UIView where TVm : IUIViewModel
{
public TVm ViewModel { get; private set; }
public virtual void Bind(TVm viewModel)
{
ViewModel = viewModel;
}
public override void Release()
{
base.Release();
ViewModel = default;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f1111111111111111111111111111111
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+17
View File
@@ -0,0 +1,17 @@
using UnityEngine;
namespace QuizPleaseTest.Common.UI
{
public class UIView : MonoBehaviour
{
public virtual void Initialize()
{
gameObject.SetActive(true);
}
public virtual void Release()
{
gameObject.SetActive(false);
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e9999999999999999999999999999999
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: