Boot and GameRoot

In short

GameBootstrap adds a [Game] object with a GameRoot after any scene loads. GameRoot.Awake creates every service in a fixed order and opens the main menu. Screens move between states only through GameRoot methods (GoToMainMenu, OpenMapSelect, EnterCustomize, EnterIndex, EnterLobby, SetState).

How it works

1. Bootstrap

GameBootstrap has two [RuntimeInitializeOnLoadMethod] hooks:

  • SubsystemRegistration: GameEvents.ClearAll(), so Play mode without a domain reload starts with no stale subscribers.
  • AfterSceneLoad: if no GameRoot exists, create new GameObject("[Game]").AddComponent<GameRoot>().

A hand-made scene that already holds a GameRoot is left alone. The build uses an empty scene (see Build and Release).

2. GameRoot.Awake, in order

  1. Singleton: a second GameRoot destroys itself. DontDestroyOnLoad.
  2. Content = ContentDatabase.LoadOrDefault(): Resources/ContentDatabase or DefaultContent.Build().
  3. FitOverrides.LoadAndApply(...): Fit Mode saves on top of content, before any batter is built (Fit Mode).
  4. Save = SaveSystem.Load() (Save System).
  5. UI.DisplaySettings.Apply(Save.settings): fullscreen, resolution, V-Sync, FPS cap.
  6. Meta = new MetaProgression(Save, Content); Leaderboard = new LocalLeaderboardService(Save); Mode = GameModes.All[0] (Solo).
  7. SetupCameraAndLight(): uses Camera.main or makes one (with an AudioListener), adds CameraRig, calls PostFx.Setup, removes the thumbnail layer from the culling mask, finds a directional light or makes a Sun with soft shadows (Rendering).
  8. Field: a Ballpark object with BallparkBuilder.Init(Sun, camera) (Ballpark Building).
  9. AtBat: AtBatController.Init(CameraRig) on the same object (At-Bat Loop).
  10. Add-on components on [Game], in this order: TimingIndicator, GreenFeedback, SwingLog (Swing Log), GameAudio, ScreenFade, ThumbnailRenderer, Intro = MatchIntroDirector and Outro = OutroDirector (Cutscenes; the outro adds its own Fireworks), ParkSnapshots (map-select pictures, UI System), FpsCounter.
  11. Net = NetRuntime; hooks SessionReplaced and JoinRequested, then HookSession() so a session’s RunStarting calls CoopRun.Start (Lobby and Session).
  12. Cage = BattingCage (Batting Cage).
  13. Runs = new RunManager(this).
  14. UIRoot (which adds DevMenu in the Editor and development builds).
  15. GoToMainMenu().

3. Navigation methods

MethodWhat it doesState
SetState(state)Sets State and raises GameEvents.StateChanged.any
GoToMainMenu()Time scale 1. Stops an active co-op run and leaves its session. Builds ballparks[0], shows the selected batter in preview.MainMenu
OpenMapSelect()Preview batter, menu camera. MapSelectView builds the picked park.MapSelect
EnterCustomize()Builds ballparks[0], preview batter on the stage (AtBat.SetStage(true)).Customize
EnterIndex()Time scale 1; the INDEX screen under the lobby top bar (Index Screen). Keeps whatever park is built.Index
EnterLobby()Stops any co-op run, builds ballparks[0], preview batter.Lobby
JoinFromPlatform(code)Steam overlay “Join game”. Ignored (with a net log line) when busy in co-op, in a session or at bat. Switches to the Steam backend, enters the lobby and joins.Lobby

OnApplicationQuit saves save.json.

Numbers

WhatValueWhere to tune
Starting game modeGameModes.All[0] (Solo)GameRoot.Awake
Park behind the menusContent.ballparks[0] (Sandlot Field)GoToMainMenu, EnterLobby, EnterCustomize

In the code

FileWhat it does
Scripts/Core/GameBootstrap.csResetStatics, Boot.
Scripts/Core/GameRoot.csAwake, SetupCameraAndLight, SetState, HookSession, JoinFromPlatform, EnterLobby, GoToMainMenu, OpenMapSelect, EnterCustomize, EnterIndex, IsReady, Run, and the Intro / Outro directors.
Scripts/Data/ContentDatabase.csLoadOrDefault().

Gotchas

  • GameRoot.Run is ActiveCoop ?? Runs. Code that wants “the run on screen” must use Run, not Runs.
  • IsReady (Meta, Runs and AtBat set) goes false after a Play-mode recompile; the UI skips drawing rather than crash.
  • Every navigation method resets Time.timeScale to 1, so leaving a paused at-bat never leaves the game frozen.
  • SwingInput has its own RuntimeInitializeOnLoadMethod hooks that create the swing input action (Controls).
  • The Intro and Outro states are not entered through a navigation method: RunManager, CoopRun and the directors’ Preview call SetState directly (Game States and Screens).