/* * author: mark joshwel * date: 29/5/2024 * description: game manager singleton for single source of truth state management */ using UnityEngine; /// /// singleton class for managing the game state as a single source of truth /// public class GameManager : MonoBehaviour { /// /// enum for available menus in the game, for use with ShowMenu() /// public enum DisplayState { Game, ScreenMainMenu, ScreenOptionsMenu, ScreenCreditsMenu, ScreenPauseMenu, ScreenCaughtPause, ScreenEscapedMenu, UnassociatedState } /// /// singleton pattern: define instance field for accessing the singleton elsewhere /// public static GameManager Instance; /// /// the current state of the game /// private DisplayState _state = DisplayState.UnassociatedState; /// /// property to check if the game is paused based on the current DisplayState /// // TODO: remove this if not needed public bool Paused => _state != DisplayState.Game; /// /// function to set don't destroy on load and check for multiple instances /// private void Awake() { // check if instance hasn't been set yet if (Instance == null) { // set this instance as the singleton instance Instance = this; // don't destroy this instance on scene load DontDestroyOnLoad(gameObject); Debug.Log("GameManager: Awake as singleton instance"); } // check if instance is already set and it's not this instance else if (Instance != null && Instance != this) { Debug.Log("GameManager: Awake as non-singleton instance, destroying self"); // destroy the new instance if it's not the singleton instance Destroy(gameObject); } } /// /// called when game starts, sets state to main menu /// private void Start() { SetDisplayState(DisplayState.ScreenMainMenu); } /// /// helper function to hide any menu that is currently showing /// private void HideMenuHelper() { // get all child menus in the "Menus" parent object foreach (var menu in GameObject.FindGameObjectsWithTag("Menus")) foreach (Transform menuChild in menu.transform) { // disable the menu Debug.Log($"GameManager: HideMenuHelper - hiding menu '{menuChild}'"); menuChild.gameObject.SetActive(false); } } /// /// helper function for SetDisplayState() to pause the game, /// called before the incoming game state is set /// /// the to-be-set state of the game private void PauseGameHelper(DisplayState incomingState) { // if we're transitioning from a state of gameplay to a state of non-gameplay, // then we should pause the game if (_state == DisplayState.Game && incomingState != DisplayState.Game) { Debug.Log("GameManager: PauseGameHelper - actually pausing game"); Time.timeScale = 0f; Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } // if the current and incoming states are the same, then we shouldn't do anything, // so we return early if (_state == incomingState) { Debug.Log("GameManager: PauseGameHelper - states are the same, returning early"); return; } // hide any menu that is currently showing HideMenuHelper(); // get all child menus in the "Menus" parent object foreach (var menuParent in GameObject.FindGameObjectsWithTag("Menus")) foreach (Transform menu in menuParent.transform) { // show the menu based on the incoming state // get the associated state of the menu var associatedState = menu.gameObject.GetComponent().associatedState; Debug.Log( $"GameManager: PauseGameHelper - found menu '{menu}' " + $"with associated state {associatedState} " + $"against incoming state {incomingState}"); // guard clause if the menu isn't what we're looking for if (associatedState != incomingState) continue; // if the associated state is the same as the incoming state, then show the menu Debug.Log($"GameManager: PauseGameHelper - showing menu for {incomingState}"); menu.gameObject.SetActive(true); } } /// /// helper function for SetDisplayState() to resume the game, /// called before the incoming game state is set /// /// the to-be-set state of the game private void ResumeGameHelper(DisplayState incomingState) { // if we're NOT transitioning from a state of non-gameplay to a state of gameplay, // (which means currently we are in a state of gameplay), // then we shouldn't do anything, because the game is already running, // so we return early if (_state == DisplayState.Game || incomingState != DisplayState.Game) { Debug.Log( "GameManager: ResumeGameHelper - returning prematurely as" + $" _state={_state} and incomingState={incomingState}"); return; } // else, we should resume the game Time.timeScale = 1f; Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; // hide any menu that is currently showing HideMenuHelper(); } /// /// function to show a menu based on the enum passed, /// and any other necessary actions /// /// the game menu to show public void SetDisplayState(DisplayState displayState) { // check if the game is paused or not if (displayState == DisplayState.Game) { Debug.Log($"GameManager: SetDisplayState({displayState}) - resuming game"); ResumeGameHelper(displayState); } else { Debug.Log($"GameManager: SetDisplayState({displayState}) - pausing game"); PauseGameHelper(displayState); } // set the state of the game to the incoming state _state = displayState; // post-state change actions case switch switch (displayState) { // if we're transitioning to the game state, // change camera to the player camera case DisplayState.Game: // TODO: change camera to player camera return; // if we're transitioning to the main menu state, // change camera to the main menu camera case DisplayState.ScreenMainMenu: // TODO: change camera to main menu camera return; } } /// /// wrapper function to quit the game /// in case of any cleanup needed /// public void Quit() { // quit the application Application.Quit(); } /// /// resets game state and starts a new game, will call SetDisplayState() /// public void NewGame() { // set to game state SetDisplayState(DisplayState.Game); // TODO } }