-
Photon - Demo 탐색Unity 2023. 10. 25. 23:58
I. 유니티 내 구성
1. TopPanel
A. LobbyTopPanel.cs
namespace Photon.Pun.Demo.Asteroids { public class LobbyTopPanel : MonoBehaviour { private readonly string connectionStatusMessage = " Connection Status: "; [Header("UI References")] public Text ConnectionStatusText; #region UNITY public void Update() { ConnectionStatusText.text = connectionStatusMessage + PhotonNetwork.NetworkClientState; } #endregion } }
ConnectionStatusText
: ConnectionStatus 오브젝트의 컴포넌트 중 Text 컴포넌트
PhotonNetWork.NetworkClientState
: 현재 클라이언트의 네트워크 상태를 반환함.
public static ClientState NetworkClientState { get { if (OfflineMode) { return (offlineModeRoom != null) ? ClientState.Joined : ClientState.ConnectedToMasterServer; } if (NetworkingClient == null) { return ClientState.Disconnected; } return NetworkingClient.State; } }
2. MainPanel
A. LobbyMainPanel.cs
a. Awake()
public void Awake() { PhotonNetwork.AutomaticallySyncScene = true; cachedRoomList = new Dictionary<string, RoomInfo>(); roomListEntries = new Dictionary<string, GameObject>(); PlayerNameInput.text = "Player " + Random.Range(1000, 10000); }
PhotonNetWork.AutomaticallySyncScene
: 방 안의 클라이언트가 마스터 클라이언트와 동일한 씬을 자동으로 로드해야하는 지 여부를 결정
: 해당 bool을 true로 한다면, 이후에도 모든 클라이언트들은 마스터 클라이언트와 같은 씬을 로드하게 됨.
딕셔너리 사용 이유
: 각 방의 이름을 키로 사용하여 해당 방의 정보를 효율적으로 관리할 수 있기 때문에, 리스트보다 딕셔너리로 관리하는 것이 유리함.
cachedRoomList
: 게임 내에서 방의 상태를 추적하고 유지하기 위한 데이터를 담은 딕셔너리.
* 캐싱 : 데이터나 계산 결과를 이후에 더 빠르게 액세스할 수 있도록 메모리에 보관하는 것.
roomListEntries
: UI에 표시되는 방 목록을 관리하는 데 사용하는 딕셔너리
b. OnConnectedToMaster()
public override void OnConnectedToMaster() // 마스터 접속을 했을 때. { this.SetActivePanel(SelectionPanel.name); }
PunClasses.OnConnectedToMaster()
public virtual void OnConnectedToMaster() { }
: OnConnectedToMaster() 은 PunClasses의 OnConnectedToMaster()을 오버라이드함.
SetActivePanel()
: 파라미터로 받은 activePanel 이 각 패널(여기서는 게임오브젝트로 연결함)의 이름과 같다면, 해당 패널을 활성화시킴.
private void SetActivePanel(string activePanel) { LoginPanel.SetActive(activePanel.Equals(LoginPanel.name)); SelectionPanel.SetActive(activePanel.Equals(SelectionPanel.name)); CreateRoomPanel.SetActive(activePanel.Equals(CreateRoomPanel.name)); JoinRandomRoomPanel.SetActive(activePanel.Equals(JoinRandomRoomPanel.name)); RoomListPanel.SetActive(activePanel.Equals(RoomListPanel.name)); // UI should call OnRoomListButtonClicked() to activate this InsideRoomPanel.SetActive(activePanel.Equals(InsideRoomPanel.name)); }
c. OnRoomListUpdate()
: 현재 Peer에 존재하는 방을 관리하여 보여줌.
=> 플레이가 진행중인 방은 삭제
public override void OnRoomListUpdate(List<RoomInfo> roomList) { ClearRoomListView(); UpdateCachedRoomList(roomList); UpdateRoomListView(); }
ClearRoomListView()
: UI에 표시된 ( = roomListEntries 에 있는) 모든 오브젝트를 다 파괴한다.
=> 방 정보를 가진 프리팹들을 모두 파괴
private void ClearRoomListView() { foreach (GameObject entry in roomListEntries.Values) { Destroy(entry.gameObject); } roomListEntries.Clear(); }
=> RoomListEntry.cs
: roomListPanel 에서 존재하고 있는 room의 정보를 보여줌.
: 각각의 room에 대한 정보를 roomListEntries에서 획득하여, Initailize()로 RoomListEntry 프리팹을 instantiate 함.
: 해당 방의 JoinRoomButton을 클릭하는 이벤트가 발생할 시, 해당 방으로 이동하는 JoinRoom 메서드를 연결시킴.
public class RoomListEntry : MonoBehaviour { public Text RoomNameText; public Text RoomPlayersText; public Button JoinRoomButton; private string roomName; public void Start() { JoinRoomButton.onClick.AddListener(() => { if (PhotonNetwork.InLobby) { PhotonNetwork.LeaveLobby(); } PhotonNetwork.JoinRoom(roomName); }); } public void Initialize(string name, byte currentPlayers, byte maxPlayers) { roomName = name; RoomNameText.text = name; RoomPlayersText.text = currentPlayers + " / " + maxPlayers; } }
UpdateCachedRoomList()
: 캐싱한 데이터를 통해 ( = chachedRoomList 에 있는 키값을 기준으로), roomSelectPanel 에서 현재 선택 가능한 룸의 정보를 관리함.
: RoomInfo 클래스를 만들어, 룸의 정보를 관리하기 위한 필드의 상태에 따라 관리.
private void UpdateCachedRoomList(List<RoomInfo> roomList) { foreach (RoomInfo info in roomList) { // Remove room from cached room list if it got closed, became invisible or was marked as removed if (!info.IsOpen || !info.IsVisible || info.RemovedFromList) { if (cachedRoomList.ContainsKey(info.Name)) { cachedRoomList.Remove(info.Name); } continue; } // Update cached room info if (cachedRoomList.ContainsKey(info.Name)) { cachedRoomList[info.Name] = info; } // Add new room info to cache else { cachedRoomList.Add(info.Name, info); } } }
UpdateRoomListView()
: 현재 Peer에 존재하는 모든 방을 보여줌.
: RoomListEntryPrefab을 인스턴스화 하고, RoomListPanel의 content에 추가함.
private void UpdateRoomListView() { foreach (RoomInfo info in cachedRoomList.Values) { GameObject entry = Instantiate(RoomListEntryPrefab); entry.transform.SetParent(RoomListContent.transform); entry.transform.localScale = Vector3.one; entry.GetComponent<RoomListEntry>().Initialize(info.Name, (byte)info.PlayerCount, (byte)info.MaxPlayers); roomListEntries.Add(info.Name, entry); } }
=> RoomListContent
: RoomListPanel에서 Vertical Layout Group 컴포넌트의 요소 중, Content 오브젝트.
=> Room 에 대한 정보가 존재할 시에, RoomListEntry 프리팹이 자식 오브젝트로써 추가된다.
d. OnJoinedLobby()
: 플레이어가 로비에 참여할 때, 이전에 캐시된 룸 데이터 (chacedRoomList)를 삭제하고, UI에서 보이는 룸의 오브젝트를 삭제하는 콜백 함수.
public override void OnJoinedLobby() { // whenever this joins a new lobby, clear any previous room lists cachedRoomList.Clear(); ClearRoomListView(); }
MonoBehaviorPunCallBacks.OnJoinedLobby()
: 마스터 서버의 로비에 들어갈 때 호출
public virtual void OnJoinedLobby() { }
e. OnLeftLobby()
: 플레이어가 로비에서 나갈 때, 이전에 캐시된 룸 데이터를 삭제하고, UI에서 보이는 룸의 오브젝트를 삭제하는 콜백 함수
public override void OnLeftLobby() { cachedRoomList.Clear(); ClearRoomListView(); }
MonoBehaviorPunCallBacks.OnLeftLobby()
: 마스터 서버의 로비에서 나갈 때 호출
public virtual void OnLeftLobby() { }
ㅡ
f. OnCreatedRoomFailed()
: 방 만들기에 실패했을 때, SelectionPanel을 활성화하는 콜백 함수
public override void OnCreateRoomFailed(short returnCode, string message) { SetActivePanel(SelectionPanel.name); }
MonoBehaviorPunCallBacks.OnCreatedRoomFailed()
: 방 만들기에 실패했을 때 호출
public virtual void OnCreateRoomFailed(short returnCode, string message) { }
g. OnJoinedRoomFailed()
: 방 참가에 실패했을 때, SelectionPanel을 활성화하는 콜백 함수
public override void OnJoinRoomFailed(short returnCode, string message) { SetActivePanel(SelectionPanel.name); }
MonoBehaviorPunCallBacks.OnJoinedRoomFailed()
: 방 참가에 실패했을 때 호출
public virtual void OnJoinRoomFailed(short returnCode, string message) { }
h. OnJoinRandomFailed()
: 랜덤 방 참가에 실패했을 경우, 새로운 방을 만들어 입장하는 콜백 함수
public override void OnJoinRandomFailed(short returnCode, string message) { string roomName = "Room " + Random.Range(1000, 10000); RoomOptions options = new RoomOptions {MaxPlayers = 2}; PhotonNetwork.CreateRoom(roomName, options, null); }
RoomOptions
public class RoomOptions { public bool IsVisible { get { return this.isVisible; } set { this.isVisible = value; } } private bool isVisible = true; public bool IsOpen { get { return this.isOpen; } set { this.isOpen = value; } } private bool isOpen = true; public int MaxPlayers; public int PlayerTtl; public int EmptyRoomTtl; public bool CleanupCacheOnLeave { get { return this.cleanupCacheOnLeave; } set { this.cleanupCacheOnLeave = value; } } private bool cleanupCacheOnLeave = true; public Hashtable CustomRoomProperties; public string[] CustomRoomPropertiesForLobby = new string[0]; public string[] Plugins; public bool SuppressRoomEvents { get; set; } public bool SuppressPlayerInfo { get; set; } public bool PublishUserId { get; set; } public bool DeleteNullProperties { get; set; } public bool BroadcastPropsChangeToAll { get { return this.broadcastPropsChangeToAll; } set { this.broadcastPropsChangeToAll = value; } } private bool broadcastPropsChangeToAll = true; }
- IsVisible
: 방이 노출되는가?
: 로비의 리스트에 방이 포함되어 있는가?
- IsOpen
: 방에 인원이 참가가 가능한가?
- MaxPlayers
: 방 참가 가능 최대 인원 수
- PlayerTtl
: 플레이어가 방에서 나간 후, 액터(플레이어의 정보)가 방에서 체류할 수 있는 시간
: TTL 시간 동안, 플레이어가 다시 들어온다면, 재연결 혹은 재참여가 가능하다.
: 단위는 밀리초
- EmptyRoomTtl
: 마지막 플레이어가 방에서 나간 후, 방의 정보가 로비에서 체류할 수 있는 시간
: 단위는 밀리초
- CleanUpCacheOnLeave
: 유저가 방에서 떠날 때, 유저의 이벤트와 프로퍼티를 방에서 삭제하는가에 대한 여부
- CustomProperties
: 추가적으로 HashTable 형식의 "key", "value" 형식의 데이터를 방이나 플레이어에 붙여서 사용할 수 있는 기능.
public override void OnConnectedToMaster() { var roomOption = new RoomOptions() roomOption.CustomRoomProperties = new HashTable() { { "Type", "대장전" } }; PhotonNetWork.JoinOrCreateRoom("Room", roomOption, null); } public override void OnJoinedRoom() { HashTable CP = PhotonNetwork.CurrentRoom.CustomProperties; switch (CP["Type"]) { case "섬멸전": Debug.Log("섬멸전을 선택하셨습니다."); break; case "대장전": Debug.Log("대장전을 선택하셨습니다."); break; } // 마스터 클라이언트일 시, 방장임을 알리는 커스텀 프로퍼티 설정 if (PhotonNetwork.IsMasterClient) { PhotonNetwork.LocalPlayer.SetCustomProperties(new Hashtable { {"IsAdmin", "Admin"} }; } } public void OnTypeEleminationSelected() { if (PhotonNetwork.LocalPlayer.CustomProperties["IsAdmin"] == "Admin") { HashTable CP = PhotonNetwork.CurrentRoom.CustomProperties; CP["Type"] = "섬멸전"; } } public void OnTypeClassicSelected() { if (PhotonNetwork.LocalPlayer.CustomProperties["IsAdmin"] == "Admin") { HashTable CP = PhotonNetwork.CurrentRoom.CustomProperties; CP["Type"] = "대장전"; } }
- SuppressRoomEvents
: 방에 참여하거나 떠나는 플레이어들의 룸 이벤트 스킵 여부
: 활성화시, 클라이언트는 룸에 있는 다른 플레이어를 알 수 없음.
- SuppressPlayerInfo
: 가입 프로세스 중에 가입된 사용자에 대한 모든 정보를 가져오는 것을 금지함.
: GetProperties 작업을 사용하여 이 정보를 가져올 수 있음.
- PublishUserId
: 룸에 있는 "published" 된 플레이어의 UserId를 정의 함.
: 친구와 게임 같이하기 기능을 구현하고 싶을 때 사용함.
PhotonNetwork.CreateRoom
: PhotonNetwork의 연결 상태 혹은 설정에 따라 방을 만들고, 만들었는 지 여부를 bool 값으로 반환함.
: 오프라인 일 경우, 오프라인 방을 만듦.
: 마스터 서버에 연결되어 있지 않은 경우, 방 생성에 실패함.
: 방을 생성하면서 방 생성을 위한 매개변수(EnterRoomParams)를 설정함.
public static bool CreateRoom(string roomName, RoomOptions roomOptions = null, TypedLobby typedLobby = null, string[] expectedUsers = null) { if (OfflineMode) { if (offlineModeRoom != null) { Debug.LogError("CreateRoom failed. In offline mode you still have to leave a room to enter another."); return false; } EnterOfflineRoom(roomName, roomOptions, true); return true; } if (NetworkingClient.Server != ServerConnection.MasterServer || !IsConnectedAndReady) { Debug.LogError("CreateRoom failed. Client is on " + NetworkingClient.Server + " (must be Master Server for matchmaking)" + (IsConnectedAndReady ? " and ready" : "but not ready for operations (State: " + NetworkingClient.State + ")") + ". Wait for callback: OnJoinedLobby or OnConnectedToMaster."); return false; } typedLobby = typedLobby ?? ((NetworkingClient.InLobby) ? NetworkingClient.CurrentLobby : null); // use given lobby, or active lobby (if any active) or none EnterRoomParams opParams = new EnterRoomParams(); opParams.RoomName = roomName; opParams.RoomOptions = roomOptions; opParams.Lobby = typedLobby; opParams.ExpectedUsers = expectedUsers; return NetworkingClient.OpCreateRoom(opParams); }
LoadbalancingPeer.EnterRoomParams
public class EnterRoomParams { public string RoomName; public RoomOptions RoomOptions; public TypedLobby Lobby; public Hashtable PlayerProperties; protected internal bool OnGameServer = true; protected internal JoinMode JoinMode; public string[] ExpectedUsers; }
'Unity' 카테고리의 다른 글
Photon - Demo 탐색 (2) (1) 2023.10.29 photon - 채팅 기능 구현 기획 (0) 2023.10.26 네트워크 게임의 이해 (0) 2023.10.24 readme 작성 (0) 2023.10.19 코루틴 (in Unity) (1) 2023.10.06