11 KiB
Apple Hills Bootstrap System
A concise, code-first overview of the project’s bootstrap system. It covers how boot runs (runtime and editor), how to wait for boot to complete, how to register post-boot initialization, how to retrieve bootstrapped systems safely, and practical case studies.
Table of Contents
- What This Solves
- Architecture at a Glance
- Quick Start (Code-First)
- Editor Workflow (Project Settings + Edit-Mode Boot)
- Case Studies
- Events, APIs, and Keys
- Troubleshooting / FAQ
- Paths & Namespaces
- Change Log
What This Solves
- Centralizes startup of global systems via a data-driven list of prefabs.
- Guarantees an explicit moment when all bootstrapped systems are ready.
- Provides a robust way to run your own initialization code after boot, including priority ordering.
- Supports editor preview of the booted world without having to enter Play Mode.
Architecture at a Glance
- Runtime orchestrator:
CustomBoot(static)- Starts automatically via
RuntimeInitializeOnLoadMethod(BeforeSplashScreen). - Loads
CustomBootSettingswith Addressables and instantiatesBootPrefabsunder a runtime container (DontDestroyOnLoad). - Emits progress via
CustomBoot.OnBootProgressChangedand completion viaCustomBoot.OnBootCompleted. - Calls
BootCompletionService.HandleBootCompleted()when done to unlock post-boot actions.
- Starts automatically via
- Boot data:
CustomBootSettings(ScriptableObject)- Field:
GameObject[] BootPrefabs— prefabs to instantiate at boot (usually contain your singletons/services). - Methods:
Initialise()/InitialiseSync()instantiate prefabs and report progress;Cleanup()destroys them.
- Field:
- Post-boot coordination:
BootCompletionService(static)bool IsBootComplete— runtime flag.event Action OnBootComplete— notify late subscribers.void RegisterInitAction(Action action, int priority = 100, string name = null)— queue work to run after boot (priority: lower runs earlier). If boot has already completed, runs immediately.Task WaitForBootCompletionAsync()— await boot in async flows.
- Editor integration:
CustomBootEditorUtils,CustomBootSettingsProvider,CustomBootProjectSettings- Project settings UI to assign Addressable references for runtime/editor boot settings.
- Edit-mode boot toggle to preview booted state in the Scene view.
Quick Start (Code-First)
Check/Wire Boot Completion
using Bootstrap;
using UnityEngine;
public class BootGateExample : MonoBehaviour
{
private void Awake()
{
if (BootCompletionService.IsBootComplete)
{
SafeStart();
}
else
{
BootCompletionService.OnBootComplete += SafeStart;
}
}
private void OnDestroy()
{
BootCompletionService.OnBootComplete -= SafeStart;
}
private void SafeStart()
{
// Your code here will only run once all BootPrefabs have been instantiated.
Debug.Log("All systems ready – starting feature!");
}
}
Register Post-Boot Initialization
Register work to run once all bootstrapped systems are ready. Two equivalent approaches are supported:
- Inline lambda (great for small one‑offs)
using Bootstrap;
// Lower priority runs earlier; name is used in logs.
BootCompletionService.RegisterInitAction(
action: () => AnalyticsService.Instance.StartSession(),
priority: 50,
name: "Start Analytics Session"
);
// Can also skip priority and name:
BootCompletionService.RegisterInitAction(
action: () => AnalyticsService.Instance.StartSession()
);
- Full function callback (method group)
using Bootstrap;
using UnityEngine;
public class AnalyticsBootstrap : MonoBehaviour
{
private void Awake()
{
// Pass a method group (no lambda needed). Helpful for reuse and testing.
BootCompletionService.RegisterInitAction(
action: InitAnalytics,
priority: 50,
name: "Init Analytics"
);
// Can also skip priority and name:
BootCompletionService.RegisterInitAction(
action: InitAnalytics
);
}
private void InitAnalytics()
{
AnalyticsService.Instance.StartSession();
// You can call other setup methods here as needed
}
}
Notes:
- Only the callback parameter is required. You can omit the priority and name parameters. If you don't have use for them, you probably should.
- If boot has already completed when you call
RegisterInitAction, your callback runs immediately. - Use
priorityto control ordering (lower numbers run earlier);namehelps with log readability.
Await Boot Asynchronously
using System.Threading.Tasks;
using Bootstrap;
public class AsyncBootConsumer
{
public async Task StartAfterBootAsync()
{
await BootCompletionService.WaitForBootCompletionAsync();
// Now safe to query singletons installed by boot prefabs
var inv = CardSystemManager.Instance; // example
}
}
Retrieve Bootstrapped Systems Safely
Boot usually instantiates service prefabs that expose global accessors (singletons or service locators). Always guard access behind boot completion.
using Bootstrap;
using UnityEngine;
public class UseServicesAfterBoot : MonoBehaviour
{
private void Start()
{
BootCompletionService.RegisterInitAction(() =>
{
// Example: get your services here
var pages = UI.Core.UIPageController.Instance; // created in a boot prefab or scene
var settings = AppleHills.Core.Settings.SettingsProvider.Instance;
Debug.Log("Services acquired and ready.");
}, priority: 100, name: "Acquire Services");
}
}
Editor Workflow (Project Settings + Edit-Mode Boot)
- Project Settings UI:
Project/Custom Boot(CustomBootSettingsProvider)- Assign Addressables references for
RuntimeSettingsandEditorSettingsinCustomBootProjectSettings. - Backed by assets
CustomBootSettings_RuntimeandCustomBootSettings_Editorthat listBootPrefabsto spawn.
- Assign Addressables references for
- Asset management helper:
CustomBootSettingsUtil- Creates missing assets and Addressables groups/entries automatically:
- Runtime asset path:
Assets/Data/Bootstrap/Runtime/CustomBootSettings_Runtime.asset - Editor asset path:
Assets/Data/Bootstrap/Editor/CustomBootSettings_Editor.asset - Project settings asset path:
ProjectSettings/CustomBoot.asset
- Runtime asset path:
- Addressables keys:
CustomBootSettings_Runtime,CustomBootSettings_Editor.
- Creates missing assets and Addressables groups/entries automatically:
Enable/Disable Edit-Mode Boot
- Menu toggle:
Bootstrap/Editor Initialise(CustomBootEditorUtils). - When enabled in Edit Mode (not Play), the system instantiates the
BootPrefabsinto the currently open scene so you can preview booted systems.
What Happens in Edit Mode (Caveats)
DontDestroyOnLoaddoes not apply in edit mode; booted objects are added to the current scene.- Entering Play Mode de-initializes any edit-mode boot and hands off to the runtime boot.
- Scene save is guarded: edit-mode boot de-initializes just before save to avoid polluting the scene, then re-initializes after.
- Changing the active scene in Edit Mode triggers de-init/re-init of edit-mode boot.
Case Studies
UI Page Controller post-boot wiring
UIPageController registers a post-boot hook:
// Inside UIPageController.Awake()
Bootstrap.BootCompletionService.RegisterInitAction(InitializePostBoot);
This guarantees input/transition wiring happens once systems from BootPrefabs are in place.
Card System post-boot registration
To ensure the card data/UI only touches CardSystemManager after boot:
using Bootstrap;
using AppleHills.Data.CardSystem;
BootCompletionService.RegisterInitAction(() =>
{
var mgr = CardSystemManager.Instance;
// subscribe to events, warm up caches, etc.
}, priority: 90, name: "CardSystem Init");
Settings access after boot
If your settings provider lives in a boot prefab, wait for boot before first access:
using Bootstrap;
using AppleHills.Core.Settings;
BootCompletionService.RegisterInitAction(() =>
{
var gameplay = SettingsProvider.Instance.GetSettings<InteractionSettings>();
Debug.Log($"DefaultPromptRange: {gameplay.DefaultPuzzlePromptRange}");
}, name: "Load Interaction Settings");
Events, APIs, and Keys
CustomBoot(static)bool Initialisedfloat CurrentProgressevent Action<float> OnBootProgressChangedevent Action OnBootCompleted
BootCompletionService(static)bool IsBootCompleteevent Action OnBootCompletevoid RegisterInitAction(Action action, int priority = 100, string name = null)Task WaitForBootCompletionAsync()
- Addressables keys
CustomBootSettings_RuntimeCustomBootSettings_Editor
Troubleshooting / FAQ
- My code runs before services exist:
- Wrap it in
BootCompletionService.RegisterInitAction(...)or checkIsBootCompleteand subscribe toOnBootComplete.
- Wrap it in
- Boot never seems to complete in Edit Mode:
- Edit-mode boot is a preview; verify the menu toggle
Bootstrap/Editor Initialiseis enabled and that yourCustomBootSettings_Editor.assetlists the necessaryBootPrefabs.
- Edit-mode boot is a preview; verify the menu toggle
- I get duplicate singletons after entering Play Mode:
- Edit-mode boot de-inits on entering Play. If you manually created objects, ensure you destroy them; prefer boot prefabs.
- Addressables key not found:
- Open
Project/Custom Bootand ensure both runtime and editor settings assets exist and are assigned; let the utility create missing assets.
- Open
Paths & Namespaces
- Runtime scripts:
Assets/Scripts/Bootstrap/CustomBoot.cs,CustomBootSettings.cs,BootCompletionService.cs
- Editor scripts:
Assets/Editor/Bootstrap/CustomBootSettingsProvider.cs,CustomBootEditorUtils.cs,CustomBootProjectSettings.cs,CustomBootSettingsUtil.cs
- Addressables keys:
CustomBootSettings_Runtime,CustomBootSettings_Editor - Namespace:
Bootstrap(runtime),Editor.Bootstrap(editor)
Change Log
- v1.0: Initial overview document with TOC, code-first playbooks, editor workflow, case studies, and API summary.