using System.Threading.Tasks; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; namespace Bootstrap { /// /// Entrypoint for the Custom Boot initialisation /// public static class CustomBoot { /// /// Current initialisation status /// public static bool Initialised { get; private set; } /// // Called as soon as the game begins /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)] private static void Initialise() { //We should always clean up after Addressables, so let's take care of that immediately Application.quitting += ApplicationOnUnloading; PerformInitialisation(); } /// /// Initialise the bootstrapper /// public static void PerformInitialisation() { //In editor, perform initialisation synchronously if (Application.isEditor) { InitialiseBootSettingsSync(); } else { //In builds, just run things asynchronously, since we can add any checks we need early on _ = InitialiseBootSettings(); } } /// /// Called as the game is quitting, allowing for cleanup /// private static void ApplicationOnUnloading() { Application.quitting -= ApplicationOnUnloading; PerformDeInitialisation(); } /// /// De-Initialise the bootstrapper /// public static void PerformDeInitialisation() { Cleanup(runtimeBootSettingsHandle); Cleanup(editorBootSettingsHandle); Initialised = false; } /// /// Initialise the boot settings asynchronously /// private static async Task InitialiseBootSettings() { await LoadCustomBootSettings(); Initialised = true; } /// /// Initialise the boot settings synchronously /// private static void InitialiseBootSettingsSync() { LoadCustomBootSettingsSync(); Initialised = true; } /// /// Clean up the boot settings /// /// private static void Cleanup(AsyncOperationHandle handle) { if (handle.IsValid()) { handle.Result.Cleanup(); Addressables.Release(handle); } } /// /// Async handle for the runtime custom boot settings scriptable object /// private static AsyncOperationHandle runtimeBootSettingsHandle; /// /// Async handle for the editor custom boot settings object /// private static AsyncOperationHandle editorBootSettingsHandle; /// /// Runtime addressable key /// private static string RuntimeAsset = $"{nameof(CustomBootSettings)}_Runtime"; /// /// Editor addressable key /// private static string EditorAsset = $"{nameof(CustomBootSettings)}_Editor"; /// /// Load the custom boot settings asynchronously and run the initialisation method /// private static async Task LoadCustomBootSettings() { if (Application.isEditor) { editorBootSettingsHandle = await InitialiseBootSettingsAsset(EditorAsset); } runtimeBootSettingsHandle = await InitialiseBootSettingsAsset(RuntimeAsset); } /// /// Load the custom boot settings synchronously and run the initialisation method /// private static void LoadCustomBootSettingsSync() { if (Application.isEditor) { editorBootSettingsHandle = InitialiseBootSettingsAssetSync(EditorAsset); } runtimeBootSettingsHandle = InitialiseBootSettingsAssetSync(RuntimeAsset); } /// /// Initialise the boot settings asset with the given key /// /// /// private static async Task> InitialiseBootSettingsAsset(string key) { var handle = Addressables.LoadAssetAsync(key); await handle.Task; switch (handle.Status) { case AsyncOperationStatus.Failed: Debug.LogError(handle.OperationException); break; case AsyncOperationStatus.Succeeded: await handle.Result.Initialise(); break; } return handle; } /// /// Initialise the boot settings asset with the given key synchronously /// /// /// private static AsyncOperationHandle InitialiseBootSettingsAssetSync(string key) { var handle = Addressables.LoadAssetAsync(key); var result = handle.WaitForCompletion(); result.InitialiseSync(); return handle; } } }