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
+176
View File
@@ -206,3 +206,179 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1000000001
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1000000002}
- component: {fileID: 1000000003}
m_Layer: 0
m_Name: GameLifetimeScope
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1000000002
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000001}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1000000003
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000001}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a1111111111111111111111111111111, type: 3}
m_Name:
m_EditorClassIdentifier:
<BootSettings>k__BackingField: {fileID: 11400000, guid: c1111111111111111111111111111111, type: 2}
<SplashView>k__BackingField: {fileID: 1000000103}
<LoadingView>k__BackingField: {fileID: 1000000203}
<MenuView>k__BackingField: {fileID: 1000000303}
--- !u!1 &1000000101
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1000000102}
- component: {fileID: 1000000103}
m_Layer: 0
m_Name: SplashView
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &1000000102
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000101}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1000000103
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000101}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b1111111111111111111111111111111, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1000000201
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1000000202}
- component: {fileID: 1000000203}
m_Layer: 0
m_Name: LoadingView
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &1000000202
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000201}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1000000203
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000201}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b2222222222222222222222222222222, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1000000301
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1000000302}
- component: {fileID: 1000000303}
m_Layer: 0
m_Name: MenuView
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &1000000302
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000301}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1000000303
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1000000301}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b3333333333333333333333333333333, type: 3}
m_Name:
m_EditorClassIdentifier:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d1111111111111111111111111111111
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d2222222222222222222222222222222
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d3333333333333333333333333333333
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,43 @@
using QuizPleaseTest.Boot.Flow;
using QuizPleaseTest.Boot.Settings;
using QuizPleaseTest.Boot.States;
using QuizPleaseTest.Boot.UI;
using QuizPleaseTest.Common.Services;
using QuizPleaseTest.Common.StateMachine;
using UnityEngine;
using VContainer;
using VContainer.Unity;
namespace QuizPleaseTest.Boot.Composition
{
public class GameLifetimeScope : LifetimeScope
{
[field: SerializeField] public BootSettings BootSettings { get; private set; }
[field: SerializeField] public SplashUIView SplashView { get; private set; }
[field: SerializeField] public LoadingUIView LoadingView { get; private set; }
[field: SerializeField] public MenuUIView MenuView { get; private set; }
protected override void Configure(IContainerBuilder builder)
{
builder.RegisterInstance(BootSettings);
builder.RegisterComponent(SplashView);
builder.RegisterComponent(LoadingView);
builder.RegisterComponent(MenuView);
builder.Register<BootFlowService>(Lifetime.Singleton)
.As<IBootFlowService>()
.As<IService>();
builder.Register<BootStatesController>(Lifetime.Singleton)
.As<IStatesController<BootStateCode>>()
.AsSelf();
builder.Register<SplashState>(Lifetime.Singleton);
builder.Register<LoadState>(Lifetime.Singleton);
builder.Register<MenuState>(Lifetime.Singleton);
builder.RegisterEntryPoint<BootstrapEntryPoint>();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1111111111111111111111111111111
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d4444444444444444444444444444444
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,19 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using QuizPleaseTest.Common.Services;
namespace QuizPleaseTest.Boot.Flow
{
public class BootFlowService : Service, IBootFlowService
{
public override UniTask InitializeAsync(CancellationToken ct)
{
return UniTask.CompletedTask;
}
public override UniTask ReleaseAsync(CancellationToken ct)
{
return UniTask.CompletedTask;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f3333333333333333333333333333333
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,53 @@
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using VContainer.Unity;
namespace QuizPleaseTest.Boot.Flow
{
public class BootstrapEntryPoint : IStartable, IDisposable
{
private readonly IBootFlowService _bootFlowService;
private CancellationTokenSource _lifetimeCts;
public BootstrapEntryPoint(IBootFlowService bootFlowService)
{
_bootFlowService = bootFlowService;
}
public void Start()
{
_lifetimeCts = new CancellationTokenSource();
RunAsync(_lifetimeCts.Token).Forget();
}
public void Dispose()
{
if (_lifetimeCts == null)
{
return;
}
_lifetimeCts.Cancel();
_bootFlowService.ReleaseAsync(CancellationToken.None).Forget();
_lifetimeCts.Dispose();
_lifetimeCts = null;
}
private async UniTask RunAsync(CancellationToken ct)
{
try
{
await _bootFlowService.InitializeAsync(ct);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested)
{
}
catch (Exception exception)
{
Debug.LogException(exception);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f4444444444444444444444444444444
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
using QuizPleaseTest.Common.Services;
namespace QuizPleaseTest.Boot.Flow
{
public interface IBootFlowService : IService
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f2222222222222222222222222222222
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d5555555555555555555555555555555
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
using UnityEngine;
namespace QuizPleaseTest.Boot.Settings
{
[CreateAssetMenu(fileName = "BootSettings", menuName = "QuizPleaseTest/Boot Settings")]
public class BootSettings : ScriptableObject
{
[field: SerializeField] public float SplashDurationSeconds { get; private set; } = 1f;
[field: SerializeField] public int LoadSteps { get; private set; } = 5;
[field: SerializeField] public int LoadStepDurationMs { get; private set; } = 200;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b4444444444444444444444444444444
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d6666666666666666666666666666666
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
namespace QuizPleaseTest.Boot.States
{
public enum BootStateCode
{
Splash,
Load,
Menu
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5555555555555555555555555555555
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using QuizPleaseTest.Common.StateMachine;
namespace QuizPleaseTest.Boot.States
{
public class BootStatesController : StatesController<BootStateCode>
{
public BootStatesController(
SplashState splashState,
LoadState loadState,
MenuState menuState)
: base(CreateStates(splashState, loadState, menuState))
{
}
private static IReadOnlyDictionary<BootStateCode, IState> CreateStates(
SplashState splashState,
LoadState loadState,
MenuState menuState)
{
return new Dictionary<BootStateCode, IState>
{
{ BootStateCode.Splash, splashState },
{ BootStateCode.Load, loadState },
{ BootStateCode.Menu, menuState }
};
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6666666666666666666666666666666
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+29
View File
@@ -0,0 +1,29 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using QuizPleaseTest.Boot.UI;
using QuizPleaseTest.Common.StateMachine;
namespace QuizPleaseTest.Boot.States
{
public class LoadState : IState
{
private readonly LoadingUIView _view;
public LoadState(LoadingUIView view)
{
_view = view;
}
public UniTask EnterAsync(CancellationToken ct)
{
_view.Initialize();
return UniTask.CompletedTask;
}
public UniTask ExitAsync(CancellationToken ct)
{
_view.Release();
return UniTask.CompletedTask;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f8888888888888888888888888888888
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+29
View File
@@ -0,0 +1,29 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using QuizPleaseTest.Boot.UI;
using QuizPleaseTest.Common.StateMachine;
namespace QuizPleaseTest.Boot.States
{
public class MenuState : IState
{
private readonly MenuUIView _view;
public MenuState(MenuUIView view)
{
_view = view;
}
public UniTask EnterAsync(CancellationToken ct)
{
_view.Initialize();
return UniTask.CompletedTask;
}
public UniTask ExitAsync(CancellationToken ct)
{
_view.Release();
return UniTask.CompletedTask;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9999999999999999999999999999999
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+29
View File
@@ -0,0 +1,29 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using QuizPleaseTest.Boot.UI;
using QuizPleaseTest.Common.StateMachine;
namespace QuizPleaseTest.Boot.States
{
public class SplashState : IState
{
private readonly SplashUIView _view;
public SplashState(SplashUIView view)
{
_view = view;
}
public UniTask EnterAsync(CancellationToken ct)
{
_view.Initialize();
return UniTask.CompletedTask;
}
public UniTask ExitAsync(CancellationToken ct)
{
_view.Release();
return UniTask.CompletedTask;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7777777777777777777777777777777
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d7777777777777777777777777777777
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
using QuizPleaseTest.Common.UI;
namespace QuizPleaseTest.Boot.UI
{
public class LoadingUIView : UIView
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2222222222222222222222222222222
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
using QuizPleaseTest.Common.UI;
namespace QuizPleaseTest.Boot.UI
{
public class MenuUIView : UIView
{
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3333333333333333333333333333333
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
using QuizPleaseTest.Common.UI;
namespace QuizPleaseTest.Boot.UI
{
public class SplashUIView : UIView
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1111111111111111111111111111111
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d8888888888888888888888888888888
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+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:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c2222222222222222222222222222222
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+17
View File
@@ -0,0 +1,17 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b4444444444444444444444444444444, type: 3}
m_Name: BootSettings
m_EditorClassIdentifier:
<SplashDurationSeconds>k__BackingField: 1
<LoadSteps>k__BackingField: 5
<LoadStepDurationMs>k__BackingField: 200
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c1111111111111111111111111111111
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant: