Boot and reset process¶
This page aims to detail how the game initialises itself from the first component's Start to the title screen on StartMenu.
How the main scene is setup¶
The main scene contains 2 important rooted GameObject:
StartMenu
which has a disabled StartMenu component (it gets enabled during the boot process)MainCam
which contains the structure of the Camere system, but more importantly, it has a child calledMainCamera
that has the only MainManager component in the game and it is enabled
This effectively means that the earliest Unity component that will kick in is MainManager which has a Start method. This is where the boot process starts.
MainManager.Start¶
MainManager.Start can be considered the first declared method to run with the only methods running before it are runtime defined such as static constructors. It is the earlest part of the boot process.
Here's what happens here:
- Application.runInBackground is set to true only on non console platforms (Application.isConsolePlatform is false)
- Thread.CurrentThread.CurrentCulture is set to the
en-US
culture info. This is done to force all data parsing with float to use.
. NOTE: It would be more appropriate if this was the invariant culture, buten-US
happens to have the correct parameters for this - On the first boot (not a reset), InputIO.StartUp runs. This is a platform specific method that may need to run some logic. On Steam, it initialises the SteamManager GameObject which is the only GameObject marked DontDestroyOnLoad so it persists between resets
- Time.timeScale is reset to 1.0 (normal speed)
musicresume
is reset to -1.0 (it avoids carrying over the value from a reset to avoid a bad timestamp restore, check the musicresume for more information)- The
instance
is set to this MainManager which completes the singleton setup - A LoadEverything coroutine starts which is the next phase of the boot process
- If not running the the Unity editor, the Application.version gets logged silently
LoadEverything (part 1/2)¶
LoadEverything is the main startup coroutine of the game. It runs once per each boot or reset as it is called by Start and only Start.
private static IEnumerator LoadEverything()
The reason this is a coroutine is because there is a loading icon childed to StartMenu with a SpriteBounce that continuously run as this boot process is ongoing. If it was a method, the icon wouldn't animate, but being a coroutine, it inserts yields in various places to allow the icon to animate as no loading is done asynchronously. While it does result in the icon animating in a staggered manner, it still means the icon isn't perpetually frozen.
There's 2 parts to LoadEverything: anything before LoadEssentials and anything after it's done. Here's what happens in the first part of that coroutine:
- Cursor.visible is set to false (so no mouse cursor is visible)
MainCamera
.depthTextureMode is set to Depth. Check the camera system documentation to learn more about what theMainCamera
is- RenderSettings.ambientLight is set to Color.gray
basicload
is reset to false (this is so that nothing runs that could interfere with the boot process after a reset)- A frame is yielded
events
is set to a new EventControl component on theinstance
. This should be the only EventControl in the game- A frame is yielded
chaptername
is set to a new LoadEssentials coroutine which is the most intensive part of the boot process that loads the vast majority of the assets in the game into various fields- Yield all frames until
chaptername
is null (indicating that LoadEssentials completed)
The rest will be mentioned below in the second part. The next phase is LoadEssentials.
LoadEssentials¶
This phase is the most intensive part of the boot process:
private IEnumerator LoadEssentials()
It's a coroutine for the same reason that LoadEverything is. It is expected that the coroutine is set to chaptername
as this coroutine will set it to null upon completion so LoadEverything can yield on it.
This is potentially the most performance intensive method in the whole game because it loads the vast majority of the game's assets into several MainManager fields for usage in the game. Most of these fields are static as they aren't expected to change during the game, but they will be reassigned on reset since this coroutine is also called once on a reset.
Here is what happens in the coroutine:
- The
letterpool
is initialised to have 500 letter slots each with NewLetter called on them to initialise all slots screenshake
is reset to Vector3.zero. Check the camera system documentation to learn more- Yield for a frame
- The following fields are set to the return of a Resources.LoadAll call each followed by a frame yield done in order:
textboxsprites
: Sprite fromSprites/GUI/textbox
partfab
: GameObject fromPrefabs/Particles
parttex
: Texture fromSprites/Particles
spritepart
: Sprite fromSprites/Particles
asounds
: AudioClip fromAudio/Sounds
- On console platforms,
msounds
is set to a new array each containing the return of Resource.Load of the AudioClip fromAudio/Music/X
whereX
a Musics enum value as string for all Musics eneum values (this is done to cache them for faster loads later on console) dsounds
is set to the return of Resource.LoadAll AudioClip fromAudio/Sounds/Dialogue
- A frame is yielded
- A silent log line is printed indicating the amount of
partfab
loaded, the amount ofparttex
+spritepart
loaded and the amount ofasounds
anddsounds
loaded GUICamera
is set to the first child ofMainCamera
's Camera component. Check the camera system documentation to learn more about this structure- The following fields are set to the return of a Resources.Load call done in order (no yielding between them):
defaultpmat
: The PhysicMaterial fromMaterials/DefaultPhysis
spritemat
: The Material fromMaterials/SpriteMat
holosprite
: The Material fromMaterials/SpriteHologram
emptymat
: The Material fromMaterials/Empty
spritematlit
: The Material fromMaterials/SpriteLit
outlinemain
: The Material fromMaterials/OutlineMain
spritedefaultunity
: The Material fromMaterials/SpriteDefault
windShader
: The Material fromMaterials/WindShader
Main3D
: The Material fromMaterials/3DMain
Fade3D
: The Material fromMaterials/3DFade
grayscale
: The Material fromMaterials/Grayscale
letters
: The TextAsset fromData/Letters
.ToString().ToCharArray() (it is UNUSED in practice)
- The following fields are set to the return of a Resources.LoadAll call each followed by a frame yield done in order:
grasssprite
: Sprite fromSprites/Objects/grass
leafsprites
: Sprite fromSprites/GUI/battleleaves
- The following fields are set to the return of a Resources.Load call done in order (no yielding between them):
fakelight
: The Material fromMaterials/FakeLight
.shadershadowsprite
: The Sprite fromSprites/Misc/shadow
mainPlane
: The Material fromMaterials/MainPlane
fadePlane
: The Material fromMaterials/FadePlane
languagehelp
: The TextAsset fromData/LanguageHelp
.ToString().Split(\n
)
endata
is loaded, check the entity data documentation to learn more- A frame is yielded
hitpart
is set to a the ParticleSystem component of a new instance of thePrefabs/Particles/HitPart
prefab positioned offscreen at -999.0 in ydeathpart
is set to a the ParticleSystem component of a new instance of thePrefabs/Particles/deathsmoke
prefab positioned offscreen at -999.0 in y and -90.0 x rotationglobalcamdir
is configured as a new GameObject namedCamDir
childed toMainCamera
with no local positioning. Check the camera system documentation to learn morelibrarysprites
is set to the return of Resource.LoadAll Sprite fromSprites/Items/EnemyPortraits
- A frame is yielded
- The following fields are initialised:
extrafollowers
(new list)sounds
(15 new AudioSources, check the sounds playback documentation to learn more)music
(1 new AudioSource, check the music playback documentation to learn more)
leafpos
is loaded, check the leafpos documentation to learn morequestchecks
is loaded, check the questcheck data documentation to learn more- A frame is yielded
termacadeprize
is loaded, check the termacade prize data documentation to learn moreenemydata
is loaded, check the enemydata documentation to learn more- A frame is yielded
fonts
is initialised to a new array containing all the Font fromFonts/X
whereX
is a font element (check the fonttype documentation to learn more)fontmat
is initialised to a new array containing all the Material fromFonts/X
whereX
is a font element (check the fonttype documentation to learn more)bleeps
is initialised to a new AudioSource added to the MainManager.instace, check the bleep documentation to learn more- All
sounds
element are set to AudioSource each added to the MainManager.instance with a velocityUpdateMode of Fixed and a volume ofsoundvolume
, check the sounds playback documentation to learn more - A frame is yielded
music[0]
is set to an AudioSource added to the MainManager.instance set to play on loop, check the music playback documentation to learn more- A frame is yielded
guisprites
is set to all Sprite loaded fromSprites/GUI/gui
followed by all the ones loaded fromSprites/GUI/gui2
cursorsprite
is set to an array with a single element beingguisprites[145]
- If Application.version contains
Beta
, NewUIObject is called to create an object calledbetawatermark
childed to theGUICamera
locally positioned at (7.8, -4.4, 1.0) using the spriteguisprites[169]
with a sortOrder of 9999 which has a color of pure black at 0.1 alpha - A frame is yielded
letterbox
is initialised to an array of 2 elements each the following configuration of a NewSolidColor of Color.black each childed to theGUICamera
:- Name:
letterboxX
whereX
is theletterbox
index - Pivot: (0.5, 0.5, 0.5)
- Color: Color.clear
- Layer: 5 (
UI
) - Scale: (5.0, 0.5, 1.0)
- sortingOrder: -40
- Name:
letterbox[0]
is locally positioned at (0.0, 5.75, 10.0)letterbox[1]
is locally positioned at (0.0, -5.75, 10.0)- A frame is yielded
recipedata
is loaded, check the recipe data documentation to learn more- A frame is yielded
- instance.
projectilepsrites
is set to Resource.LoadAll Sprite fromSprites/Misc/projectiles
- A frame is yielded
- Physics.gravity is set to -40.0 in y
- InputIO.GetJoyButtons is called which updates the controller bindings according to the current settings
- A frame is yielded
- InputIO.SetDefaultKeys is called which sets the default keyboard bindings
- A frame is yielded
chaptername
is set to null to mark this coroutine as completed
LoadEverything (part 2/2)¶
After LoadEssentials, the boot process continues in LoadEverything:
- An frame is yielded after LoadEverything is done
- If
languageid
isn't defined (it's negative meaning this is the first boot), InputIO.LoadSettings is called without overwrite. Otherwise (languageid
is defined after a reset), SetVariables is called, but this call isn't useful since it will get called later by StartMenu, check the SetVariables section below to learn more - A frame is yielded
- SetRenderTexture 0 is called to turn the feature off
- A frame is yielded
- instance.FontSet is called
- A frame is yielded
basicload
is set to true which enables most of the logic of MainManager's Unity events (Update, LateUpdate and FixedUpdate)- The StartMenu in the scene is finally enabled and it is where the boot process will resume
From there, LoadEverything is done and won't be called until a reset happens. This means MainManager starts its update process, but the boot process isn't completely done. It resume on StartMenu.Start which has been newly enabled so it will kick in on the next frame.
StartMenu.Start¶
The boot process continues in StartMenu.Start which setup the last phase of the boot process.
Here's what happens here:
- RenderSettings.skybox is set to Resource.Load Material from
Materials/Skybox/Grass1
with its_Tint
color set to Color.gray - The StartMenu gets childed to the
GUICamera
locally positioned at (0.0, 4.5, -17.0) - StartMenu's
sprites
gets set to all SpriteRenderer in the StartMenu's children - Any invoke of DoClock on MainManager.instance is cancelled
- StartMenu's
copycursor
is initialised for the save file selection - If this Start isn't running after selecting the language select option on the title screen (StartMenu's
noload
is false), all the save data get preloaded for selection later. Otherwise (this is a reset that happened due to selecting the language select option on the title screen), Resource.UnloadUnusedAssets is called - StartMenu.LoadModel is called which setups some title screen entities depending on the furthest progression of any save file
- What happens here depends if
languageid
is defined (meaning not negative) if it's not defined, it means that no language was loaded from config.dat so the game will go to the language selection screen with by doing the following:- SetUpList is called to setup a languages listtype without description and sell
- ShowItemList is called to setup the same listtype with a position of (-1.0, -0.35) without description and sell
sprites[3]
(the load icon) gets disabled
- Otherwise (
languageid
is defined from config.dat):- MainManager.instance.SetVariables is called which is the last step of the boot process
- The Intro coroutine starts which starts the actual title screen UI
noload
is reset to false (since the reset completed)
So from there, there's 2 possibilities: either the game proceeds to the language selection screen or the game proceeds to the last stage of the boot process which is SetVariables.
Language selection¶
Essentially, most of the handling here is done entirely by MainManager.Update, specifically handling the languages listtype. Only confirmations are accepted since a language must be selected before proceeding. Eventually, when a language is selected, the languages listtype confirmation handling kicks in which will notably call SetVariables and reach the last stage of the boot process.
It's possible however after boot to get back to the language selection from the title screen. Doing this will cause a full reset, but the languageid
becomes undefined and then saved in the settings so the next time StartMenu.Start kicks in, it will detect this case (since this also sets noload
to true) and immediately go to the language selection.
There's also a specific method involved when confirming the language: LoadLangSpecific.
public static void LoadLangSpecific()
This method applies a German language specific adjustement where some medals UI icons gets their sprites changed such that they display an "A" instead of an "M". It is only called as part of event 22 (loading a save file) and on Update when processing a confirmation in the language selection listtype. This ensures that the adjustement is applied or not depending on the language being selected.
SetVariables¶
This is the last stage of the boot process where lots of TextAsset are parsed and loaded into fields as well as several fields getting initialised.
public void SetVariables()
It's important to mention that it's possible this is the second time SetVariables gets called: it might have been called earlier during LoadEverything as mentioned above. However, the only time this happens is under circumstances where a second call here ALWAYS happen meaning the first call simply redo everything the first one did rendering the first call useless, but doesn't have adverse effects in practice. Nonetheless, by the time StartMenu.Start is done or the language selection is done, SetVariables is guaranteed to be called once which is required for the game's boot.
Here is what happens here:
- The following fields are set to the return of a Resources.Load call that loads language specific data done in order where
X
in the asset path is thelanguageid
(check the linked documentation to learn more):battlemessage
: All Sprite fromSprites/GUI/BattleMessage/battlemX
(if this returns null or empty array, the path fallsback toSprites/GUI/BattleMessage/battlem0
which are the Sprite in English)menutext
: TextAsset fromData/DialoguesX/MenuText
(dialogue data documentation)commondialogue
: TextAsset fromData/DialoguesX/CommonDialogue
(dialogue data documentation)musicnames
: TextAsset fromData/DialoguesX/MusicList
(musics data documentation)commandhelptext
: TextAsset fromData/DialoguesX/ActionCommands
(other language specific data documentation)areanames
: TextAsset fromData/DialoguesX/AreaNames
(other language specific data documentation)
- The following fields gets initialised:
samiramusics
(empty list). Check the music playback documentation to learn more- The hardcoded values of
prizeflags
,prizeids
andprizeenemyids
, check the prize medals documentation to learn more - Both elements of
badgeshops
andavaliablebadgepool
(each being empty lists) - All 3
items
elements (each being empty list)
- The following data gets loaded (check the linked documentation to learn more about the formats and TextAsset used, some are language specific):
itemdata
: items data documentationskilldata
: skills data documentationbadgedata
andbadgeorder
: medals data documentationboardquestdata
: boardquests data documentationlibrarydata
,libraryorder
,achiveicons
,discoveryicons
: Several library data documentations:- discoveries data documentation
- enemies data documentation
- recipes data documentation
- records data documentation (logic documented in the achievements documentation)
- All 3
boardquests
elements gets initialised (all with a list containing 0 which is the dummyNone
element). Check the QuestBoards system documentation to learn more enemynames
gets loaded, check the enemies data documentation to learn morebadges
gets initialed to a new list followed by SetUpBadges being called. Check the medals documentation to learn morestatbonus
gets initialised to a new list followed by a ChangeParty fromscratch with a party of {Bee
,Beetle
}. Check the player party documentation to learn more- All of the following fields gets initialised to a value:
enemyencounter
: new int[256, 2], check the enemy ecounter save data documentation to learn moreinsideid
: -1 (not in an inside), check the insides documentation to learn moretp
,maxtp
andbasetp
: 10bp
andmaxbp
(MP fields): 5partylevel
: 1maxitems
: 10maxstorage
: 35- Several camera system related fields:
camspeed
: 0.1camanglespeed
: 0.1camoffset
:defaultcamoffset
which is normally (0.0, 2.25, -0.25)camoffset2
: Vector3.zerocamangleoffset
:defaultcamangle
which is normally (10.0, 0.0, 0.0)
- flags array: new array of 750 elements
- flagvar array: new array of 70 elements with these initial values (rest are left to 0):
flagvar[28]
: 9500flagvar[29]
: 4500
- flagstring array: new array of 15 elements
- regionalflags array: new array of 100 elements
- crystalbflags array: new array of 50 elements
vectorflags
array: new array of 10 elements (this array is UNUSED and it contains Vector3 elements)librarystuff
array: new bool[librarydata
.GetLength(0),librarydata
.GetLength(1)]. Each first dimension's subarrays is docuimented in the following pages:- 0: discoveries entry
- 1: bestiary entry
- 2: Recipe entry
- 3: Records entry
- 4: Areas
musicvolume
: 1.0 (the music playback base volume scaler)soundvolume
: 1.0 (the sound playback base volume scaler)halt
: false (a field only used in event 1 which is after hitting the bridge at Snakemouth Den)- instance.
switchicon
: The SpriteRenderer of a NewUIObject with nameSwitchIcon
childed to theGUICamera
with a pos of (-8.0, 0.0, 10.0) scaled by 0.75x uniformely promptpick
: -1 (A prompt or numberprompt related field)
- LoadItemSprites gets called to initialise
itemsprites
, check the section below for details - InputIO.LoadSettings without overwrite is called to reload config.dat
Once this is done, the boot process is complete and the game is ready to proceed to the title screen.
LoadItemSprites and GetMoreItem¶
These 2 methods involve complex logic to initialise itemsprites
.
private static void LoadItemSprites()
This is a part of SetVariables.
This method intialises itemsprites
as a [2, 256] array of sprites where the [0, i
] array contains the Items sprites (each have an item id of i
) and the [1, m
] array contains the Medals sprites (each have a medal id of m
). The sprites comes from the Sprites/Items/items0
and Sprites/Items/items1
sprites arrays.
The sprite array used and the index used in that sprite array for each items or medals are determined with a very specific logic:
- For items and where the item id is below 176, the sprite is pulled from
Sprites/Items/items0
where the sprite array index is the same as the item id - For items and where the item id is 176 or above, the sprite is pulled from
Sprites/Items/items1
where the sprite index is 176 - the item id - For medals and where the matching
badgedata
's field 8 is negative, the sprite is pulled fromSprites/Items/items0
where the sprite index is 176 + the medal id - For medals and where the matching
badgedata
's field 8 isn't negative, the sprite is pulled fromSprites/Items/items1
where the sprite index is the matchingbadgedata
's field 8
private static Sprite GetMoreItem(int i, int offset = 176)
This is a part of LoadItemSprites that loads a sprite from Sprites/Items/items1
using the index offset
- i
.
Reset¶
As mentioned multiple times, the game supports soft resetting itself through the Reset method:
public static void Reset()
All this method does is call SceneManager.LoadScene(0) which reloads the main scene. Since the entire game is built with a single scene being the main scene, reloading it is equivalent to a full soft reset: everything is lost except the SteamManager (if applicable) and any values of static fields.
When a reset happens, most of the boot process is redone from scratch.