226 lines
7.9 KiB
C#
226 lines
7.9 KiB
C#
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 UnityEngine.UI;
|
|
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 UIPreloadService _preloadService;
|
|
private SampleResourceBackend _sampleBackend;
|
|
private PlayerData _playerData;
|
|
private CancellationTokenSource _lifetime;
|
|
private bool _started;
|
|
private bool _shutdown;
|
|
|
|
public int ReleasedResourceCount => _sampleBackend?.ReleaseCount ?? 0;
|
|
public bool IsShutdown => _shutdown;
|
|
|
|
private async void Awake()
|
|
{
|
|
_lifetime = new CancellationTokenSource();
|
|
await StartupAsync(_lifetime.Token);
|
|
}
|
|
|
|
public async Task StartupAsync(CancellationToken cancellationToken)
|
|
{
|
|
if (_started)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_started = true;
|
|
_shutdown = false;
|
|
_root = new ContainerType();
|
|
_gameFlow = new GameFlow();
|
|
|
|
_sampleBackend = new SampleResourceBackend();
|
|
_sampleBackend.Register(
|
|
"FlowScope/Samples/MainMenuP0/MainMenuPanel",
|
|
ResolveMainMenuPanelPrefab().gameObject);
|
|
var resources = new ResourceService(_sampleBackend);
|
|
_preloadService = new UIPreloadService(resources);
|
|
var uiManager = new UIManager(resources, _preloadService);
|
|
uiManager.RegisterLayer("hud", ResolveHudCanvas(), 0, PanelStrategy.Destroy);
|
|
var screenNavigator = new UIScreenNavigator(uiManager);
|
|
|
|
var migrations = new SaveMigrationRegistry();
|
|
migrations.Register(new PlayerDataV1ToV2Migration());
|
|
_saveService = new SaveService(
|
|
new FileSaveStorage(),
|
|
new JsonSaveSerializer(2, migrations));
|
|
_playerData = await _saveService.LoadAsync(
|
|
SaveKey,
|
|
new PlayerData { Gold = new R3.ReactiveProperty<int>(defaultGold) },
|
|
cancellationToken);
|
|
|
|
await _preloadService.PreloadAsync<MainMenuPanel>(cancellationToken);
|
|
|
|
var audioService = gameObject.AddComponent<AudioService>().Initialize(resources);
|
|
_root.RegisterInstance(_root);
|
|
_root.RegisterInstance<IConfigProvider>(
|
|
new JsonConfigProvider(new[]
|
|
{
|
|
JsonConfigProvider.Mapping<SampleMenuConfig>(
|
|
"main_menu.json",
|
|
new InMemoryConfigSource(
|
|
new Dictionary<string, string>
|
|
{
|
|
{ "main_menu.json", "[{\"Id\":1,\"Title\":\"Main Menu P1\"}]" }
|
|
},
|
|
"MainMenuP0"),
|
|
new JsonConfigParser())
|
|
}));
|
|
_root.RegisterInstance<IResourceService>(resources);
|
|
_root.RegisterInstance<ISaveService>(_saveService);
|
|
_root.RegisterInstance<IAudioService>(audioService);
|
|
_root.RegisterInstance(uiManager);
|
|
_root.RegisterInstance<IUIPreloadService>(_preloadService);
|
|
_root.RegisterInstance<IUIScreenNavigator>(screenNavigator);
|
|
_root.RegisterInstance(_playerData);
|
|
_root.RegisterFactory(_ => new MainMenuFeature(screenNavigator, _playerData));
|
|
|
|
await _gameFlow.StartupAsync<MainMenuFeature>(_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);
|
|
}
|
|
|
|
_preloadService?.ReleaseAll();
|
|
_shutdown = true;
|
|
}
|
|
|
|
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<Canvas>();
|
|
hudCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
return hudCanvas;
|
|
}
|
|
|
|
private MainMenuPanel ResolveMainMenuPanelPrefab()
|
|
{
|
|
if (mainMenuPanelPrefab != null)
|
|
{
|
|
return mainMenuPanelPrefab;
|
|
}
|
|
|
|
var panelObject = new GameObject("MainMenuPanel Template");
|
|
panelObject.transform.SetParent(transform, false);
|
|
panelObject.SetActive(false);
|
|
|
|
var panel = panelObject.AddComponent<MainMenuPanel>();
|
|
|
|
var goldObject = new GameObject("GoldText");
|
|
goldObject.transform.SetParent(panelObject.transform, false);
|
|
var goldText = goldObject.AddComponent<Text>();
|
|
goldText.text = defaultGold.ToString();
|
|
goldText.alignment = TextAnchor.MiddleCenter;
|
|
|
|
var buttonObject = new GameObject("IncrementGoldButton");
|
|
buttonObject.transform.SetParent(panelObject.transform, false);
|
|
buttonObject.AddComponent<Image>();
|
|
var button = buttonObject.AddComponent<Button>();
|
|
|
|
panel.Configure(goldText, button);
|
|
mainMenuPanelPrefab = panel;
|
|
return mainMenuPanelPrefab;
|
|
}
|
|
|
|
private sealed class SampleResourceBackend : IResourceBackend
|
|
{
|
|
private readonly Dictionary<string, GameObject> _prefabs = new();
|
|
|
|
public string Name => "MainMenuP0Sample";
|
|
public int ReleaseCount { get; private set; }
|
|
|
|
public void Register(string key, GameObject prefab)
|
|
{
|
|
_prefabs[key] = prefab;
|
|
}
|
|
|
|
public Task<T> LoadAsync<T>(
|
|
string key,
|
|
CancellationToken cancellationToken)
|
|
where T : class
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
if (typeof(T) == typeof(GameObject) &&
|
|
_prefabs.TryGetValue(key, out var prefab))
|
|
{
|
|
return Task.FromResult(prefab as T);
|
|
}
|
|
|
|
throw new InvalidOperationException($"Sample resource is not registered: {key}");
|
|
}
|
|
|
|
public void Release<T>(string key, T asset)
|
|
where T : class
|
|
{
|
|
ReleaseCount++;
|
|
}
|
|
}
|
|
|
|
private sealed class SampleMenuConfig : IConfigRow
|
|
{
|
|
public int Id { get; set; }
|
|
public string Title { get; set; }
|
|
}
|
|
|
|
private sealed class PlayerDataV1ToV2Migration : ISaveMigration
|
|
{
|
|
public string Key => typeof(PlayerData).FullName;
|
|
public int FromVersion => 1;
|
|
public int ToVersion => 2;
|
|
|
|
public string Migrate(string json) => json;
|
|
}
|
|
}
|
|
}
|