Archived on 2026-10-24. This is a historical implementation note, not current setup instructions or an active task list. Original claims, code examples, file paths, and completion statements have not all been revalidated. Use the current documentation and issue register for maintenance decisions.
MenuController GameMenu Integration¶
Overview¶
This document describes the unified back button handling between MenuController and GameMenuManager to properly support settings toggling during gameplay.
Problem¶
Original GameMenuManager Issues¶
- Duplicate Pause Tracking: Had both
isGamePausedin GameMenuManager andGameStateService.isPausedin GameManager - Inconsistent Back Button: Settings was toggled manually, but subpages within settings needed normal navigation
- Missing State Checks: Could open settings during rewards or game over
- Resume Bug: Closing settings during rewards would resume time incorrectly
MainMenu Pattern (for comparison)¶
- Has
initialPage(main menu) that's always visible - Back on root → Shows quit confirmation (
exitPage) - Navigation: Push/pop through pages normally
- Clean, predictable behavior
GameMenu Pattern (unique requirements)¶
- No visible root page during gameplay
- Settings is "toggled" by back button, not always visible
- Settings has subpages (audio, controls, etc.) that need normal navigation
- Different from MainMenu pattern
Solution¶
Unified Back Button Logic¶
The new approach respects GameMenuManager's unique requirements while integrating cleanly with MenuController:
private void HandleBackButton()
{
// 1. Check if we can open settings (not during reward or game over)
if (!GameManager.instance.GameState.CanPause)
{
Debug.LogWarning("Cannot open settings during reward or game over");
return;
}
// 2. Check current state using GameState (single source of truth)
bool isPaused = GameManager.instance?.isPaused ?? false;
bool isSettingsOpen = menuController.IsPageInStack(settingsPage);
if (!isPaused && !isSettingsOpen)
{
// Scenario 1: Game running, settings closed → Open settings
GameManager.instance.PauseGame();
menuController.PushPage(settingsPage);
}
else if (isPaused && isSettingsOpen)
{
// Check if we're on settings root or a subpage
bool isOnSettingsRoot = menuController.IsPageOnTopOfStack(settingsPage);
if (isOnSettingsRoot)
{
// Scenario 2: Settings root open → Close settings and resume
GameManager.instance.isPaused = false; // This calls UpdateTimeScale
menuController.PopPage();
}
else
{
// Scenario 3: Settings subpage open → Navigate back normally
menuController.PopPage();
}
}
}
State Flow¶
Gameplay State:
Stack: Empty
isPaused: false
isSettingsOpen: false
→ Back press → Pause, push settings
Settings Root State:
Stack: [settingsPage]
isPaused: true
isSettingsOpen: true
isOnSettingsRoot: true
→ Back press → Pop settings, resume
Settings Subpage (e.g., Audio):
Stack: [settingsPage, audioPage]
isPaused: true
isSettingsOpen: true
isOnSettingsRoot: false
→ Back press → Pop audio (normal navigation)
Back to Settings Root:
Stack: [settingsPage]
isPaused: true
isSettingsOpen: true
isOnSettingsRoot: true
→ Back press → Pop settings, resume
Key Changes¶
1. Removed Duplicate State Tracking¶
Before:
private bool isGamePaused = false; // GameMenuManager
// AND
public bool isPaused { get; set; } // GameStateService
After:
// Only use GameManager.GameState.isPaused (single source of truth)
bool isPaused = GameManager.instance?.isPaused ?? false;
2. Proper Settings Subpage Navigation¶
Settings subpages (audio, controls, etc.) now navigate normally through the page stack:
Settings (root)
├── Audio (subpage)
├── Controls (subpage)
├── Video (subpage)
└── Gameplay (subpage)
Navigation flow: - Press Settings button → Navigate to subpage - Press Back → Return to Settings root - Press Back → Close settings and resume game
3. Integration with GameState Blocking¶
Settings can only be opened when GameState.CanPause returns true:
public bool CanPause => !isRewarding && !isGameOver;
This prevents: - ❌ Opening settings during rewards - ❌ Opening settings during game over - ❌ Opening settings during critical animations
4. Fixed Resume Bug¶
Problem: Old code called ResumeGame() directly, which would set Time.timeScale = 1 even if rewards were still active.
Solution: Set isPaused = false, which calls UpdateTimeScale():
GameManager.instance.isPaused = false; // Automatically calls UpdateTimeScale()
UpdateTimeScale() checks ALL states:
public void UpdateTimeScale()
{
Time.timeScale = ShouldTimeBePaused ? 0f : 1f;
}
public bool ShouldTimeBePaused => isPaused || isRewarding || isGameOver;
Result: Time stays paused during rewards, even after settings close.
5. Clean Gate Animation¶
Added logic to close settings before gate animation (play again/quit):
public void ShowPlayAgainConfirmation()
{
// Close settings if open
if (menuController.IsPageInStack(settingsPage))
{
while (menuController.IsPageInStack(settingsPage))
{
menuController.PopPage();
}
}
menuController.StartCoroutine(menuController.GateAnimation(true));
}
This prevents settings from interfering with gate animations.
Benefits¶
1. Single Source of Truth¶
- ✅ Only
GameStateServicetracks pause state - ✅ No duplicate/conflicting state
- ✅ Easier to debug and maintain
2. Proper Navigation¶
- ✅ Settings subpages navigate normally
- ✅ Settings root toggles pause
- ✅ Clear state transitions
3. Integration with GameState¶
- ✅ Respects
CanPauseblocking logic - ✅ Cannot open during rewards/game over
- ✅ Time scale automatically managed
4. Bug Fixes¶
- ✅ Settings no longer resume time during rewards
- ✅ Consistent with unified state system
- ✅ No more duplicate pause tracking
5. Clean Architecture¶
- ✅ GameMenuManager handles toggle logic
- ✅ MenuController handles page navigation
- ✅ GameState handles state management
- ✅ Clear separation of concerns
Comparison: MainMenu vs GameMenu¶
| Aspect | MainMenuManager | GameMenuManager |
|---|---|---|
| Root Page | Main menu (always visible) | None during gameplay |
| Initial Page | Main menu | null |
| Exit Page | Quit confirmation | null |
| Back on Root | Show quit confirmation | N/A (no visible root) |
| Settings | Normal page in stack | Toggled by back button |
| Pause | N/A | Pauses game on open |
| Resume | N/A | Resumes on close |
Settings Subpage Requirements¶
For settings to have subpages (Audio, Controls, Video, Gameplay), ensure:
-
Subpages are separate Page components:
public class AudioPage : Page { } public class ControlsPage : Page { } public class VideoPage : Page { } public class GameplayPage : Page { } -
Navigation buttons push subpages:
public void OpenAudioPage() { menuController.PushPage(audioPage); } -
Back button handles navigation:
- Already implemented in
HandleBackButton() - Checks if on settings root or subpage
- Navigates appropriately
Testing Checklist¶
- [ ] Back button opens settings during gameplay
- [ ] Back button closes settings and resumes game
- [ ] Can navigate to settings subpages (audio, controls, etc.)
- [ ] Back button in settings subpages navigates to settings root
- [ ] Cannot open settings during rewards
- [ ] Cannot open settings during game over
- [ ] Settings close before gate animation (play again/quit)
- [ ] Time pauses when settings open
- [ ] Time resumes when settings close
- [ ] Time stays paused during rewards (settings closed)
- [ ] No duplicate pause state tracking
- [ ] Settings subpages navigate normally
Files Modified¶
GameMenuManager.cs¶
Changes:
- Removed isGamePaused private field
- Updated HandleBackButton() to use GameState
- Added logic for settings root vs subpage navigation
- Added settings cleanup before gate animation
- Integrated with GameState.CanPause blocking
Integration Points¶
The integration works through MenuController's existing system:
menuController.Configure(
initialPage: null, // No visible root during gameplay
exitPage: null, // No exit page
firstFocusItem: firstFocusItem,
onRootBackPressed: HandleBackButton, // Our custom handler
onGateAnimationComplete: HandleGateComplete
);
HandleBackButton() is called by MenuController when back is pressed and page stack is empty (root).
Future Improvements¶
- Add Settings State Tracking
- Track which settings tab is active
-
Save/restore settings state
-
Add Settings Page Enum
public enum SettingsPage { None, Root, Audio, Controls, Video, Gameplay } -
Add Settings History
- Remember last opened settings subpage
-
Return to same subpage when reopened
-
Add Settings Animation
- Animate settings open/close
- Smooth transitions