PUN丨(四)大厅建房解析

时间:2020-05-30
本文章向大家介绍PUN丨(四)大厅建房解析,主要包括PUN丨(四)大厅建房解析使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

大家好,我是SKODE。

小提示:选中左侧目录,可快速找到所需内容

本系列博客地址:传送门

一、实现目标

Demo:DemoAsteroids大厅的解析

二、大厅思路

Awake:设置同步场景的方式

登陆按钮:同步本地昵称、连接到服务器

连接到服务器回调:关闭登陆界面,打开 创建房间 / 加入房间 / 显示房间列表 的面板选择界面

选择创建房间界面的Btn:打开创建房间界面

    创建房间界面:输入房间名、最大人数、拥有创建房间、返回的按钮
        返回按钮:返回到功能选择界面

    创建房间Btn:根据房间名、最大人数,创建服务器房间

        创建/加入房间回调:打开房间内面板、实例化当前所有玩家的条形信息预制体,并将(昵称、是否准备)信息初始化到该预制体上的脚本上↓
            条形信息物体:上有脚本:保存了该玩家的 ID、昵称、准备信息
                           根据初始化的ID==本地玩家ID?不等于则不显示该预制体的准备按钮(即咱们不显示别人电脑的ready,咱们只能控制咱们的ready)
                            若等于,即代表着这个预制体是我们自己的。则显示准备按钮。且将准备信息等做为自定义的同步信息
                            根据本房间内,该玩家的Number,决定这个预制体是什么颜色。(demo设定最多8人,因此这有8种对应的case)【用PUN的 Number更新回调实现】
                            注意,这儿要用到PUN自带的脚本:PlayerNumbering,要将其挂在场景中
                         上有准备按钮:
                            每次点击,改变自身状态(是否关闭等)、同步自身是否准备信息
                            若自己是主服务器,则还可根据当前玩家是否都已准备,显示开始游戏按钮(检查是否都已准备,就是foreach所有玩家的准备信息,进行判断)
                         开始游戏按钮:
                            设置当前房间状态:不可再加入、大厅列表不可见(隐身)
                            PUN同步加载场景
        
    
    加入随机房间按钮:加入随机房间,显示服务器房间界面
        返回按钮:退出服务器房间,返回到功能选择界面

    显示房间列表按钮:使用加入大厅API,使PUN调用 刷新大厅列表 回调,在该回调中完成相关逻辑(该回调会传入所有房间列表缓存):
        清空、删掉房间预制体、
        根据从网络获得的缓存列表,判断房间是否可加入、可见性、标记性,删除不需要的房间,将需要的房间添加到本地房间列表。
        更新实例化本地房间列表
    




开始游戏按钮状态:只有主客户端进行检测判断。(其他客户端没有开游戏的资格,自然不用检测)
    主客户端点击准备时、
    本地玩家进入房间时、(新玩家进来了,当然关闭按钮了)
    其他玩家进入房间时、(新玩家进来了,当然关闭按钮了)
    其他玩家离开房间时、
    主客户端切换给别人时、
    玩家属性更新时、(PUN回调)


房间列表更新时机:
    显示房间列表信息按钮、
    本地玩家退出大厅回调、
    本地玩家离开房间回调、
    其他玩家加入房间回调、
    其他玩家离开房间回调、

三、主要脚本

该场景主要由这两个脚本实现功能

PlayerNumbering作为PUN实用脚本,挂载到场景中,配合我们写的代码。

LobbyMainPanel

