评价用的不是很爽,但不得不用...
翻译文档:虽让版本很老,但是还是有价值
https://vibrantlink.com/pun_multiplay_manual/?tdsourcetag=s_pcqq_aiomsg
NetWork.onClick.AddAudioListener(() => {
if (PhotonNetwork.IsConnected)
{
UIMgr.OpenPopPanel<W_SelectNetworkGameMode>();
}
else
{
UIMgr.OpenPanel<W_ConnectTip>();
PhotonNetwork.ConnectUsingSettings();
PhotonNetwork.ConnectToBestCloudServer();
}
ShengYaMgrData.Inst.type = 3;
});
//配套回调
public override void OnConnectedToMaster()
{
base.OnConnectedToMaster();
}
//创建房间 创建一个带名字的房间
PhotonNetwork.CreateRoom(CreateRoomInputField.text, new RoomOptions { MaxPlayers = 5 },
new TypedLobby(lobbyName, LobbyType.SqlLobby));
//配套回调
public override void OnJoinedRoom()
{
base.OnJoinedRoom();
UIMgr.ClosePanel<W_ConnectTip>();
UIMgr.OpenPopPanel<W_NetworkRoom>();
}
//随机加入一个指定类型的房间
PhotonNetwork.JoinRandomRoom(null, 2, MatchmakingMode.FillRoom, new TypedLobby(PassionSpeedMgrData.inst.curLobbyName.ToString(), LobbyType.SqlLobby), null);
//随机加入房间失败会返回OnJoinRandomFailed 需要在里面创建一个新房间
public override void OnJoinRandomFailed(short returnCode, string message)
{
base.OnJoinRandomFailed(returnCode, message);
Debug.LogError("OnJoinRandomFailed");
if (PassionSpeedMgrData.inst.curNetworkRoom == NetworkRoom.DEFAULT)
{
//string lobbyName = string.Equals(PassionSpeedMgrData.Inst.curLobbyName.ToString(), LobbyName.SPEED.ToString()) ? LobbyName.SPEED.ToString() : LobbyName.OVERTAKE.ToString();
PhotonNetwork.CreateRoom(null, new RoomOptions { MaxPlayers = 5 }, new TypedLobby(PassionSpeedMgrData.inst.curLobbyName.ToString(), LobbyType.SqlLobby));
}
else
{
//string lobbyName = string.Equals(PassionSpeedMgrData.Inst.curLobbyName.ToString(), LobbyName.SPEED.ToString()) ? LobbyName.RANKINGSPEED.ToString() : LobbyName.RANKINGOVERTAKE.ToString();
PhotonNetwork.CreateRoom(null, new RoomOptions { MaxPlayers = 5 }, new TypedLobby(PassionSpeedMgrData.inst.curLobbyName.ToString(), LobbyType.SqlLobby));
}
}
//查找房间
string lobbyName = string.Equals(PassionSpeedMgrData.Inst.curLobbyName.ToString(), LobbyName.SPEED.ToString()) ? LobbyName.SPEED.ToString() : LobbyName.OVERTAKE.ToString();
TypedLobby typedLobby = new TypedLobby(lobbyName, LobbyType.SqlLobby);
//if (ShengYaMgrData.Inst.type == 2)
//sqlLobby = new TypedLobby(Lobby.SPEED.ToString(), LobbyType.SqlLobby);
//else if (ShengYaMgrData.Inst.type == 2)
// sqlLobby = new TypedLobby(Lobby.SPEED.ToString(), LobbyType.SqlLobby);
RestRoom();
PhotonNetwork.GetCustomRoomList(typedLobby, "10000");
//配套回调
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
foreach (RoomInfo entry in roomList)
{
if(entry.Name.Contains(containsString))
{
GameObject item= GameObject.Instantiate(PassionSpeedMgr.Inst.Loader.LoadSync("UIItem_Rooms", "UI/UIItem/{0}")) as GameObject;
item.transform.SetParent(Content, false);
item.transform.GetChild(0).GetComponent<Text>().text = entry.Name;
item.transform.GetChild(1).GetComponent<Text>().text = entry.PlayerCount+"/"+ entry.MaxPlayers;
item.transform.GetChild(2).GetComponent<Button>().onClick.AddAudioListener(() => {
UIMgr.OpenPanel<W_ConnectTip>();
PhotonNetwork.JoinRoom(entry.Name);
});
// Debug.LogError
}
roomListCell = roomList;
}
}
//特别注意,坑1
//向其它玩家发送事件,只有其它玩家才能收到此事件的监听(疑惑?)(自己不能给自己发)
public void SendEvnet(NetClientEventCode _id, ExitGames.Client.Photon.Hashtable _paras =null)
{
PhotonNetwork.RaiseEvent((byte)_id,
_paras,
new RaiseEventOptions() { Receivers = ReceiverGroup.All, CachingOption = EventCaching.AddToRoomCache },
SendOptions.SendReliable);
}
ex:
public void UpRankData()
{
//void UpData()
//{
ExitGames.Client.Photon.Hashtable moveHt = new ExitGames.Client.Photon.Hashtable();
moveHt.Add("playerID", PhotonNetwork.LocalPlayer.UserId);
moveHt.Add("playerCarId", PassionSpeedMgrData.inst.CurCarTable.id);
moveHt.Add("playerCarJiFen", PassionSpeedMgrData.inst.ChaoCheCount);
moveHt.Add("playerDis", PassionSpeedMgrData.inst.XingShiLiCheng);
moveHt.Add("playerName", PassionSpeedMgrData.inst.UserName);
moveHt.Add("playerTime", TTOTool.inst.SecondToDate(PassionSpeedMgrData.Inst.XingShiShiJian));
PassionSpeedMgr.Inst.SendEvnet(NetClientEventCode.UpNetData, moveHt);
//}
}
//相关回调
public void OnEvent(EventData photonEvent)
{
// throw new System.NotImplementedException();
Debug.Log("{0}: {1}".TTOFormat(this.name,photonEvent.Code));
if (photonEvent.Code == (byte)NetClientEventCode.UpNetData)
{
ExitGames.Client.Photon.Hashtable evTable = photonEvent.CustomData as ExitGames.Client.Photon.Hashtable;
var playerID = (string)evTable["playerID"];
var _playerCarId = (int)evTable["playerCarId"];
var _playerCarJiFen = (int)evTable["playerCarJiFen"];
var _playerDis = (float)evTable["playerDis"];
var _playerName = (string)evTable["playerName"];
var _playerTime = (string)evTable["playerTime"];
NetRankInfo ni = new NetRankInfo
{
playerCarId = _playerCarId,
playerCarJiFen = _playerCarJiFen,
playerDis = _playerDis,
playerName = _playerName,
playerTime = _playerTime
};
if (netRankList.ContainsKey(playerID))
{
netRankList[playerID] = ni;
}
else
{
netRankList.Add(playerID, ni);
}
var win = UIMgr.GetPanel<W_NetworkRank>();
var giWin= UIMgr.GetPanel<W_GameIn>();
if (win != null)
{
//刷新数据
win.ShutData(netRankList.Values.ToList());
}
if (giWin != null)
{
giWin.InitGameInNetRank();
}
Debug.Log("{0}: {1}:{2}:{3}:{4}".TTOFormat("UpNetData:", _playerName,_playerCarId, playerID,""));
}
else if (photonEvent.Code == (byte)NetClientEventCode.MasterPaly
|| photonEvent.Code == (byte)NetClientEventCode.PaiWeiSaiStart
|| photonEvent.Code == (byte)NetClientEventCode.OtherrPlayerQuitRoom
|| photonEvent.Code == 253//
|| photonEvent.Code == 255
|| photonEvent.Code == 226)
{
var win = UIMgr.GetPanel<W_NetworkRoom>();
if (win != null)
{
win.ShutWinByEventCode(photonEvent);
}
}
else if (photonEvent.Code == 254)
{
// netRankList.Clear();
}
}
事件代码(在LoadBalancingPeer.cs 文件里的 OperationCode.RaiseEvent )
直接发送通知给指定玩家
//RPC 方法定义
[PunRPC]
void NetPaiWeiFinish()
{
Debug.Log("排位赛结束,游戏结束");
// PhotonNetwork.LeaveRoom();
PassionSpeedMgrData.inst.PauseGame(true);
//var win= UIMgr.OpenPanel<W_GameSuck>();
//win.SetAniTrigger("fail");
PassionSpeedMgr.Inst.UpRankData();
if (!PassionSpeedMgrData.Inst.IsNetOver)
{
var win = UIMgr.OpenPanel<W_NetworkRank>();
PassionSpeedMgrData.Inst.IsNetOver = true;
}
//获取胜利者
}
ex: PhotonView.Get(this).RPC("NetSceneFinish", RpcTarget.Others);
OnPhotonSerializeView方法是同步方法 需理解发送方和接收方
using HotFix_PassionSpeed;
using Photon.Pun;
using QFramework;
using QFramework.PassionSpeed;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TransPosSynchronization : GameInst<TransPosSynchronization>, IPunObservable
{
//需要实时同步的一些数据
private float m_Distance;
private float m_Angle;
private PhotonView m_PhotonView;
private Vector3 m_Direction;
private Vector3 m_NetworkPosition;
// Vector3 m_NetworkPositionZFrame;//单帧速度
private Vector3 m_StoredPosition;
private Quaternion m_NetworkRotation;
public bool m_SynchronizePosition = true;
public bool m_SynchronizeRotation = true;
public bool m_SynchronizeScale = false;
bool m_firstTake = false;
public void Awake()
{
m_PhotonView = GetComponent<PhotonView>();
m_StoredPosition = transform.position;
m_NetworkPosition = Vector3.zero;
m_NetworkRotation = Quaternion.identity;
}
void OnEnable()
{
m_firstTake = true;
}
[PunRPC]
void NetSceneFinish()
{
Debug.Log("你们失败了,游戏结束");
// PhotonNetwork.LeaveRoom();
PassionSpeedMgrData.inst.PauseGame(true);
//var win= UIMgr.OpenPanel<W_GameSuck>();
//win.SetAniTrigger("fail");
PassionSpeedMgr.Inst.UpRankData();// UpData();
var win = UIMgr.OpenPanel<W_NetworkRank>();
PassionSpeedMgrData.Inst.IsNetOver = true;
}
[PunRPC]
void NetPaiWeiFinish()
{
Debug.Log("排位赛结束,游戏结束");
// PhotonNetwork.LeaveRoom();
PassionSpeedMgrData.inst.PauseGame(true);
//var win= UIMgr.OpenPanel<W_GameSuck>();
//win.SetAniTrigger("fail");
PassionSpeedMgr.Inst.UpRankData();
if (!PassionSpeedMgrData.Inst.IsNetOver)
{
var win = UIMgr.OpenPanel<W_NetworkRank>();
PassionSpeedMgrData.Inst.IsNetOver = true;
}
//获取胜利者
}
float DataUpTime = 1;
public void Update()
{
if (PassionSpeedMgrData.inst.IsNetModel && !TTOSceneMgr.inst.IsCheKu())
{
if (PassionSpeedMgrData.inst.curNetworkRoom == NetworkRoom.DEFAULT)
{
if (PassionSpeedMgrData.inst.curLobbyName == LobbyName.SPEED)
{
//满足条件直接胜利
if (PassionSpeedMgrData.inst.XingShiLiCheng >= PassionSpeedMgrData.Inst.NetJinSuTarget)
{
if (!PassionSpeedMgrData.Inst.IsNetOver)
{
PhotonView.Get(this).RPC("NetSceneFinish", RpcTarget.Others);
Debug.Log("suck res");
PassionSpeedMgrData.inst.PauseGame(true);
//var win= UIMgr.OpenPanel<W_GameSuck>();
// win.SetAniTrigger("victory");
PassionSpeedMgr.Inst.UpRankData();
var win = UIMgr.OpenPanel<W_NetworkRank>();
//PhotonNetwork.LeaveRoom();
PassionSpeedMgrData.Inst.IsNetOver = true;
//获取排位信息
//PhotonNetwork.PlayerList.
//win.SetAniTrigger("victory");
}
}
}
else
{
if (PassionSpeedMgrData.inst.ChaoCheCount >= PassionSpeedMgrData.Inst.NetChaoCheCount)
{
if (!PassionSpeedMgrData.Inst.IsNetOver)
{
PhotonView.Get(this).RPC("NetSceneFinish", RpcTarget.Others);
//PhotonNetwork.LeaveRoom();
Debug.Log("suck res");
PassionSpeedMgrData.inst.PauseGame(true);
PassionSpeedMgr.Inst.UpRankData();
//PassionSpeedMgr.Inst.NetRankListAddSelf();
var win = UIMgr.OpenPanel<W_NetworkRank>();
//var win = UIMgr.OpenPanel<W_GameSuck>();
//win.SetAniTrigger("victory");
PassionSpeedMgrData.Inst.IsNetOver = true;
}
}
}
}
else if (PassionSpeedMgrData.inst.curNetworkRoom == NetworkRoom.RANKING)
{
if (!PassionSpeedMgrData.Inst.IsNetOver)
{
//排位赛按规定时间去处理
if (PassionSpeedMgrData.inst.XingShiShiJian >= PassionSpeedMgrData.inst.NetJinSuTime)
{
//通知所有人 结束了
PhotonView.Get(this).RPC("NetPaiWeiFinish", RpcTarget.All);
//PassionSpeedMgr.Inst
//PassionSpeedMgrData.Inst.IsNetOver = true;
}
}
//if (PassionSpeedMgrData.Inst.curLobbyName == LobbyName.RANKINGSPEED)
//{
//}
}
//定时同步信息
DataUpTime -= Time.deltaTime;
if (DataUpTime <= 0)
{
PassionSpeedMgr.Inst.UpRankData();
}
}
if (!this.m_PhotonView.IsMine)
{
var disz = transform.position.z - this.m_NetworkPosition.z;
this.m_Distance =Vector3.Distance(transform.position, this.m_NetworkPosition);
//var disz = transform.position.z - this.m_NetworkPosition.z;
//解决抖动问题
//Debug.Log("{0}:{1}:{2}".TTOFormat(transform.position, this.m_NetworkPosition, m_Distance));
//transform.position = Vector3.MoveTowards(transform.position, this.m_NetworkPosition, this.m_Distance *1f * (1.0f / PhotonNetwork.SerializationRate));
transform.position = Vector3.MoveTowards(transform.position, this.m_NetworkPosition, this.m_Distance *Time.deltaTime);
transform.rotation = Quaternion.RotateTowards(transform.rotation, this.m_NetworkRotation, this.m_Angle * (1.0f / PhotonNetwork.SerializationRate));
}
}
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
//是否是发送方
if (stream.IsWriting)
{
if (this.m_SynchronizePosition)
{
this.m_Direction = transform.position - this.m_StoredPosition;
this.m_StoredPosition = transform.position;
stream.SendNext(new Vector3(transform.position.x, transform.position.y, PassionSpeedMgrData.inst.NetLength));
stream.SendNext(this.m_Direction);
}
if (this.m_SynchronizeRotation)
{
stream.SendNext(transform.rotation);
}
if (this.m_SynchronizeScale)
{
stream.SendNext(transform.localScale);
}
}
else
{
//接收方
if (this.m_SynchronizePosition)
{
this.m_NetworkPosition = (Vector3)stream.ReceiveNext();
var dis = this.m_NetworkPosition.z - (PassionSpeedMgrData.inst.NetLength);
this.m_NetworkPosition = new Vector3(this.m_NetworkPosition.x, this.m_NetworkPosition.y, dis);
this.m_Direction = (Vector3)stream.ReceiveNext();
if (m_firstTake)
{
//初始化位置
transform.position = this.m_NetworkPosition;
this.m_Distance = 0f;
}
else
{
float lag = Mathf.Abs((float)(PhotonNetwork.Time - info.SentServerTime));
this.m_NetworkPosition += this.m_Direction * lag;
// this.m_Distance = Vector3.Distance(transform.position, this.m_NetworkPosition);
}
}
if (this.m_SynchronizeRotation)
{
this.m_NetworkRotation = (Quaternion)stream.ReceiveNext();
if (m_firstTake)
{
this.m_Angle = 0f;
transform.rotation = this.m_NetworkRotation;
}
else
{
this.m_Angle = Quaternion.Angle(transform.rotation, this.m_NetworkRotation);
}
}
if (this.m_SynchronizeScale)
{
transform.localScale = (Vector3)stream.ReceiveNext();
}
if (m_firstTake)
{
m_firstTake = false;
}
}
}
}
public static void SetIsStart(this Player player, int isStart)
{
Hashtable IsStart = new Hashtable(); // using PUN's implementation of Hashtable
IsStart[PlayerIsStartProp] = isStart;
player.SetCustomProperties(IsStart); // this locally sets the score and will sync it in-game asap.
}
public static int GetIsStart(this Player player)
{
object isStart;
if (player.CustomProperties.TryGetValue(PlayerIsStartProp, out isStart))
{
return (int)isStart;
}
return 0;
}
//调用参考
public bool IsReady()
{
bool isReady = true;
PhotonNetwork.PlayerList.ForEach(s=> {
if (s.GetIsStart() != 1)
{
isReady = false;
}
});
return isReady;
}
自定义类继承自Photon.PunBehaviour
加入房间: PhotonNetwork.JoinRandomRoom();
加入房间失败时的回调方法: OnPhotonRandomJoinFailed(object[] codeAndMsg) {}
创建房间: PhotonNetwork.CreateRoom(string roomName);
创建或加入房间: PhotonNetwork.JoinOrCreateRoom(string roomName);
PhotonNetwork.LocalPlayer.SetIsReady(!PhotonNetwork.LocalPlayer.GetIsReady());
PhotonNetwork.IsConnectedAndReady (连接成功并已经准备完毕)
创建房间成功的回调方法: OnCreatedRoom() {}
加入房间成功时的回调方法: OnJoinedRoom() {}
OnPlayerEnteredRoom
override void W_NetworkRoom.OnLeftRoom()
OnPhotonPlayerConnected(PhotonPlayer newPlayer) {}
房间内所有玩家是否同步场景的属性: PhotonNetwork.automaticallySyncScene == true;
// defines if all clients in a room should load the same level/scene as the Master client (if that used PhotonNetwork.LoadLevel())
判断当前客户端是否为主机(房主): PhotonNetwork.isMasterClient
载场景: PhotonNetwork.LoadLevel(string sceneName); // 比如 PhotonNetwork.LoadLevel("main");
//经常用到的函数
PhotonNetwork.Instantiate
PhotonNetwork.InstantiateSceneObject
//事件代码(在LoadBalancingPeer.cs 文件里的 OperationCode.RaiseEvent )
public class OperationCode
{
[Obsolete("Exchanging encrpytion keys is done internally in the lib now. Don't expect this operation-result.")]
public const byte ExchangeKeysForEncryption = 250;
/// <summary>(255) Code for OpJoin, to get into a room.</summary>
[Obsolete]
public const byte Join = 255;
/// <summary>(231) Authenticates this peer and connects to a virtual application</summary>
public const byte AuthenticateOnce = 231;
/// <summary>(230) Authenticates this peer and connects to a virtual application</summary>
public const byte Authenticate = 230;
/// <summary>(229) Joins lobby (on master)</summary>
public const byte JoinLobby = 229;
/// <summary>(228) Leaves lobby (on master)</summary>
public const byte LeaveLobby = 228;
/// <summary>(227) Creates a game (or fails if name exists)</summary>
public const byte CreateGame = 227;
/// <summary>(226) Join game (by name)</summary>
public const byte JoinGame = 226;
/// <summary>(225) Joins random game (on master)</summary>
public const byte JoinRandomGame = 225;
// public const byte CancelJoinRandom = 224; // obsolete, cause JoinRandom no longer is a "process". now provides result immediately
/// <summary>(254) Code for OpLeave, to get out of a room.</summary>
public const byte Leave = (byte)254;
/// <summary>(253) Raise event (in a room, for other actors/players)</summary>
public const byte RaiseEvent = (byte)253;
/// <summary>(252) Set Properties (of room or actor/player)</summary>
public const byte SetProperties = (byte)252;
/// <summary>(251) Get Properties</summary>
public const byte GetProperties = (byte)251;
/// <summary>(248) Operation code to change interest groups in Rooms (Lite application and extending ones).</summary>
public const byte ChangeGroups = (byte)248;
/// <summary>(222) Request the rooms and online status for a list of friends (by name, which should be unique).</summary>
public const byte FindFriends = 222;
/// <summary>(221) Request statistics about a specific list of lobbies (their user and game count).</summary>
public const byte GetLobbyStats = 221;
/// <summary>(220) Get list of regional servers from a NameServer.</summary>
public const byte GetRegions = 220;
/// <summary>(219) WebRpc Operation.</summary>
public const byte WebRpc = 219;
/// <summary>(218) Operation to set some server settings. Used with different parameters on various servers.</summary>
public const byte ServerSettings = 218;
/// <summary>(217) Get the game list matching a supplied sql filter (SqlListLobby only) </summary>
public const byte GetGameList = 217;
}