当前位置: 首页 > 工具软件 > Photon > 使用案例 >

Photon 使用心得

袁恩
2023-12-01

评价用的不是很爽,但不得不用...

翻译文档:虽让版本很老,但是还是有价值

https://vibrantlink.com/pun_multiplay_manual/?tdsourcetag=s_pcqq_aiomsg

1.创建连接

    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();
    }

2.创建或加入房间

//创建房间 创建一个带名字的房间
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;
            }
        }

3.事件发送PhotonNetwork.RaiseEvent

//特别注意,坑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 )

4.RPC调用

直接发送通知给指定玩家

//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);

5.实例实时同步

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;
            }
        }
    }

}

6.玩家属性同步

    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;
    }

7.常用api 备忘

自定义类继承自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;
    }

 

 

 类似资料: