using Cysharp.Threading.Tasks; using System.Collections; using UnityEngine; namespace BulletHellTemplate { /// /// Manages the game maps, including loading scenes, spawning characters, /// and unlocking new maps upon game completion. /// public class GameManager : MonoBehaviour { #region Public Fields /// /// Singleton reference to the GameManager. /// public static GameManager Singleton; #endregion #region Private Fields /// /// Stores the ID of the current map being played. /// private int currentMapId; #endregion #region Unity Callbacks private void Awake() { // Implement singleton pattern if (Singleton == null) { Singleton = this; } else { Destroy(gameObject); } DontDestroyOnLoad(gameObject); } #endregion #region Public Methods /// /// Starts the game with the specified MapId and stores the current map ID. /// /// The ID of the map to load. public void StartGame(int mapId) { currentMapId = mapId; // Store the ID of the current map foreach (var map in GameInstance.Singleton.mapInfoData) { if (map.mapId == mapId) { bool isUnlocked = map.isUnlocked || PlayerSave.GetUnlockedMaps().Contains(map.mapId); if (isUnlocked) { StartCoroutine(LoadSceneAndSpawnCharacter(map)); } else { Debug.LogError("Map is not unlocked."); } return; } } Debug.LogError("MapId not found."); } /// /// Ends the game, displays a win/lose screen, and returns to the main menu. /// If the player wins, it checks if the current map is the latest unlocked map and unlocks the next map if true. /// /// Whether the player won the game. public async UniTask EndGameAsync(bool won) { AudioManager.Singleton.StopAllAudioPlay(); #if FUSION2 FusionLobbyManager.Instance?.EndGameSession(); #endif int monstersKilled = GameplayManager.Singleton.GetMonstersKilled(); int gainedGold = GameplayManager.Singleton.GetGainGold(); int characterId = PlayerSave.GetSelectedCharacter(); var result = await BackendManager.Service.CompleteGameSessionAsync(new EndGameSessionData { won = won, mapId = currentMapId, characterId = characterId, monstersKilled = monstersKilled, gainedGold = gainedGold, }); if (result.Success) { if (!string.IsNullOrEmpty(result.Reason)) Debug.Log($"EndGame Result: {result.Reason}"); } else { Debug.LogWarning($"EndGame failed: {result.Reason}"); } UIGameplay.Singleton.DisplayEndGameScreen(won); } /// /// Retrieves the ID of the current map being played. /// /// The ID of the current map. public int GetCurrentMapId() { return currentMapId; } #endregion #region Private Methods /// /// Coroutine that loads the specified map scene asynchronously and, /// once the scene is fully loaded, spawns the player's character at the designated start position. /// Uses the LoadingManager to display the loading screen during the process. /// /// The map data containing the scene to be loaded. /// An IEnumerator used for coroutine execution. private IEnumerator LoadSceneAndSpawnCharacter(MapInfoData map) { yield return LoadingManager.Singleton.LoadSceneWithLoadingScreen(map.scene); // Spawn the player's character after the scene has been loaded SpawnCharacterAtStartPosition(map); } /// /// Spawns the player's character at the starting position in the loaded scene. /// The character passEntryPrefab is instantiated at a default position (Vector3.zero), /// and the character's data is set based on the game instance. /// Additionally, it spawns the correct UIGameplay Canvas based on the specified platform type. /// /// The map data used for setting the minimap image if available. private void SpawnCharacterAtStartPosition(MapInfoData map) { Vector3 startPosition = Vector3.zero; // Check if the character passEntryPrefab is set in the GameInstance if (GameInstance.Singleton.characterEntity != null) { // Instantiate the character at the start position CharacterEntity character = Instantiate(GameInstance.Singleton.characterEntity, startPosition, Quaternion.identity); UIGameplay uiGameplayPrefab = GameInstance.Singleton.GetUIGameplayForPlatform(); // Get the correct UIGameplay instance based on the specified platform type if (uiGameplayPrefab != null) { // Instantiate UIGameplay and store the instance var uiGameplayInstance = Instantiate(uiGameplayPrefab, Vector3.zero, Quaternion.identity); // Ensure the instance has the necessary components if (uiGameplayInstance.minimapImage != null) { if (map.mapMinimapImage != null) { // Assign the map's sprite to the minimap uiGameplayInstance.minimapImage.sprite = map.mapMinimapImage; } else { // Assign the unlockedSprite if map's sprite is null uiGameplayInstance.minimapImage.sprite = uiGameplayInstance.unlockedSprite; } } else { Debug.LogWarning("MinimapImage is not set in the UIGameplay passEntryPrefab."); } } CharacterData selectedCharacter = GameInstance.Singleton.GetCharacterData(); int skinIndex = PlayerSave.GetCharacterSkin(selectedCharacter.characterId); character.SetCharacterData(selectedCharacter,skinIndex); } else { // Log an error if the character passEntryPrefab is not set Debug.LogError("CharacterPrefab is not set in the GameInstance.Singleton."); } } /// /// Determines if the map is unlocked based on PlayerPrefs or default settings in the ScriptableObject. /// /// The ID of the map. /// True if the map is unlocked, false otherwise. private bool IsMapUnlocked(int mapId) { // Check if the map is marked as unlocked in the editor foreach (var map in GameInstance.Singleton.mapInfoData) { if (map.mapId == mapId && map.isUnlocked) { return true; } } // If not, check if it is unlocked in the player's saved data return PlayerSave.GetUnlockedMaps().Contains(mapId); } private CharacterData GetCharacterDataById(int characterId) { if (GameInstance.Singleton == null || GameInstance.Singleton.characterData == null) return null; foreach (CharacterData item in GameInstance.Singleton.characterData) if (item.characterId == characterId) return item; return null; } /// /// Initiates the process of returning to the main menu by loading the main menu scene. /// public void ReturnToMainMenu() { StartCoroutine(LoadMainMenuAndLoadPlayerInfo()); } /// /// Initiates the process of loading a specific scene. /// /// The name of the scene to load. public void LoadSpecificScene(string scene) { StartCoroutine(LoadAsyncSpecificScene(scene)); } /// /// Coroutine that loads the main menu scene asynchronously and, /// once the scene is fully loaded, it calls the method to load the player's information. /// Uses the LoadingManager to display the loading screen during the process. /// /// An IEnumerator used for coroutine execution. private IEnumerator LoadMainMenuAndLoadPlayerInfo() { yield return LoadingManager.Singleton.LoadSceneWithLoadingScreen(GameInstance.Singleton.mainMenuScene); // After the scene is loaded, call the method to load player information UIMainMenu.Singleton.LoadPlayerInfo(); } /// /// Coroutine that loads a specific scene asynchronously. /// Uses the LoadingManager to display the loading screen during the process. /// /// The name of the scene to load. /// An IEnumerator used for coroutine execution. private IEnumerator LoadAsyncSpecificScene(string scene) { yield return LoadingManager.Singleton.LoadSceneWithLoadingScreen(scene); } #endregion } }