using ExitGames.Client.Photon;
using Photon.Realtime;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace Photon.Pun.Demo.Asteroids
{
    public class LobbyMainPanel : MonoBehaviourPunCallbacks
    {
        #region Public Parameters

        [Header("Login Panel")]
        public GameObject LoginPanel;

        [Tooltip("玩家昵称输入框")]
        public InputField PlayerNameInput;

        [Header("房间操作界面")]
        public GameObject SelectionPanel;

        [Header("Create Room Panel")]
        public GameObject CreateRoomPanel;

        public InputField RoomNameInputField;
        public InputField MaxPlayersInputField;

        [Header("Join Random Room Panel")]
        public GameObject JoinRandomRoomPanel;

        [Header("Room List Panel")]
        public GameObject RoomListPanel;

        public GameObject RoomListContent;
        public GameObject RoomListEntryPrefab;

        [Header("Inside Room Panel")]
        public GameObject InsideRoomPanel;

        public Button StartGameButton;
        public GameObject PlayerListEntryPrefab;

        #endregion


        #region Private Parameters

        Dictionary<string, RoomInfo> cachedRoomList = new Dictionary<string, RoomInfo>();
        Dictionary<string, GameObject> roomListEntries = new Dictionary<string, GameObject>();
        Dictionary<int, GameObject> playerListEntries;

        #endregion


        #region Mono CallBacks

        public void Awake()
        {
            PhotonNetwork.AutomaticallySyncScene = true;

            PlayerNameInput.text = "Player " + Random.Range(1000, 10000);
        }

        #endregion


        #region PUN CallBacks

        public override void OnConnectedToMaster()
        {
            SetActivePanel(SelectionPanel.name);
        }

        //本地玩家进入房间时
        public override void OnJoinedRoom()
        {
            print("OnJoinedRoom");

            SetActivePanel(InsideRoomPanel.name);

            if (playerListEntries == null)
                playerListEntries = new Dictionary<int, GameObject>();

            foreach (Player p in PhotonNetwork.PlayerList)
            {
                GameObject entry = Instantiate(PlayerListEntryPrefab);
                entry.transform.SetParent(InsideRoomPanel.transform);
                entry.transform.localScale = Vector3.one;
                entry.GetComponent<PlayerListEntry>().Initialize(p.ActorNumber, p.NickName);

                object isPlayerReady;
                if (p.CustomProperties.TryGetValue(AsteroidsGame.PLAYER_READY, out isPlayerReady))
                {
                    entry.GetComponent<PlayerListEntry>().SetPlayerReady((bool)isPlayerReady);
                }

                playerListEntries.Add(p.ActorNumber, entry);
            }

            StartGameButton.gameObject.SetActive(CheckPlayersReady());

            Hashtable props = new Hashtable
            {
                {AsteroidsGame.PLAYER_LOADED_LEVEL, false}
            };
            PhotonNetwork.LocalPlayer.SetCustomProperties(props);
        }

        //刷新列表回调
        public override void OnRoomListUpdate(List<RoomInfo> roomList)
        {
            ClearRoomListView();

            UpdateCachedRoomList(roomList);
            UpdateRoomListView();
        }

        public override void OnLeftLobby()
        {
            cachedRoomList.Clear();

            ClearRoomListView();
        }

        public override void OnCreateRoomFailed(short returnCode, string message)
        {
            SetActivePanel(SelectionPanel.name);
        }

        public override void OnJoinRoomFailed(short returnCode, string message)
        {
            SetActivePanel(SelectionPanel.name);
        }

        public override void OnJoinRandomFailed(short returnCode, string message)
        {
            string roomName = "Room " + Random.Range(1000, 10000);

            RoomOptions options = new RoomOptions { MaxPlayers = 8 };

            PhotonNetwork.CreateRoom(roomName, options, null);
        }

        public override void OnLeftRoom()
        {
            SetActivePanel(SelectionPanel.name);

            foreach (GameObject entry in playerListEntries.Values)
            {
                Destroy(entry.gameObject);
            }

            playerListEntries.Clear();
            playerListEntries = null;
        }

        //其他玩家进入房间时
        public override void OnPlayerEnteredRoom(Player newPlayer)
        {
            GameObject entry = Instantiate(PlayerListEntryPrefab);
            entry.transform.SetParent(InsideRoomPanel.transform);
            entry.transform.localScale = Vector3.one;
            entry.GetComponent<PlayerListEntry>().Initialize(newPlayer.ActorNumber, newPlayer.NickName);

            playerListEntries.Add(newPlayer.ActorNumber, entry);

            StartGameButton.gameObject.SetActive(CheckPlayersReady());
        }

        public override void OnPlayerLeftRoom(Player otherPlayer)
        {
            Destroy(playerListEntries[otherPlayer.ActorNumber].gameObject);
            playerListEntries.Remove(otherPlayer.ActorNumber);

            StartGameButton.gameObject.SetActive(CheckPlayersReady());
        }

        public override void OnMasterClientSwitched(Player newMasterClient)
        {
            if (PhotonNetwork.LocalPlayer.ActorNumber == newMasterClient.ActorNumber)
            {
                StartGameButton.gameObject.SetActive(CheckPlayersReady());
            }
        }

        public override void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
        {
            if (playerListEntries == null)
                playerListEntries = new Dictionary<int, GameObject>();

            GameObject entry;
            if (playerListEntries.TryGetValue(targetPlayer.ActorNumber, out entry))
            {
                object isPlayerReady;
                if (changedProps.TryGetValue(AsteroidsGame.PLAYER_READY, out isPlayerReady))
                {
                    entry.GetComponent<PlayerListEntry>().SetPlayerReady((bool)isPlayerReady);
                }
            }

            StartGameButton.gameObject.SetActive(CheckPlayersReady());
        }

        #endregion


        #region Public Methods

        //绑定到登陆按钮
        public void OnLoginButtonClicked()
        {
            string playerName = PlayerNameInput.text;

            if (!playerName.Equals(""))
            {
                PhotonNetwork.LocalPlayer.NickName = playerName;
                PhotonNetwork.ConnectUsingSettings();
            }
            else
            {
                Debug.LogError("Player Name is invalid.");
            }
        }

        //绑定到创建服务器房间按钮
        public void OnCreateRoomButtonClicked()
        {
            string roomName = RoomNameInputField.text;
            roomName = (roomName.Equals(string.Empty)) ? "Room " + Random.Range(1000, 10000) : roomName;

            byte maxPlayers;
            byte.TryParse(MaxPlayersInputField.text, out maxPlayers);
            maxPlayers = (byte)Mathf.Clamp(maxPlayers, 2, 8);

            RoomOptions options = new RoomOptions { MaxPlayers = maxPlayers };

            PhotonNetwork.CreateRoom(roomName, options, null);
        }

        //给玩家信息条的准备按钮使用。当本地玩家是主客户端时,执行。
        public void LocalPlayerPropertiesUpdated()
        {
            StartGameButton.gameObject.SetActive(CheckPlayersReady());
        }

        //绑定到开始游戏按钮
        public void OnStartGameButtonClicked()
        {
            //当前房间不可再加入
            PhotonNetwork.CurrentRoom.IsOpen = false;

            //让当前房间不可见:在大厅的列表中搜不到。(并且当你创建这个房间时,也可设为隐身的房间)
            PhotonNetwork.CurrentRoom.IsVisible = false;

            PhotonNetwork.LoadLevel("DemoAsteroids-GameScene");
        }

        //绑定到加入随机房间按钮
        public void OnJoinRandomRoomButtonClicked()
        {
            SetActivePanel(JoinRandomRoomPanel.name);

            PhotonNetwork.JoinRandomRoom();
        }

        //绑定到显示列表按钮
        public void OnRoomListButtonClicked()
        {
            if (!PhotonNetwork.InLobby)
            {
                //使用该API,使PUN调用 刷新大厅房间列表 回调,并在该回调完成相关逻辑。
                PhotonNetwork.JoinLobby();
            }

            SetActivePanel(RoomListPanel.name);
        }

        //绑定到创建房间界面的返回按钮
        public void OnBackButtonClicked()
        {
            //这并不会执行,因为没加入大厅
            if (PhotonNetwork.InLobby)
            {
                PhotonNetwork.LeaveLobby();
            }

            SetActivePanel(SelectionPanel.name);
        }

        //绑定到加入随机房间界面的返回按钮上
        public void OnLeaveGameButtonClicked()
        {
            PhotonNetwork.LeaveRoom();
        }

        #endregion


        #region Private Methods

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

        //检查所有玩家是否已经准备
        bool CheckPlayersReady()
        {
            if (!PhotonNetwork.IsMasterClient)
            {
                return false;
            }

            foreach (Player p in PhotonNetwork.PlayerList)
            {
                object isPlayerReady;
                if (p.CustomProperties.TryGetValue(AsteroidsGame.PLAYER_READY, out isPlayerReady))
                {
                    if (!(bool)isPlayerReady)
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }

            return true;
        }

        //清空本地房间数据列表、删除房间预制体
        void ClearRoomListView()
        {
            foreach (GameObject entry in roomListEntries.Values)
            {
                Destroy(entry.gameObject);
            }

            roomListEntries.Clear();
        }

        //更新本地房间数据列表
        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 cached room info
                if (cachedRoomList.ContainsKey(info.Name))
                {
                    cachedRoomList[info.Name] = info;
                }
                // Add new room info to cache
                else
                {
                    cachedRoomList.Add(info.Name, info);
                }
            }
        }

        //实例化房间预制体
        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, info.MaxPlayers);

                roomListEntries.Add(info.Name, entry);
            }
        }
        #endregion
    }
}

