using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; using Unity.Netcode; using Colosseum.Player; using Colosseum.Enemy; namespace Colosseum { /// /// 게임 상태 열거형 /// public enum GameState { Waiting, // 대기 중 Playing, // 게임 진행 중 GameOver, // 게임 오버 Victory // 승리 } /// /// 게임 전체를 관리하는 매니저. /// 게임 상태, 플레이어 사망 체크, 승리/패배 조건을 처리합니다. /// public class GameManager : NetworkBehaviour { [Header("UI Prefabs")] [Tooltip("게임 오버 UI 프리팹")] [SerializeField] private GameObject gameOverUIPrefab; [Tooltip("승리 UI 프리팹")] [SerializeField] private GameObject victoryUIPrefab; [Tooltip("승리 연출 이펙트 프리팹")] [SerializeField] private GameObject victoryEffectPrefab; [Header("Settings")] [Tooltip("게임 오버 후 재시작까지 대기 시간")] [SerializeField] private float gameOverRestartDelay = 5f; [Tooltip("승리 후 로비로 이동까지 대기 시간")] [SerializeField] private float victoryToLobbyDelay = 5f; [Header("Debug")] [SerializeField] private bool debugMode = true; // 싱글톤 public static GameManager Instance { get; private set; } // 게임 상태 private NetworkVariable currentState = new NetworkVariable(GameState.Waiting); // 인스턴스화된 UI private GameObject gameOverUIInstance; private GameObject victoryUIInstance; private GameObject victoryEffectInstance; // 이벤트 public event Action OnGameStateChanged; public event Action OnGameOver; public event Action OnVictory; // Properties public GameState CurrentState => currentState.Value; public bool IsGameOver => currentState.Value == GameState.GameOver; public bool IsVictory => currentState.Value == GameState.Victory; private void Awake() { // 싱글톤 설정 if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; } public override void OnNetworkSpawn() { currentState.OnValueChanged += HandleGameStateChanged; // 네트워크 씬 로드 이벤트 구독 if (NetworkManager.Singleton.SceneManager != null) { NetworkManager.Singleton.SceneManager.OnLoadEventCompleted += OnSceneLoadCompleted; } // UI 인스턴스화 (모든 클라이언트에서) SpawnUI(); if (IsServer) { // 플레이어 사망 이벤트 구독 StartCoroutine(WaitForPlayersAndSubscribe()); // 보스 사망 이벤트 구독 SubscribeToBossEvents(); } } private void OnSceneLoadCompleted(string sceneName, LoadSceneMode loadSceneMode, List clientsCompleted, List clientsTimedOut) { if (loadSceneMode == LoadSceneMode.Single) { if (debugMode) { Debug.Log($"[GameManager] Scene loaded: {sceneName}"); } // 씬 로드 완료 시 플레이어 리스폰 if (IsServer) { RespawnAllPlayersClientRpc(); } } } [Rpc(SendTo.ClientsAndHost)] private void RespawnAllPlayersClientRpc() { // 모든 플레이어 리스폰 var players = FindObjectsByType(FindObjectsSortMode.None); foreach (var player in players) { player.Respawn(); } // 카메라 재설정 var playerMovement = FindObjectsByType(FindObjectsSortMode.None); foreach (var movement in playerMovement) { movement.RefreshCamera(); } } public override void OnNetworkDespawn() { currentState.OnValueChanged -= HandleGameStateChanged; // 네트워크 씬 로드 이벤트 구독 해제 if (NetworkManager.Singleton.SceneManager != null) { NetworkManager.Singleton.SceneManager.OnLoadEventCompleted -= OnSceneLoadCompleted; } // UI 정리 CleanupUI(); if (IsServer) { UnsubscribeFromPlayerEvents(); UnsubscribeFromBossEvents(); } } #region UI Management /// /// UI 프리팹 인스턴스화 /// private void SpawnUI() { // Canvas 찾기 또는 생성 Canvas canvas = FindOrCreateCanvas(); // 게임 오버 UI if (gameOverUIPrefab != null) { gameOverUIInstance = Instantiate(gameOverUIPrefab, canvas.transform); gameOverUIInstance.name = "GameOverUI"; gameOverUIInstance.SetActive(false); if (debugMode) { Debug.Log("[GameManager] GameOverUI instantiated"); } } // 승리 UI if (victoryUIPrefab != null) { victoryUIInstance = Instantiate(victoryUIPrefab, canvas.transform); victoryUIInstance.name = "VictoryUI"; victoryUIInstance.SetActive(false); if (debugMode) { Debug.Log("[GameManager] VictoryUI instantiated"); } } // 승리 연출 이펙트 if (victoryEffectPrefab != null) { victoryEffectInstance = Instantiate(victoryEffectPrefab); victoryEffectInstance.name = "VictoryEffect"; victoryEffectInstance.SetActive(false); if (debugMode) { Debug.Log("[GameManager] VictoryEffect instantiated"); } } } /// /// Canvas 찾기 또는 생성 /// private Canvas FindOrCreateCanvas() { // 기존 Canvas 찾기 Canvas canvas = FindFirstObjectByType(); if (canvas == null) { // 새 Canvas 생성 var canvasObject = new GameObject("GameUI Canvas"); canvas = canvasObject.AddComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvasObject.AddComponent(); canvasObject.AddComponent(); if (debugMode) { Debug.Log("[GameManager] Created new Canvas"); } } return canvas; } /// /// UI 정리 /// private void CleanupUI() { if (gameOverUIInstance != null) { Destroy(gameOverUIInstance); } if (victoryUIInstance != null) { Destroy(victoryUIInstance); } if (victoryEffectInstance != null) { Destroy(victoryEffectInstance); } } #endregion private void HandleGameStateChanged(GameState oldValue, GameState newValue) { OnGameStateChanged?.Invoke(newValue); // UI 활성화/비활성화 UpdateUIVisibility(newValue); if (debugMode) { Debug.Log($"[GameManager] State changed: {oldValue} -> {newValue}"); } } private void UpdateUIVisibility(GameState state) { // 게임 오버 UI if (gameOverUIInstance != null) { gameOverUIInstance.SetActive(state == GameState.GameOver); } // 승리 UI if (victoryUIInstance != null) { victoryUIInstance.SetActive(state == GameState.Victory); } // 승리 연출 if (victoryEffectInstance != null && state == GameState.Victory) { victoryEffectInstance.SetActive(true); } } #region Player Death Tracking private List alivePlayers = new List(); private IEnumerator WaitForPlayersAndSubscribe() { // 플레이어들이 스폰될 때까지 대기 yield return new WaitForSeconds(1f); SubscribeToPlayerEvents(); // 게임 시작 SetGameState(GameState.Playing); } private void SubscribeToPlayerEvents() { var players = FindObjectsByType(FindObjectsSortMode.None); foreach (var player in players) { player.OnDeath += HandlePlayerDeath; if (!player.IsDead) { alivePlayers.Add(player); } } if (debugMode) { Debug.Log($"[GameManager] Subscribed to {players.Length} players, {alivePlayers.Count} alive"); } } private void UnsubscribeFromPlayerEvents() { var players = FindObjectsByType(FindObjectsSortMode.None); foreach (var player in players) { player.OnDeath -= HandlePlayerDeath; } alivePlayers.Clear(); } private void HandlePlayerDeath(PlayerNetworkController player) { alivePlayers.Remove(player); if (debugMode) { Debug.Log($"[GameManager] Player died. Alive: {alivePlayers.Count}"); } // 모든 플레이어 사망 체크 if (alivePlayers.Count == 0) { TriggerGameOver(); } } #endregion #region Boss Death Tracking private void SubscribeToBossEvents() { BossEnemy.OnBossSpawned += HandleBossSpawned; // 이미 스폰된 보스가 있는지 확인 if (BossEnemy.ActiveBoss != null) { SubscribeToBossDeath(BossEnemy.ActiveBoss); } } private void UnsubscribeFromBossEvents() { BossEnemy.OnBossSpawned -= HandleBossSpawned; } private void HandleBossSpawned(BossEnemy boss) { SubscribeToBossDeath(boss); if (debugMode) { Debug.Log($"[GameManager] Boss spawned: {boss.name}"); } } private void SubscribeToBossDeath(BossEnemy boss) { boss.OnDeath += HandleBossDeath; } private void HandleBossDeath() { if (debugMode) { Debug.Log("[GameManager] Boss died!"); } TriggerVictory(); } #endregion #region Game State Management private void SetGameState(GameState newState) { if (!IsServer) return; currentState.Value = newState; } /// /// 게임 오버 처리 (서버에서만 실행) /// public void TriggerGameOver() { if (!IsServer || currentState.Value != GameState.Playing) return; SetGameState(GameState.GameOver); OnGameOver?.Invoke(); if (debugMode) { Debug.Log("[GameManager] Game Over!"); } // N초 후 씬 재시작 StartCoroutine(RestartSceneAfterDelay(gameOverRestartDelay)); } /// /// 승리 처리 (서버에서만 실행) /// public void TriggerVictory() { if (!IsServer || currentState.Value != GameState.Playing) return; SetGameState(GameState.Victory); OnVictory?.Invoke(); if (debugMode) { Debug.Log("[GameManager] Victory!"); } // N초 후 씬 재시작 (또는 로비로 이동) StartCoroutine(RestartSceneAfterDelay(victoryToLobbyDelay)); } private IEnumerator RestartSceneAfterDelay(float delay) { yield return new WaitForSeconds(delay); // 현재 씬 다시 로드 string currentScene = SceneManager.GetActiveScene().name; if (IsServer) { // 네트워크 씬 관리 사용 NetworkManager.Singleton.SceneManager.LoadScene(currentScene, LoadSceneMode.Single); } } #endregion #region Utility /// /// 살아있는 플레이어 목록 반환 /// public List GetAlivePlayers() { return alivePlayers.Where(p => p != null && !p.IsDead).ToList(); } /// /// 랜덤한 살아있는 플레이어 반환 (관전용) /// public PlayerNetworkController GetRandomAlivePlayer() { var alive = GetAlivePlayers(); if (alive.Count == 0) return null; return alive[UnityEngine.Random.Range(0, alive.Count)]; } #endregion } }