using System.Collections.Generic; using UnityEngine; using Colosseum.Player; namespace Colosseum.Player { /// /// 플레이어 관전 시스템. /// 사망한 플레이어가 살아있는 플레이어를 관찰할 수 있게 합니다. /// public class PlayerSpectator : MonoBehaviour { [Header("References")] [Tooltip("PlayerCamera 컴포넌트")] [SerializeField] private PlayerCamera playerCamera; [Tooltip("PlayerNetworkController 컴포넌트")] [SerializeField] private PlayerNetworkController networkController; [Header("Spectate Settings")] [Tooltip("관전 대상 전환 키")] [SerializeField] private KeyCode nextTargetKey = KeyCode.Tab; [Tooltip("관전 UI 표시 여부")] [SerializeField] private bool showSpectateUI = true; // 관전 상태 private bool isSpectating = false; private int currentSpectateIndex = 0; private List alivePlayers = new List(); // 이벤트 public event System.Action OnSpectateModeChanged; // (isSpectating) public event System.Action OnSpectateTargetChanged; // Properties public bool IsSpectating => isSpectating; public PlayerNetworkController CurrentTarget => alivePlayers.Count > currentSpectateIndex ? alivePlayers[currentSpectateIndex] : null; private void Awake() { // 컴포넌트 자동 참조 if (playerCamera == null) playerCamera = GetComponent(); if (networkController == null) networkController = GetComponentInParent(); } private void Start() { if (networkController != null) { networkController.OnDeathStateChanged += HandleDeathStateChanged; } } private void OnDestroy() { if (networkController != null) { networkController.OnDeathStateChanged -= HandleDeathStateChanged; } } private void Update() { if (!isSpectating) return; // Tab 키로 다음 관전 대상 전환 if (Input.GetKeyDown(nextTargetKey)) { CycleToNextTarget(); } } private void HandleDeathStateChanged(bool dead) { if (dead) { StartSpectating(); } else { StopSpectating(); } } /// /// 관전 모드 시작 /// private void StartSpectating() { // 살아있는 플레이어 목록 갱신 RefreshAlivePlayers(); if (alivePlayers.Count == 0) { // 관전할 플레이어가 없음 (게임 오버) Debug.Log("[PlayerSpectator] No alive players to spectate"); return; } isSpectating = true; currentSpectateIndex = 0; // 첫 번째 살아있는 플레이어 관전 SetSpectateTarget(alivePlayers[currentSpectateIndex]); OnSpectateModeChanged?.Invoke(true); Debug.Log($"[PlayerSpectator] Started spectating. {alivePlayers.Count} players alive."); } /// /// 관전 모드 종료 /// private void StopSpectating() { isSpectating = false; alivePlayers.Clear(); currentSpectateIndex = 0; // 원래 플레이어로 카메라 복귀 if (playerCamera != null && networkController != null) { playerCamera.ResetToPlayer(networkController.transform); } OnSpectateModeChanged?.Invoke(false); Debug.Log("[PlayerSpectator] Stopped spectating"); } /// /// 살아있는 플레이어 목록 갱신 /// private void RefreshAlivePlayers() { alivePlayers.Clear(); var allPlayers = FindObjectsByType(FindObjectsSortMode.None); foreach (var player in allPlayers) { // 자신이 아니고, 살아있는 플레이어만 추가 if (player != networkController && !player.IsDead) { alivePlayers.Add(player); } } } /// /// 다음 관전 대상으로 전환 /// private void CycleToNextTarget() { if (alivePlayers.Count == 0) return; // 목록 갱신 (중간에 사망했을 수 있음) RefreshAlivePlayers(); if (alivePlayers.Count == 0) { Debug.Log("[PlayerSpectator] No more alive players"); return; } currentSpectateIndex = (currentSpectateIndex + 1) % alivePlayers.Count; SetSpectateTarget(alivePlayers[currentSpectateIndex]); } /// /// 관전 대상 설정 /// private void SetSpectateTarget(PlayerNetworkController target) { if (target == null || playerCamera == null) return; playerCamera.SetTarget(target.transform); OnSpectateTargetChanged?.Invoke(target); Debug.Log($"[PlayerSpectator] Now spectating: Player {target.OwnerClientId}"); } /// /// 현재 관전 대상 이름 반환 (UI용) /// public string GetCurrentTargetName() { var target = CurrentTarget; if (target == null) return "None"; return $"Player {target.OwnerClientId}"; } } }