PlayerListEntry

// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlayerListEntry.cs" company="Exit Games GmbH">
//   Part of: Asteroid Demo,
// </copyright>
// <summary>
//  Player List Entry
// </summary>
// <author>developer@exitgames.com</author>
// --------------------------------------------------------------------------------------------------------------------

using UnityEngine;
using UnityEngine.UI;

using ExitGames.Client.Photon;
using Photon.Realtime;
using Photon.Pun.UtilityScripts;

namespace Photon.Pun.Demo.Asteroids
{
    public class PlayerListEntry : MonoBehaviour
    {
        [Tooltip("玩家昵称")]
        public Text PlayerNameText;

        public Image PlayerColorImage;
        public Button PlayerReadyButton;
        public Image PlayerReadyImage;

        int ownerId;
        bool isPlayerReady;


        #region Mono CallBacks

        public void OnEnable()
        {
            //每次房间索引更新时调用
            PlayerNumbering.OnPlayerNumberingChanged += OnPlayerNumberingChanged;
        }


        public void Start()
        {
            if (PhotonNetwork.LocalPlayer.ActorNumber != ownerId)
            {
                PlayerReadyButton.gameObject.SetActive(false);
            }
            else
            {
                Hashtable initialProps = new Hashtable() {{AsteroidsGame.PLAYER_READY, isPlayerReady}, {AsteroidsGame.PLAYER_LIVES, AsteroidsGame.PLAYER_MAX_LIVES}};
                PhotonNetwork.LocalPlayer.SetCustomProperties(initialProps);

                //这将在本地设置分数,并将同步它在游戏中尽快。
                PhotonNetwork.LocalPlayer.SetScore(0);

                PlayerReadyButton.onClick.AddListener(() =>
                {
                    isPlayerReady = !isPlayerReady;
                    SetPlayerReady(isPlayerReady);

                    Hashtable props = new Hashtable() {{AsteroidsGame.PLAYER_READY, isPlayerReady}};
                    PhotonNetwork.LocalPlayer.SetCustomProperties(props);

                    //检测是否全员准备,是则显示开始游戏按钮
                    if (PhotonNetwork.IsMasterClient)
                    {
                        FindObjectOfType<LobbyMainPanel>().LocalPlayerPropertiesUpdated();
                    }
                });
            }
        }

        public void OnDisable()
        {
            PlayerNumbering.OnPlayerNumberingChanged -= OnPlayerNumberingChanged;
        }

        #endregion

        public void Initialize(int playerId, string playerName)
        {
            ownerId = playerId;
            PlayerNameText.text = playerName;
        }

        private void OnPlayerNumberingChanged()
        {
            foreach (Player p in PhotonNetwork.PlayerList)
            {
                if (p.ActorNumber == ownerId)
                {
                    PlayerColorImage.color = AsteroidsGame.GetColor(p.GetPlayerNumber());
                }
            }
        }

        public void SetPlayerReady(bool playerReady)
        {
            PlayerReadyButton.GetComponentInChildren<Text>().text = playerReady ? "Ready!" : "Ready?";
            PlayerReadyImage.enabled = playerReady;
        }
    }
}

这是第三次探索PUN了,这次终于硬看源码搞清了它的逻辑  (* ̄︶ ̄)

一起成长

我是SDKODE

来源:淮北网站优化

原文地址:https://www.cnblogs.com/vwvwvwgwgvervae/p/12994071.html