diff --git a/My project/Assets/FlowScope/Samples.meta b/My project/Assets/FlowScope/Samples.meta new file mode 100644 index 0000000..b45fd0b --- /dev/null +++ b/My project/Assets/FlowScope/Samples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c21892f24eee4c20b29f51afc7228321 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Configs.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/Configs.meta new file mode 100644 index 0000000..f79c84a --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Configs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 76085fdd1da44ab08b69395b17064b59 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Configs/player_start.json b/My project/Assets/FlowScope/Samples/MainMenuP0/Configs/player_start.json new file mode 100644 index 0000000..a65d073 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Configs/player_start.json @@ -0,0 +1,3 @@ +{ + "Gold": 0 +} diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Configs/player_start.json.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/Configs/player_start.json.meta new file mode 100644 index 0000000..45ea51d --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Configs/player_start.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bd391f0c499b42a3a264d0a9de2483f8 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs.meta new file mode 100644 index 0000000..57597fe --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c88fe653c15d473cac0029539a2ff355 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs/MainMenuPanel.prefab b/My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs/MainMenuPanel.prefab new file mode 100644 index 0000000..974f46a --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs/MainMenuPanel.prefab @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &100000 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: [] + m_Layer: 0 + m_Name: MainMenuPanel + m_TagString: Untagged + m_IsActive: 1 diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs/MainMenuPanel.prefab.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs/MainMenuPanel.prefab.meta new file mode 100644 index 0000000..645d056 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Prefabs/MainMenuPanel.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9841d62cd2d24cb48e23ac11adaaf5a9 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Scenes.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/Scenes.meta new file mode 100644 index 0000000..585d361 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 946526d21b06440688088de21f383782 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity b/My project/Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity new file mode 100644 index 0000000..065296e --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity @@ -0,0 +1,27 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 +--- !u!1 &100000 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: [] + m_Layer: 0 + m_Name: MainMenuP0 + m_TagString: Untagged + m_IsActive: 1 diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity.meta new file mode 100644 index 0000000..73b8a81 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Scenes/MainMenuP0.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 03630d34cf304668afe00ab221f3557e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts.meta new file mode 100644 index 0000000..a5d0e45 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 93879cc02e2644039382a2006c5434b1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/GameBootstrap.cs b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/GameBootstrap.cs new file mode 100644 index 0000000..7c130b7 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/GameBootstrap.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using FlowScope.Audio; +using FlowScope.Config; +using FlowScope.Flow; +using FlowScope.Resources; +using FlowScope.Save; +using FlowScope.UI; +using UnityEngine; +using ContainerType = FlowScope.Container.Container; + +namespace FlowScope.Samples.MainMenuP0 +{ + public sealed class GameBootstrap : MonoBehaviour + { + private const string SaveKey = "main-menu-player"; + + [SerializeField] private Canvas hudCanvas; + [SerializeField] private MainMenuPanel mainMenuPanelPrefab; + [SerializeField] private int defaultGold; + + private ContainerType _root; + private GameFlow _gameFlow; + private ISaveService _saveService; + private PlayerData _playerData; + private CancellationTokenSource _lifetime; + + private async void Awake() + { + _lifetime = new CancellationTokenSource(); + await StartupAsync(_lifetime.Token); + } + + public async Task StartupAsync(CancellationToken cancellationToken) + { + _root = new ContainerType(); + _gameFlow = new GameFlow(); + + var resources = new SampleResourceService(); + if (mainMenuPanelPrefab != null) + { + resources.Register("FlowScope/Samples/MainMenuP0/MainMenuPanel", mainMenuPanelPrefab.gameObject); + } + + _saveService = new SaveService( + new FileSaveStorage(), + new JsonSaveSerializer()); + _playerData = await _saveService.LoadAsync( + SaveKey, + new PlayerData { Gold = new R3.ReactiveProperty(defaultGold) }, + cancellationToken); + + var uiManager = new UIManager(resources); + uiManager.RegisterLayer("hud", ResolveHudCanvas(), 0, PanelStrategy.Cache); + + var audioService = gameObject.AddComponent().Initialize(resources); + _root.RegisterInstance(_root); + _root.RegisterInstance( + new JsonConfigProvider(Array.Empty())); + _root.RegisterInstance(resources); + _root.RegisterInstance(_saveService); + _root.RegisterInstance(audioService); + _root.RegisterInstance(uiManager); + _root.RegisterInstance(_playerData); + _root.RegisterFactory(_ => new MainMenuFeature(uiManager, _playerData)); + + await _gameFlow.StartupAsync(_root, cancellationToken); + } + + public async Task ShutdownAsync(CancellationToken cancellationToken) + { + if (_saveService != null && _playerData != null) + { + await _saveService.SaveAsync(SaveKey, _playerData, cancellationToken); + } + + if (_gameFlow != null && _gameFlow.State != GameFlowState.Disposed) + { + await _gameFlow.ShutdownAsync(cancellationToken); + } + } + + private async void OnApplicationQuit() + { + _lifetime?.Cancel(); + await ShutdownAsync(CancellationToken.None); + } + + private void OnDestroy() + { + _lifetime?.Cancel(); + _lifetime?.Dispose(); + } + + private Canvas ResolveHudCanvas() + { + if (hudCanvas != null) + { + return hudCanvas; + } + + var canvasObject = new GameObject("FlowScope HUD Canvas"); + canvasObject.transform.SetParent(transform, false); + hudCanvas = canvasObject.AddComponent(); + hudCanvas.renderMode = RenderMode.ScreenSpaceOverlay; + return hudCanvas; + } + + private sealed class SampleResourceService : IResourceService + { + private readonly Dictionary _prefabs = new(); + + public void Register(string key, GameObject prefab) + { + _prefabs[key] = prefab; + } + + public Task> LoadAsync( + string key, + CancellationToken cancellationToken) + where T : class + { + cancellationToken.ThrowIfCancellationRequested(); + if (typeof(T) == typeof(GameObject) && + _prefabs.TryGetValue(key, out var prefab)) + { + return Task.FromResult>( + new SampleResourceHandle(key, prefab as T)); + } + + throw new InvalidOperationException($"Sample resource is not registered: {key}"); + } + + public IResourceGroup CreateGroup() + { + return new ResourceGroup(); + } + } + + private sealed class SampleResourceHandle : IResourceHandle where T : class + { + public SampleResourceHandle(string key, T asset) + { + Key = key; + Asset = asset; + } + + public string Key { get; } + public T Asset { get; private set; } + public bool IsDisposed { get; private set; } + + public void Dispose() + { + IsDisposed = true; + Asset = null; + } + } + } +} diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/GameBootstrap.cs.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/GameBootstrap.cs.meta new file mode 100644 index 0000000..8f944a7 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/GameBootstrap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd97432f2838454cbbf2d19dbe14d7f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuFeature.cs b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuFeature.cs new file mode 100644 index 0000000..207f714 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuFeature.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using FlowScope.Flow; +using FlowScope.UI; + +namespace FlowScope.Samples.MainMenuP0 +{ + public sealed class MainMenuFeature : FeatureBase + { + private readonly UIManager _uiManager; + private readonly PlayerData _playerData; + private MainMenuViewModel _viewModel; + + public MainMenuFeature(UIManager uiManager, PlayerData playerData) + { + _uiManager = uiManager; + _playerData = playerData; + } + + public override Task LoadAsync(FeatureContext context) + { + base.LoadAsync(context); + _viewModel = new MainMenuViewModel(_playerData); + return Task.CompletedTask; + } + + public override async Task EnterAsync(FeatureContext context) + { + await _uiManager.OpenAsync( + _viewModel, + context.CancellationToken); + } + + public override Task ExitAsync(FeatureContext context) + { + _uiManager.Close(); + return base.ExitAsync(context); + } + + public override void Dispose() + { + _viewModel = null; + base.Dispose(); + } + } +} diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuFeature.cs.meta b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuFeature.cs.meta new file mode 100644 index 0000000..e7d609e --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuFeature.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b015663928874766b56036f39f0aca6d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuPanel.cs b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuPanel.cs new file mode 100644 index 0000000..629d3a5 --- /dev/null +++ b/My project/Assets/FlowScope/Samples/MainMenuP0/Scripts/MainMenuPanel.cs @@ -0,0 +1,62 @@ +using FlowScope.UI; +using UnityEngine; +using UnityEngine.UI; + +namespace FlowScope.Samples.MainMenuP0 +{ + [UIPanel("hud", "FlowScope/Samples/MainMenuP0/MainMenuPanel", PanelStrategy.Cache)] + public sealed class MainMenuPanel : UIPanelBase + { + [SerializeField] private Text goldText; + [SerializeField] private Button incrementButton; + + public void Configure(Text goldLabel, Button incrementGoldButton) + { + goldText = goldLabel; + incrementButton = incrementGoldButton; + } + + protected override void OnBind(MainMenuViewModel viewModel) + { + if (goldText == null) + { + goldText = GetComponentInChildren(true); + } + + if (incrementButton == null) + { + incrementButton = GetComponentInChildren