The main menu is the first interactive screen in PC Simulator after the Menu scene loads. It is driven by the MainMenu behaviour, which initializes localization, restores settings, coordinates child pages through MenuManager, and starts asynchronous scene loads behind a Loading screen.
| Unity scene | Menu (project: Assets/Scenes/Menu.unity)
|
| Core scripts | MainMenu.cs, MenuManager.cs, FileMenu.cs
|
| Singleton | MainMenu.Instance
|
| Save loads | SaveManager.Loader + scene startRoomSceneIndex + GameData.room
|
| Examples | StreamingAssets/Examples/*.pc
|
Overview
The main menu is the hub for:
- Opening and starting games from user save files (
.pc) in the save folder - Launching the Tutorial scene and recording that the current tutorial revision was shown
- Loading example presets from streaming assets (e.g.
Classic.pc, versioned examples,Hidden Room.pcwhen triggered from in-game) - Navigating sub-pages (options, language, about, file tools, etc.) via a stacked UI managed by
MenuManager - Quitting the application where supported
The title line alternates every 0.5 seconds between PC Simulator and PC Simulator_, with PC in orange using Unity UI Rich Text.
Localization
On Awake, MainMenu:
- Calls
Localization.CreateContent() - Sets language from
PlayerPrefskey"Language", or fromApplication.systemLanguagemapped to codes such asEN,DE,ZH-CN,JA, etc. - Subscribes to
Localization.LanguageChangedto write the active language back toPlayerPrefs
Start restores:
- FPS —
FpsSetting.RestoreSetting() - Resolution —
ResolutionSetting.RestoreSetting() - Volume —
AudioListener.volumefromPlayerPrefs "Volume"(default1f)
Tutorial prompt
If PlayerPrefs.GetInt("TutorialVersion", -1) is less than the inspector field tutorialVersion, the object guideToTutorial is enabled; otherwise it is hidden. Calling Tutorial() sets TutorialVersion to the current value and loads the scene named "Tutorial".
MenuManager keeps a stack of active menu GameObject pages:
ShowMenu(string pageName)— finds a child whose hierarchy name equalspageName(e.g.Loading,FileInformation), hides the previous top if different, pushes and shows the new pageBack()— pops one level if the stack has more than one entry, reactivates the previous pageHideMenu(bool hide)— toggles visibility of the top page without changing the stack
Optional UI click sounds can play on ShowMenu and Back when enabled in the inspector.
Scene loads always call ShowMenu("Loading") first so the loading panel is visible during the async operation.
Loading screen behaviour
Loads use SceneManager.LoadSceneAsync with allowSceneActivation == false until progress reaches 0.9f.
- Displayed percent:
Mathf.Clamp01(operation.progress / 0.9f) * 100f - When
progress >= 0.9f, the text shows 100%, waits 0.5 seconds, then setsallowSceneActivation = true - Label text: localized
"Loading..."plus a new line and[percentage%]
Save files and examples
User saves (FileMenu)
- Scans the save directory for files matching the save extension
- Sorts by last write time, newest first
- Each slot shows
GameData.roomNameand a Hardcore marker whenGameData.hardcoreis true - Clicking the name runs
MainMenu.Instance.LoadFile(loader), which setsSaveManager.Loaderand loads build indexstartRoomSceneIndex + GameData.room
Example presets
LoadExample(string name) reads StreamingAssets/Examples/ + name + .pc. On Android and WebGL the file is read via UnityWebRequest; elsewhere via File.ReadAllText. The string is parsed with DataLoader.LoadFromString, then the same LoadFile path as a normal save.
Scene index convention
LoadScene(int sceneBuildIndex) loads startRoomSceneIndex + sceneBuildIndex. Saves store a small integer room; the inspector base index aligns that id with the correct Unity scene in build order.
Exit
Exit() calls Application.Quit() (no visible effect in the Unity Editor).
Implementation reference
| Script | Role |
|---|---|
MainMenu
|
Singleton; localization; tutorial gating; title blink (InvokeRepeating every 0.5 s); example and scene loading; quit
|
MenuManager
|
Stack of menu pages; show, back, hide; optional click sound |
FileMenu
|
Lists saves, rename/metadata UI, delete, import external .pc
|
In-depth: source code
MainMenu.cs — lifecycle
Awake
- Sets
MainMenu.Instanceso other scripts (e.g.FileMenu,HiddenRoom) can invokeLoadFile/LoadExamplewithout serialized references. - Runs localization setup and applies stored or system-default language.
- Persists language changes through
Localization.LanguageChanged.
Start
- Restores FPS and resolution from saved settings.
- Shows or hides the tutorial guide based on
TutorialVersionvstutorialVersion. - Applies master volume to
AudioListener. - Starts the repeating title animation.
Blinking
Toggles between two Rich Text strings on the title Text component for a blinking underscore effect.
LoadExample(string name)
Resolves StreamingAssets/Examples/{name}.pc, reads file bytes as text (platform-specific), then DataLoader.LoadFromString and LoadFile. On Android/WebGL the implementation uses UnityWebRequest and waits synchronously for completion.
LoadFile(DataLoader loader)
Assigns SaveManager.Loader = loader and loads startRoomSceneIndex + loader.GameData.room.
Tutorial()
Writes PlayerPrefs "TutorialVersion" and loads the "Tutorial" scene by name via LoadScene(string).
LoadAsync coroutine
- Shows the
Loadingmenu page by name. - Holds
allowSceneActivationfalse while updatingloadingTextfrom asyncprogress. - After
0.9fprogress, shows 100%, waits 0.5 s, then allows activation.
MenuManager.cs
Start
Initializes the stack: whichever entry in the menus array is already active is pushed as the initial page.
ShowMenu(string pageName)
Linear search for menu.name == pageName. If the stack top is a different object, deactivate it, push the new page, activate it. Same reference as current top results in no duplicate push.
Back
If menuStack.Count <= 1, return. Otherwise pop, deactivate popped page, activate new top.
HideMenu(bool hide)
Sets SetActive(!hide) on the stack top without popping.
FileMenu.cs
Start
Enumerates save files, maps path → last write time, sorts descending, calls AddSlot for each. Shows empty when the list is empty.
AddSlot(string path)
Wrapped in try/catch; failures are skipped. Instantiates a row, binds roomName and hardcore flag, wires Edit to file information and Name to MainMenu.Instance.LoadFile.
RefreshLoadButton / DeleteLoadButton
Refresh updates labels after metadata changes. Delete removes the file, destroys the row, updates empty state.
Import
Uses native file picker filtered to .pc. Verifies read permission, validates via DataLoader.LoadFromPath, copies into the save folder with SaveUtility.GetNewPath, then clears and rebuilds the slot list from disk.
Data flow summary
| Action | Code path |
|---|---|
| Player starts a save from the list | MainMenu.LoadFile → SaveManager.Loader → async load startRoomSceneIndex + room
|
| Example or secret loads a preset | LoadExample reads StreamingAssets/Examples/<name>.pc → LoadFile
|
| Any scene load from menu | MenuManager.ShowMenu("Loading") then LoadAsync
|
| Player confirms tutorial | Tutorial() updates TutorialVersion → load "Tutorial"
|
See also
- Tutorial scene and
Tutorial.csin-game steps - Pause menu and in-game
MenuManagerusage - Save format:
DataLoader,GameData,SaveUtility