using System.Collections.Generic; using Colosseum.Network; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.UI; namespace Colosseum.UI { /// /// 로비 씬 UI 제어. /// 두 패널 구조: /// connectPanel — IP/포트 입력 후 Host 또는 Join /// lobbyPanel — 대기실 (플레이어 목록, 준비, 게임 시작) /// public class LobbyUI : MonoBehaviour { // ─── Connect Panel ──────────────────────────────────────── [Header("Connect Panel")] [SerializeField] private GameObject connectPanel; [SerializeField] private TMP_InputField ipInput; [SerializeField] private TMP_InputField portInput; [SerializeField] private Button hostButton; [SerializeField] private Button joinButton; [SerializeField] private TextMeshProUGUI connectStatusText; // ─── Lobby Panel ────────────────────────────────────────── [Header("Lobby Panel")] [SerializeField] private GameObject lobbyPanel; [SerializeField] private Transform playerListParent; [SerializeField] private GameObject playerSlotPrefab; // TextMeshProUGUI 하나 포함 [SerializeField] private Button readyButton; [SerializeField] private Button startButton; // 호스트만 표시 [SerializeField] private Button disconnectButton; // ─── 내부 ───────────────────────────────────────────────── private readonly List _slots = new(); private bool _isReady; // ─── 초기화 ─────────────────────────────────────────────── private void Start() { ipInput.text = "127.0.0.1"; portInput.text = "7777"; hostButton.onClick.AddListener(OnHostClicked); joinButton.onClick.AddListener(OnJoinClicked); readyButton.onClick.AddListener(OnReadyClicked); startButton.onClick.AddListener(OnStartClicked); disconnectButton.onClick.AddListener(OnDisconnectClicked); ShowConnectPanel(); } private void OnEnable() { if (NetworkManager.Singleton == null) return; NetworkManager.Singleton.OnClientConnectedCallback += OnConnectionEvent; NetworkManager.Singleton.OnClientDisconnectCallback += OnConnectionEvent; } private void OnDisable() { if (NetworkManager.Singleton != null) { NetworkManager.Singleton.OnClientConnectedCallback -= OnConnectionEvent; NetworkManager.Singleton.OnClientDisconnectCallback -= OnConnectionEvent; } } // ─── Connect Panel 버튼 ─────────────────────────────────── private void OnHostClicked() { if (!TryGetAddress(out string ip, out ushort port)) return; LobbyManager.Instance.StartHost(ip, port); LobbyManager.Instance.OnPlayersChanged += RefreshPlayerList; ShowLobbyPanel(); } private void OnJoinClicked() { if (!TryGetAddress(out string ip, out ushort port)) return; SetConnectStatus("Connecting..."); joinButton.interactable = false; hostButton.interactable = false; LobbyManager.Instance.StartClient(ip, port); // 연결 성공/실패 대기 NetworkManager.Singleton.OnClientConnectedCallback += OnJoinSuccess; NetworkManager.Singleton.OnTransportFailure += OnJoinFailed; } private void OnJoinSuccess(ulong clientId) { if (clientId != NetworkManager.Singleton.LocalClientId) return; NetworkManager.Singleton.OnClientConnectedCallback -= OnJoinSuccess; NetworkManager.Singleton.OnTransportFailure -= OnJoinFailed; LobbyManager.Instance.OnPlayersChanged += RefreshPlayerList; ShowLobbyPanel(); } private void OnJoinFailed() { NetworkManager.Singleton.OnClientConnectedCallback -= OnJoinSuccess; NetworkManager.Singleton.OnTransportFailure -= OnJoinFailed; SetConnectStatus("Connection failed. Check IP/Port."); joinButton.interactable = true; hostButton.interactable = true; } // ─── Lobby Panel 버튼 ───────────────────────────────────── private void OnReadyClicked() { _isReady = !_isReady; LobbyManager.Instance.SetReadyRpc(_isReady); readyButton.GetComponentInChildren().text = _isReady ? "Ready!" : "Ready"; } private void OnStartClicked() { LobbyManager.Instance.StartGame(); } private void OnDisconnectClicked() { LobbyManager.Instance.OnPlayersChanged -= RefreshPlayerList; LobbyManager.Instance.Disconnect(); _isReady = false; ShowConnectPanel(); } // ─── 패널 전환 ──────────────────────────────────────────── private void ShowConnectPanel() { connectPanel.SetActive(true); lobbyPanel.SetActive(false); SetConnectStatus(""); hostButton.interactable = true; joinButton.interactable = true; } private void ShowLobbyPanel() { connectPanel.SetActive(false); lobbyPanel.SetActive(true); bool isHost = NetworkManager.Singleton.IsHost; startButton.gameObject.SetActive(isHost); readyButton.gameObject.SetActive(!isHost); RefreshPlayerList(); } // ─── 플레이어 목록 갱신 ─────────────────────────────────── private void OnConnectionEvent(ulong _) => RefreshPlayerList(); private void RefreshPlayerList() { if (LobbyManager.Instance == null) return; // 슬롯 수 맞추기 int count = LobbyManager.Instance.PlayerCount; while (_slots.Count < count) { var slot = Instantiate(playerSlotPrefab, playerListParent); _slots.Add(slot); } while (_slots.Count > count) { Destroy(_slots[^1]); _slots.RemoveAt(_slots.Count - 1); } for (int i = 0; i < count; i++) { var data = LobbyManager.Instance.GetPlayer(i); var label = _slots[i].GetComponentInChildren(); if (label != null) label.text = $"{data.PlayerName} {(data.IsReady ? "[Ready]" : "")}"; } // 호스트: 모두 준비됐을 때만 시작 활성화 if (NetworkManager.Singleton.IsHost) { bool allReady = AllPlayersReady(); startButton.interactable = allReady && count >= 1; } } private bool AllPlayersReady() { if (LobbyManager.Instance == null) return false; int count = LobbyManager.Instance.PlayerCount; for (int i = 0; i < count; i++) { // 호스트 본인은 준비 체크 면제 if (LobbyManager.Instance.GetPlayer(i).ClientId == NetworkManager.Singleton.LocalClientId) continue; if (!LobbyManager.Instance.GetPlayer(i).IsReady) return false; } return count > 0; } // ─── 유틸 ───────────────────────────────────────────────── private bool TryGetAddress(out string ip, out ushort port) { ip = ipInput.text.Trim(); if (string.IsNullOrEmpty(ip)) ip = "127.0.0.1"; if (!ushort.TryParse(portInput.text.Trim(), out port)) { port = 7777; SetConnectStatus("Invalid port number."); return false; } return true; } private void SetConnectStatus(string msg) { if (connectStatusText != null) connectStatusText.text = msg; } } }