274 lines
10 KiB
C#

using Cysharp.Threading.Tasks;
using System.Collections;
using UnityEngine;
namespace BulletHellTemplate
{
/// <summary>
/// Manages the game maps, including loading scenes, spawning characters,
/// and unlocking new maps upon game completion.
/// </summary>
public class GameManager : MonoBehaviour
{
#region Public Fields
/// <summary>
/// Singleton reference to the GameManager.
/// </summary>
public static GameManager Singleton;
#endregion
#region Private Fields
/// <summary>
/// Stores the ID of the current map being played.
/// </summary>
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
/// <summary>
/// Starts the game with the specified MapId and stores the current map ID.
/// </summary>
/// <param name="mapId">The ID of the map to load.</param>
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.");
}
/// <summary>
/// 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.
/// </summary>
/// <param name="won">Whether the player won the game.</param>
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);
}
/// <summary>
/// Retrieves the ID of the current map being played.
/// </summary>
/// <returns>The ID of the current map.</returns>
public int GetCurrentMapId()
{
return currentMapId;
}
#endregion
#region Private Methods
/// <summary>
/// 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.
/// </summary>
/// <param name="map">The map data containing the scene to be loaded.</param>
/// <returns>An IEnumerator used for coroutine execution.</returns>
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);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="map">The map data used for setting the minimap image if available.</param>
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.");
}
}
/// <summary>
/// Determines if the map is unlocked based on PlayerPrefs or default settings in the ScriptableObject.
/// </summary>
/// <param name="mapId">The ID of the map.</param>
/// <returns>True if the map is unlocked, false otherwise.</returns>
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;
}
/// <summary>
/// Initiates the process of returning to the main menu by loading the main menu scene.
/// </summary>
public void ReturnToMainMenu()
{
StartCoroutine(LoadMainMenuAndLoadPlayerInfo());
}
/// <summary>
/// Initiates the process of loading a specific scene.
/// </summary>
/// <param name="scene">The name of the scene to load.</param>
public void LoadSpecificScene(string scene)
{
StartCoroutine(LoadAsyncSpecificScene(scene));
}
/// <summary>
/// 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.
/// </summary>
/// <returns>An IEnumerator used for coroutine execution.</returns>
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();
}
/// <summary>
/// Coroutine that loads a specific scene asynchronously.
/// Uses the LoadingManager to display the loading screen during the process.
/// </summary>
/// <param name="scene">The name of the scene to load.</param>
/// <returns>An IEnumerator used for coroutine execution.</returns>
private IEnumerator LoadAsyncSpecificScene(string scene)
{
yield return LoadingManager.Singleton.LoadSceneWithLoadingScreen(scene);
}
#endregion
}
}