반응형
멀티를 심심하지 않게.. 대기방에서 준비를 눌러서 시작했으면 좋겠다!
데이터베이스에 저장된 닉네임 정보를 받아와서 방을 만들때 방 이름도 닉네임으로 만들어지고 접속 했을때도 닉네임으로 표시가 되도록 했다.
서로 같은 화면을 공유해야했기때문에 UI 동기화가 필요한 부분에 포톤뷰 컴포넌트를 붙여줬다.
>> NetworkManager.cs
이건 코드 전문을 가져왔고 포톤을 이용해서 멀티를 연결할 수 있게 하는 클래스이다.
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using System.Collections.Generic;
public class NetworkManager : MonoBehaviourPunCallbacks
{
private static NetworkManager _instance;
public static NetworkManager Instance { get { return _instance; } }
[SerializeField] private int mainGameSceneIndex = 2; // 씬 인덱스
[SerializeField] private GameObject roomPrefab; // 방을 표시할 프리팹
[SerializeField] private Transform roomListParent; // 방 리스트의 부모 스크롤뷰의 content에 연결하면됨
private Dictionary<string, RoomInfo> cachedRoomList = new Dictionary<string, RoomInfo>();
private Dictionary<string, GameObject> roomListEntries = new Dictionary<string, GameObject>();
private bool isConnecting = false;
void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(this.gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(this.gameObject);
PhotonNetwork.AutomaticallySyncScene = true; // 포톤 네트워크에서 씬을 자동으로 동기화하도록 설정
}
void Start()
{
ConnectToPhoton(); // 포톤 네트워크에 연결하는 메서드 호출
}
public void ConnectToPhoton()
{
if (!PhotonNetwork.IsConnected)
{
isConnecting = true;
PhotonNetwork.ConnectUsingSettings(); // 포톤 서버에 설정을 사용하여 연결 시도
}
}
public override void OnConnectedToMaster()
{
Debug.Log("마스터 서버에 연결됨");
PhotonNetwork.JoinLobby(); // 로비에 참가하도록 포톤 네트워크에 요청
}
public void RefreshRoomList()
{
ClearRoomList();
if (PhotonNetwork.IsConnectedAndReady && PhotonNetwork.InLobby)
{
TypedLobby lobby = new TypedLobby("", LobbyType.Default);
PhotonNetwork.GetCustomRoomList(lobby, ""); // 로비에서 사용자 정의 방 리스트를 가져옴
}
else
{
Debug.LogError("마스터 서버에 연결되지 않음");
}
}
private void ClearRoomList()
{
// UI에서 이미 존재하는 방 엔트리들을 모두 제거
foreach (Transform child in roomListParent)
{
Destroy(child.gameObject);
}
cachedRoomList.Clear();
roomListEntries.Clear();
}
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
Debug.Log($"현재 방 개수: {roomList.Count}개");
UpdateCachedRoomList(roomList);
UpdateRoomListView();
}
private void UpdateCachedRoomList(List<RoomInfo> roomList)
{
foreach (RoomInfo info in roomList)
{
if (!info.IsOpen || !info.IsVisible || info.RemovedFromList)
{
if (cachedRoomList.ContainsKey(info.Name))
{
cachedRoomList.Remove(info.Name);
}
continue;
}
// Update or add room info to cache
if (cachedRoomList.ContainsKey(info.Name))
{
cachedRoomList[info.Name] = info;
}
else
{
cachedRoomList.Add(info.Name, info);
}
}
}
private void UpdateRoomListView()
{
foreach (RoomInfo info in cachedRoomList.Values)
{
GameObject entry = Instantiate(roomPrefab, roomListParent);
entry.transform.localScale = Vector3.one;
RoomData roomData = entry.GetComponent<RoomData>();
if (roomData != null)
{
roomData.Initialize(info);
}
else
{
Debug.LogError("룸 데이터 초기화 오류");
}
roomListEntries.Add(info.Name, entry);
}
}
public void CreateRoom()
{
int maxPlayers = 2;
if (PhotonNetwork.IsConnectedAndReady)
{
RoomOptions roomOptions = new RoomOptions { MaxPlayers = (byte)maxPlayers }; // 방 옵션 설정
PhotonNetwork.CreateRoom(PlayerManager.Instance.LoggedInUser.nickname.ToString(), roomOptions, TypedLobby.Default); // 포톤 네트워크를 통해 방 생성
Debug.Log("방 생성 완료");
}
else
{
ConnectToPhoton();
}
}
public override void OnCreatedRoom()
{
base.OnCreatedRoom();
Debug.Log("방이 생성됨. 리스트 새로 고침...");
}
public override void OnJoinedRoom()
{
base.OnJoinedRoom();
Debug.Log("방에 입장함. 씬 로드...");
PhotonNetwork.LoadLevel(mainGameSceneIndex); // 메인 게임 씬을 로드(내가 설정해놓은 씬인덱스 변수)
}
public override void OnLeftRoom()
{
base.OnLeftRoom();
Debug.Log("로비로 돌아가서 새로 고침...");
PhotonNetwork.JoinLobby(); // 로비에 참여
}
public override void OnPlayerEnteredRoom(Player newPlayer)
{
base.OnPlayerEnteredRoom(newPlayer); // 다른 플레이어가 방에 입장했을 때 실행되는 콜백
}
public override void OnPlayerLeftRoom(Player otherPlayer)
{
base.OnPlayerLeftRoom(otherPlayer); // 다른 플레이어가 방을 떠났을 때 실행되는 콜백
}
public void JoinRoom(string roomName)
{
PhotonNetwork.JoinRoom(roomName); // 특정 방에 입장하는 메서드
}
public override void OnJoinedLobby()
{
base.OnJoinedLobby();
Debug.Log("로비에 입장함. 리스트 새로 고침...");
RefreshRoomList();
}
// 포톤 네트워크 연결 해제
public void Disconnect() => PhotonNetwork.Disconnect();
public override void OnDisconnected(DisconnectCause cause) => Debug.Log("끊김");
}
>> UIMultiMatchView.cs
- 이건 UIMultiMatchView 클래스인데 다른 코드들은 다른 기능을 포함하고 있어서 영상의 부분의 기능을 하는곳만 가져왔다.
private void Start()
{
networkManager = NetworkManager.Instance;
SetMultiplayerCharacterInfo();
}
private void SetMultiplayerCharacterInfo()
{
if (PhotonNetwork.IsConnectedAndReady)
{
// 플레이어1
if (PhotonNetwork.IsMasterClient)
{
playerName.text = PlayerManager.Instance.LoggedInUser.nickname;
photonView.RPC(nameof(SetPlayer1Info), RpcTarget.OthersBuffered, PlayerManager.Instance.LoggedInUser.nickname);
}
// 플레이어2
else if (PhotonNetwork.PlayerList.Length > 1 && !PhotonNetwork.IsMasterClient)
{
player2Name.text = PlayerManager.Instance.LoggedInUser.nickname;
photonView.RPC(nameof(SetPlayer2Info), RpcTarget.OthersBuffered, PlayerManager.Instance.LoggedInUser.nickname);
}
}
else
{
Debug.Log("포톤 연결 이상");
}
}
[PunRPC]
private void SetPlayer1Info(string nickname)
{
playerName.text = nickname;
}
[PunRPC]
private void SetPlayer2Info(string nickname)
{
player2Name.text = nickname;
}
}
기능 이미지
접속시 실시간으로 같은 화면을 공유하는 모습을 볼 수 있다.
기능 영상
오늘의 회고
코드가 더러울지도 모르지만 포톤 진짜 너무 어려워서 머리 박박 뽑으면서 했다 진자 눈물이 나서 팀원들 바짓가랑이 잡으면서 부탁했는데 사실 포톤문제가 아니라 이미 싱글 바탕으로 만들어진 게임 로직을 멀티를 끼워넣으려니 내가 아무리 뭘 집어넣고 집어넣어봐도 안되는거였다.
그렇다..
그냥 처음부터..
다시 갈아엎어야 하는거였다..
그래서 결국 여기까지 하고 마무리하게됐는데 너무 아쉬운 마음만 남는다.
처음부터 멀티를 생각하고 로직을 짤걸.. 시간이 남아서 멀티를 해보겠다 한건데 절대 안되는일이였다..
그래도 칭찬 많이 받았고 많은것을 얻게된 팀 프로젝트였다.
반응형
'Record > TIL' 카테고리의 다른 글
[Unity] 스파르타 본사 방문날 (2) | 2024.06.28 |
---|---|
[Unity] 이누야샤 레퍼런스 카드전략게임 팀프로젝트 마무리 (0) | 2024.06.26 |
[Unity] 에디터 두개 빌드하기 (빌드없이 멀티 테스트하기) (0) | 2024.06.24 |
[Unity] Singleton<T>로 한번에 관리하기 (0) | 2024.06.21 |
[Unity] 디자인패턴 (0) | 2024.06.20 |