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