여기 있는 코드들은 거의 개인작 할때 만들었던 코드들이다.
전부는 아니였던거 같아서 거의라고 적긴 했는데 기억이 가물해서 전부일수도 있고 한두개 정도는 포톤API 기본예제일수도 있다.
에러코드 : https://doc-api.photonengine.com/en/pun/v1/class_error_code.html
연결상태
Debug.Log($"PhotonNetwork.IsConnected: {PhotonNetwork.IsConnected}"); //false
PhotonNetwork.ConnectUsingSettings(); //접속시도
Debug.Log($"PhotonNetwork.IsConnected: {PhotonNetwork.IsConnected}"); //true
//IsConnected는 접속호출 즉시 True가됨
Debug.Log($"PhotonNetwork.IsConnectedAndReady: {PhotonNetwork.IsConnectedAndReady}");
//IsConnectedAndReady로 해야 ConnectedToMaster되었는지 체크가능
RPC 샘플
public void SendChatMessage(string message)
{
photonView.RPC(nameof(ReceiveChatMessage), RpcTarget.Others, message);
}
[PunRPC]
void ReceiveChatMessage(string message)
{
Debug.Log($"{nameof(ReceiveChatMessage)}:{message}");
}
RPC 샘플 (발신자가 오너가 아닐경우, 자신포함 브로드캐스트)
이런경우 어차피 1줄짜리이므로 함수를 분리하기 보단
그냥 SendHealEffect를 칠자리에 RPC코드를 넣는게 낫다
public void SendHealEffect()
{
//발신자가 동작할 코드
photonView.RPC(nameof(ReceiveHealEffect), RpcTarget.All);
}
[PunRPC]
public void ReceiveHealEffect()
{
if (photonView.IsMine)
{
//오너가 동작할 코드
}
else
{
//오너제외
}
//모두가 동작할 코드
}
RPC샘플 V2
//브로드캐스트 (오너가 모두에게)
[PunRPC]
public void ChatMessage(string message)
{
if (photonView.IsMine)
{
var methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
photonView.RPC(methodName, RpcTarget.Others, message);
}
Debug.Log($"{nameof(ChatMessage)}:{message}");
}
//주인에게 보낼때
[PunRPC]
public void ChatMessage(string message)
{
if (photonView.IsMine)
{
Debug.Log($"{nameof(ChatMessage)}:{message}");
}
else
{
var methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
photonView.RPC(methodName, photonView.Controller, message);
}
}
RPC샘플 이전버전들
//브로드캐스트(발신자가 오너가 아닐때)
//인자에 기본값이 들어있으므로 그냥 HealEffect(); 치면 된다.
//다만 이건 의미없이 네트워크 송신비용이 발생하므로 V1처럼 가급적 함수를 하나 더 만들자
[PunRPC]
public void HealEffect(bool isSender = true)
{
if (photonView.IsMine || isSender)
{
//오너와 발신자가 작동할 코드
}
if (isSender)
{
var methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
photonView.RPC(methodName, photonView.Controller, false);
//발신자만 작동할 코드
return;
}
if (photonView.IsMine)
{
var methodName = System.Reflection.MethodBase.GetCurrentMethod().Name;
photonView.RPC(methodName, RpcTarget.Others, false);
//오너만 작동할 코드
}
//모두가 작동할 코드
}
RPC 관련 코드
//오너에게 보낼때(Fixed타입이면 오너가 null이므로 주의)
photonView.RPC(nameof(ReceiveChatMessage), photonView.Owner, message);
//Controller에게 보낼때(Fixed타입일때 실질적 오너)
photonView.RPC(nameof(ReceiveChatMessage), photonView.Controller, message);
변수동기화 샘플
using Photon.Pun;
[RequireComponent(typeof(PhotonView))]
public class GameSystem : MonoBehaviourPunCallbacks, IPunObservable
{
float timer = 200;
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.IsWriting)
{
stream.SendNext(timer);
}
else
{
timer = (float)stream.ReceiveNext();
}
}
}
//클래스 교환
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.IsWriting)
{
string[] sampleclassJsons = System.Array.ConvertAll(sampleClass,x=> JsonUtility.ToJson(x));
stream.SendNext(sampleclassJsons);
}
else
{
string[] sampleclassJsons = (string[])stream.ReceiveNext();
sampleClass = System.Array.ConvertAll(sampleclassJsons, x => JsonUtility.FromJson<SampleClass>(x));
}
}
타이머 동기화
[RequireComponent(typeof(PhotonView))]
public class GameSystem : MonoBehaviourPunCallbacks, IPunObservable
{
System.DateTime startTime = System.DateTime.UtcNow;
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.IsWriting)
{
stream.SendNext(startTime.Ticks);
}
else
{
startTime = new System.DateTime((long)stream.ReceiveNext());
}
}
public void SetTimer()
{
timer = oneGameTime - ((System.DateTime.UtcNow.Ticks - startTime.Ticks) / 10000000);
timer = Mathf.Clamp(0, timer, oneGameTime);
timerUI.text = timer.ToString();
}
static readonly float oneGameTime = 200;
float timer = oneGameTime;
public Text timerUI;
로컬타이머
if (PhotonNetwork.CurrentRoom == null)
{
localTimer = 0;
}
else
{
localTimer += Time.deltaTime;
}
해당 오브젝트의 오너의 닉네임을 불러옴
textUI.text = photonView.Owner.NickName;
자신이 마스터인지 체크
public bool CheckRoomMaster()
{
return (PhotonNetwork.IsMasterClient) || (PhotonNetwork.CurrentRoom == null);
}
닉네임 설정
Photon.Pun.PhotonNetwork.NickName = "Name";
타임아웃시간 설정
PhotonNetwork.KeepAliveInBackground = 3;
PhotonView들이 OnPhotonSerialize를 초당 몇회 호출할지
PhotonNetwork.SerializationRate=10;
서버시간
var serverTime = PhotonNetwork.Time;
방을 나가도 파괴 안되는 오브젝트
PhotonNetwork.InstantiateRoomObject(prefab.name, pos, rot, 0);
플레이어 관련
//플레이어 리스트
PhotonNetwork.PlayerList
//플레이어 리스트 (자기자신 제외)
PhotonNetwork.PlayerListOthers
//로컬플레이어 (자기자신)
PhotonNetwork.LocalPlayer
//액터넘버(근데 이건 플레이어로 한정되는 고유값이므로 잘 안 쓴다. photonView.ViewID를 쓰자)
PhotonNetwork.LocalPlayer.ActorNumber
//UserId 설정 (연결전에 설정되어야함)
var auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
PhotonNetwork.AuthValues = new Photon.Realtime.AuthenticationValues(auth.CurrentUser.UserId);
//userId to player
var player = System.Array.Find(PhotonNetwork.PlayerList,x=>x.UserId== userId);
if (player == null)
{
Debug.LogError($"해당 id의 플레이어가 존재하지 않음{userId}");
return;
}
//userIdList to playerList
var playerList = PhotonNetwork.PlayerList.ToList().FindAll(x=> userIdList.Contains(x.UserId));
룸관련
//룸옵션
var roomOptions=new Photon.Realtime.RoomOptions();
roomOptions.MaxPlayers = 0;
//방잠그기
PhotonNetwork.CurrentRoom.IsOpen = false;
특정상태의 플레이어 리스트 관리
Ready는 각자호출하고 리스트는 오너가 관리함
이 방식 해보니까 오프라인모드일때나 룸이동시에 처리도 번거롭고 해서 그냥 오브젝트 생성식이 나았다
데이터는 절감될거 같은데 굳~이 따지면 기계어가 낫냐 어셈블리어가 낫냐의 차이이므로 그냥 오브젝트 생성식을 쓴다
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.IsWriting)
{
stream.SendNext(ReadReadyPlayerList().ConvertAll(x=>x.UserId).ToArray());
}
else
{
var userIdList = ((string[])stream.ReceiveNext()).ToList();
readyPlayerList = PhotonNetwork.PlayerList.ToList().FindAll(x=> userIdList.Contains(x.UserId));
}
}
List<Photon.Realtime.Player> readyPlayerList = new List<Photon.Realtime.Player>();
void Ready()
{
Debug.Log("Ready()");
photonView.RPC(nameof(AddReadyPlayerList), photonView.Controller, PhotonNetwork.LocalPlayer.UserId);
}
void ReadyOff()
{
photonView.RPC(nameof(RemoveReadyPlayerList), photonView.Controller, PhotonNetwork.LocalPlayer.UserId);
}
[PunRPC]
void AddReadyPlayerList(string userId)
{
Debug.Log("AddReadyPlayerList()");
var player = System.Array.Find(PhotonNetwork.PlayerList,x=>x.UserId== userId);
if (player == null)
{
Debug.LogError($"해당 id의 플레이어가 존재하지 않음{userId}");
return;
}
if (readyPlayerList.Find(x => x.UserId == userId) == null)
{
readyPlayerList.Add(player);
}
}
[PunRPC]
void RemoveReadyPlayerList(string userId)
{
var player = System.Array.Find(PhotonNetwork.PlayerList, x => x.UserId == userId);
if (player==null)
{
Debug.LogError($"해당 id의 플레이어가 존재하지 않음{userId}");
return;
}
readyPlayerList.Remove(player);
}
List<Photon.Realtime.Player> ReadReadyPlayerList()
{
//오프라인 유저 제거
readyPlayerList=readyPlayerList.FindAll(x => PhotonNetwork.PlayerList.Contains(x));
return readyPlayerList;
}
커스텀서버
var appSettings = new Photon.Realtime.AppSettings();
PhotonNetwork.PhotonServerSettings.AppSettings.CopyTo(appSettings);
appSettings.AppVersion = serverName + appSettings.AppVersion;
PhotonNetwork.ConnectUsingSettings(appSettings, PhotonNetwork.PhotonServerSettings.StartInOfflineMode);
로비분리
Photon.Realtime.TypedLobby lobby;
public override void OnConnectedToMaster()
{
Debug.Log("ConnectedToMaster");
lobby = new Photon.Realtime.TypedLobby("loobyNameSample", Photon.Realtime.LobbyType.Default);
PhotonNetwork.JoinLobby(lobby); //로비접속
}
public override void OnJoinRandomFailed(short returnCode, string message)
{
Debug.LogError("PhotonRandomJoinFailed");
PhotonNetwork.CreateRoom(null,null, lobby);
}
룸리스트
List<RoomInfo> roomList= new List<RoomInfo>();
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
this.roomList.RemoveAll(x=>roomList.FindIndex(y=>y.Name== x.Name)>=0);
this.roomList.AddRange(roomList);
this.roomList.RemoveAll(x => x.MaxPlayers == 0 || x.PlayerCount == 0);
textUI.text = string.Join("",roomList.ConvertAll(room => $"{room.Name}:{room.CustomProperties["RoomName"]}"));
}
연결상태
[System.Serializable]
public class BoolEvent : UnityEngine.Events.UnityEvent<bool> { }
public BoolEvent OnChangedConnectedEvent;
public override void OnConnectedToMaster()
{
Debug.Log("ConnectedToMaster");
OnChangedConnectedEvent.Invoke(true);
}
public override void OnDisconnected(DisconnectCause cause)
{
Debug.Log("OnDisconnected");
OnChangedConnectedEvent.Invoke(false);
}
연결오류 모음
public override void OnDisconnected(DisconnectCause cause)
{
switch (cause)
{
case DisconnectCause.None:
break;
case DisconnectCause.ExceptionOnConnect:
break;
case DisconnectCause.DnsExceptionOnConnect:
break;
case DisconnectCause.ServerAddressInvalid:
break;
case DisconnectCause.Exception:
break;
case DisconnectCause.ServerTimeout:
break;
case DisconnectCause.ClientTimeout:
Debug.LogError($"포톤 서버가 터짐. Dev Region과 Fixed Region을 공백으로 하세요");
break;
case DisconnectCause.DisconnectByServerLogic:
break;
case DisconnectCause.DisconnectByServerReasonUnknown:
break;
case DisconnectCause.InvalidAuthentication:
break;
case DisconnectCause.CustomAuthenticationFailed:
break;
case DisconnectCause.AuthenticationTicketExpired:
break;
case DisconnectCause.MaxCcuReached:
break;
case DisconnectCause.InvalidRegion:
break;
case DisconnectCause.OperationNotAllowedInCurrentState:
break;
case DisconnectCause.DisconnectByClientLogic:
break;
case DisconnectCause.DisconnectByOperationLimit:
break;
case DisconnectCause.DisconnectByDisconnectMessage:
break;
default:
break;
}
Debug.LogError($"{cause}");
}
포톤 프로퍼티
//생성
public override void OnJoinRoomFailed(short returnCode, string message)
{
Debug.LogWarning("JoinRoomFailed");
Debug.LogWarning("CreateRoom");
//PhotonNetwork.CreateRoom(roomName);
RoomOptions roomOptions = new RoomOptions();
var hashtable = new ExitGames.Client.Photon.Hashtable();
hashtable.Add(key:"mapName", value: "testMap"); //mapName 프로퍼티
hashtable.Add(key: "password", value: "testPW"); //testPW 프로퍼티
roomOptions.CustomRoomProperties = hashtable;
roomOptions.CustomRoomPropertiesForLobby = System.Array.ConvertAll(roomOptions.CustomRoomProperties.Keys.ToArray(), x => x.ToString());
Debug.Log($"Properties : {string.Join(", ", roomOptions.CustomRoomPropertiesForLobby)}");
TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby);
PhotonNetwork.CreateRoom(null, roomOptions,sqlLobby);
}
//수정
public override void OnCreatedRoom()
{
var hashtable = PhotonNetwork.CurrentRoom.CustomProperties;
hashtable["mapName"] = "testMap2"; //수정
Debug.Log($"mapName : {PhotonNetwork.CurrentRoom.CustomProperties["mapName"]}");
Debug.Log("CreatedRoom");
}
//접속
public override void OnConnectedToMaster()
{
Debug.Log("ConnectedToMaster");
TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby);
string sqlLobbyFilter = "mapName = \"testMap\"";
// "C0 = 1"
// "C0 = 1 AND C2 > 50"
// "C5 = \"Map2\" AND C2 > 10 AND C2 < 20"
PhotonNetwork.JoinRandomRoom(null, 2, MatchmakingMode.FillRoom, sqlLobby, sqlLobbyFilter);
PhotonNetwork.JoinRoom(roomName);
}
'Unity > Photon' 카테고리의 다른 글
포톤 메신저 (0) | 2021.11.15 |
---|---|
포톤 PUN -> PUN2 변경점 (0) | 2021.07.10 |
포톤 보이스 (0) | 2021.07.10 |