之前在SP版本中用过,但是要经过RPC函数重写的脚本先不放
1.登录面板
using UnityEngine; using System.Collections; using UnityEngine.UI; using Photon; public class LoginPanelController : PunBehaviour { public GameObject loginPanel; //游戏登录面板 public GameObject userMessage; //玩家昵称信息 public Button backButton; //后退按钮 public GameObject lobbyPanel; //游戏大厅面板 public GameObject roomPanel; //游戏房间面板 public Text username; //玩家昵称文本 public Text connectionState; //网络连接状态 //初始化,根据当前客户端连接状态,显示相应的游戏面板 void Start () { //如果未连接Photon服务器 if (!PhotonNetwork.connected) { SetLoginPanelActive (); //启用游戏登录面板 username.text = PlayerPrefs.GetString ("Username"); //在本地保存玩家昵称 } //如果已连接Photon服务器 else SetLobbyPanelActive (); //启用游戏大厅面板 connectionState.text = ""; //初始化网络连接状态文本信息 } //条件编译指令,只在Unity编辑器中(UNITY_EDITOR)编译此段代码 //#if(UNITY_EDITOR) void Update(){ //在游戏画面左下角显示当前的网络连接状态 connectionState.text = PhotonNetwork.connectionStateDetailed.ToString (); } //#endif //启用游戏登录面板 public void SetLoginPanelActive(){ loginPanel.SetActive (true); //启用游戏登录面板 userMessage.SetActive (false); //禁用玩家昵称信息 backButton.gameObject.SetActive (false); //禁用后退按钮 lobbyPanel.SetActive (false); //禁用游戏大厅面板 if(roomPanel!=null) roomPanel.SetActive (false); //禁用游戏房间面板 } //启用游戏大厅面板 public void SetLobbyPanelActive(){ loginPanel.SetActive (false); //禁用游戏登录面板 userMessage.SetActive (true); //启用玩家昵称信息 backButton.gameObject.SetActive (true); //启用后退按钮 lobbyPanel.SetActive (true); //启用游戏大厅面板 } //"登录"按钮事件处理函数 public void ClickLogInButton(){ SetLobbyPanelActive (); //启用游戏大厅面板 //客户端连接Photon服务器,游戏版本标识符为“1.0” if (!PhotonNetwork.connected) PhotonNetwork.ConnectUsingSettings ("1.0"); //如果玩家未输入昵称,这里自动为其分配一个昵称 if (username.text == "") username.text = "游客" + Random.Range (1, 9999); PhotonNetwork.player.name = username.text; //设置玩家昵称 PlayerPrefs.SetString ("Username", username.text); //将玩家昵称保存在本地 } //"退出"按钮事件处理函数 public void ClickExitGameButton(){ Application.Quit (); //退出游戏应用 } /**覆写IPunCallback回调函数,当玩家进入游戏大厅时调用 * 显示玩家昵称 */ public override void OnJoinedLobby(){ userMessage.GetComponentInChildren<Text> ().text = "欢迎," + PhotonNetwork.player.name; } /**覆写IPunCallback回调函数,当客户端断开与Photon服务器的连接时调用 * 游戏画面返回游戏登录面板 */ public override void OnConnectionFail(DisconnectCause cause){ SetLoginPanelActive (); } }
2.大厅面板
using UnityEngine; using System.Collections; using UnityEngine.UI; using Photon; public class LobbyPanelController : PunBehaviour { public GameObject loginPanel; //游戏登录面板 public GameObject lobbyPanel; //游戏大厅面板 public GameObject userMessage; //玩家昵称信息 public Button backButton; //返回按钮 public GameObject lobbyLoadingLabel; //游戏大厅加载提示信息 public GameObject roomLoadingLabel; //游戏房间加载提示信息 public GameObject roomMessagePanel; //房间信息面板 public Button randomJoinButton; //"随机进入房间"按钮 public GameObject previousButton; //"上一页"按钮 public GameObject nextButton; //"下一页"按钮 public Text pageMessage; //房间页数文本控件 public GameObject createRoomPanel; //创建房间面板 public GameObject roomPanel; //游戏房间面板 private RoomInfo[] roomInfo; //游戏大厅房间列表信息 private int currentPageNumber; //当前房间页 private int maxPageNumber; //最大房间页 private int roomPerPage = 4; //每页显示房间个数 private GameObject[] roomMessage; //游戏房间信息 //当游戏大厅面板启用时调用,初始化信息 void OnEnable(){ currentPageNumber = 1; //初始化当前房间页 maxPageNumber = 1; //初始化最大房间页 lobbyLoadingLabel.SetActive (true); //启用游戏大厅加载提示信息 roomLoadingLabel.SetActive (false); //禁用游戏房间加载提示信息 if(createRoomPanel!=null) createRoomPanel.SetActive (false); //禁用创建房间面板 //获取房间信息面板 RectTransform rectTransform = roomMessagePanel.GetComponent<RectTransform> (); roomPerPage = rectTransform.childCount; //获取房间信息面板的条目数 //初始化每条房间信息条目 roomMessage = new GameObject[roomPerPage]; for (int i = 0; i < roomPerPage; i++) { roomMessage [i] = rectTransform.GetChild (i).gameObject; roomMessage [i].SetActive (false); //禁用房间信息条目 } backButton.onClick.RemoveAllListeners (); //移除返回按钮绑定的所有监听事件 backButton.onClick.AddListener (delegate() { //为返回按钮绑定新的监听事件 PhotonNetwork.Disconnect(); //断开客户端与Photon服务器的连接 loginPanel.SetActive(true); //启用游戏登录面板 lobbyPanel.SetActive(false); //禁用游戏大厅面板 userMessage.SetActive (false); //禁用玩家昵称信息 backButton.gameObject.SetActive (false); //禁用返回按钮 }); if(roomPanel!=null) roomPanel.SetActive (false); //禁用游戏房间面板 } /**覆写IPunCallback回调函数,当玩家进入游戏大厅时调用 * 禁用游戏大厅加载提示 */ public override void OnJoinedLobby(){ lobbyLoadingLabel.SetActive (false); } /**覆写IPunCallback回调函数,当玩家进入游戏房间时调用 * 禁用游戏大厅面板,启用游戏房间面板 */ public override void OnJoinedRoom(){ lobbyPanel.SetActive (false); roomPanel.SetActive (true); } /**覆写IPunCallback回调函数,当客户端连接到MasterServer时调用 * 加入默认游戏大厅 * 效果等同于勾选PhotonServerSettings中的Auto-join Lobby public override void OnConnectedToMaster () { PhotonNetwork.JoinLobby (); } */ /**覆写IPunCallback回调函数,当房间列表更新时调用 * 更新游戏大厅中房间列表的显示 */ public override void OnReceivedRoomListUpdate(){ roomInfo = PhotonNetwork.GetRoomList (); //获取游戏大厅中的房间列表 maxPageNumber = (roomInfo.Length - 1) / roomPerPage + 1; //计算房间总页数 if (currentPageNumber > maxPageNumber) //如果当前页大于房间总页数时 currentPageNumber = maxPageNumber; //将当前房间页设为房间总页数 pageMessage.text = currentPageNumber.ToString () + "/" + maxPageNumber.ToString (); //更新房间页数信息的显示 ButtonControl (); //翻页按钮控制 ShowRoomMessage (); //显示房间信息 if (roomInfo.Length == 0) { randomJoinButton.interactable = false; //如果房间数为0,禁用"随机进入房间"按钮的交互功能 } else randomJoinButton.interactable = true; //如果房间数不为0,启用"随机进入房间"按钮的交互功能 } //显示房间信息 void ShowRoomMessage(){ int start, end, i, j; start = (currentPageNumber - 1) * roomPerPage; //计算需要显示房间信息的起始序号 if (currentPageNumber * roomPerPage < roomInfo.Length) //计算需要显示房间信息的末尾序号 end = currentPageNumber * roomPerPage; else end = roomInfo.Length; //依次显示每条房间信息 for (i = start,j = 0; i < end; i++,j++) { RectTransform rectTransform = roomMessage [j].GetComponent<RectTransform> (); string roomName = roomInfo [i].name; //获取房间名称 rectTransform.GetChild (0).GetComponent<Text> ().text = (i + 1).ToString (); //显示房间序号 rectTransform.GetChild (1).GetComponent<Text> ().text = roomName; //显示房间名称 rectTransform.GetChild (2).GetComponent<Text> ().text = roomInfo [i].playerCount + "/" + roomInfo [i].maxPlayers; //显示房间人数 Button button = rectTransform.GetChild (3).GetComponent<Button> (); //获取"进入房间"按钮组件 //如果游戏房间人数已满,或者游戏房间的Open属性为false(房间内游戏已开始),表示房间无法加入,禁用"进入房间"按钮 if (roomInfo [i].playerCount == roomInfo [i].maxPlayers || roomInfo [i].open == false) button.gameObject.SetActive (false); //如果房间可以加入,启用"进入房间"按钮,给按钮绑定新的监听事件,加入此房间 else { button.gameObject.SetActive (true); button.onClick.RemoveAllListeners (); button.onClick.AddListener (delegate() { ClickJoinRoomButton (roomName); }); } roomMessage [j].SetActive (true); //启用房间信息条目 } //禁用不显示的房间信息条目 while (j < 4) { roomMessage [j++].SetActive (false); } } //翻页按钮控制函数 void ButtonControl(){ //如果当前页为1,禁用"上一页"按钮;否则,启用"上一页"按钮 if (currentPageNumber == 1) previousButton.SetActive (false); else previousButton.SetActive (true); //如果当前页等于房间总页数,禁用"下一页"按钮;否则,启用"下一页"按钮 if (currentPageNumber == maxPageNumber) nextButton.SetActive (false); else nextButton.SetActive (true); } //"创建房间"按钮事件处理函数,启用创建房间面板 public void ClickCreateRoomButton(){ createRoomPanel.SetActive (true); } //"随机进入房间"按钮事件处理函数,玩家随机加入大厅中的房间,启用游戏房间加载提示信息 public void ClickRandomJoinButton(){ PhotonNetwork.JoinRandomRoom (); roomLoadingLabel.SetActive (true); } //"上一页"按钮事件处理函数 public void ClickPreviousButton(){ currentPageNumber--; //当前房间页减一 pageMessage.text = currentPageNumber.ToString () + "/" + maxPageNumber.ToString (); //更新房间页数显示 ButtonControl (); //当前房间页更新,调动翻页控制函数 ShowRoomMessage (); //当前房间页更新,重新显示房间信息 } //"下一页"按钮事件处理函数 public void ClickNextButton(){ currentPageNumber++; //当前房间页加一 pageMessage.text = currentPageNumber.ToString () + "/" + maxPageNumber.ToString (); //更新房间页数显示 ButtonControl (); //当前房间页更新,调动翻页控制函数 ShowRoomMessage (); //当前房间页更新,重新显示房间信息 } //"进入房间"按钮事件处理函数 public void ClickJoinRoomButton(string roomName){ PhotonNetwork.JoinRoom(roomName); //根据房间名加入游戏房间 roomLoadingLabel.SetActive (true); //启用房间加载提示信息 } }3.创建房间
using UnityEngine; using System.Collections; using UnityEngine.UI; public class CreateRoomController : MonoBehaviour { public GameObject createRoomPanel; //创建房间面板 public GameObject roomLoadingPanel; //禁用游戏房间加载提示信息 public Text roomName; //房间名称文本 public Text roomNameHint; //房间名称提示文本 public GameObject maxPlayerToggle; //最大玩家个数开关组 private byte[] maxPlayerNum = { 2, 4 }; //最大玩家个数 //创建房间面板激活时调用 void OnEnable(){ roomNameHint.text = ""; //清空房间名称提示文本 } //"确认创建"按钮事件处理函数 public void ClickConfirmCreateRoomButton(){ RoomOptions roomOptions=new RoomOptions(); RectTransform toggleRectTransform = maxPlayerToggle.GetComponent<RectTransform> (); int childCount = toggleRectTransform.childCount; //根据最大玩家个数开关组的打开情况,确认房间最大玩家个数 for (int i = 0; i < childCount; i++) { if (toggleRectTransform.GetChild (i).GetComponent<Toggle> ().isOn == true) { roomOptions.maxPlayers = maxPlayerNum [i]; break; } } RoomInfo[] roomInfos = PhotonNetwork.GetRoomList(); //获取游戏大厅内所有游戏房间 bool isRoomNameRepeat = false; //遍历游戏房间,检查新创建的房间名是否与已有房间重复 foreach (RoomInfo info in roomInfos) { if (roomName.text == info.name) { isRoomNameRepeat = true; break; } } //如果房间名称重复,房间名称提示文本显示"房间名称重复!" if (isRoomNameRepeat) { roomNameHint.text = "房间名称重复!"; } //否则,根据玩家设置的房间名、房间玩家人数创建房间 else { PhotonNetwork.CreateRoom (roomName.text, roomOptions, TypedLobby.Default); //在默认游戏大厅中创建游戏房间 createRoomPanel.SetActive (false); //禁用创建房间面板 roomLoadingPanel.SetActive (true); //启用游戏房间加载提示信息 } } //"取消创建"按钮事件处理函数 public void ClickCancelCreateRoomButton(){ createRoomPanel.SetActive (false); //禁用创建房间面板 roomNameHint.text = ""; //清空房间名称提示文本 } }
4.房间面板
using UnityEngine; using System.Collections; using UnityEngine.UI; using Photon; public class RoomPanelController : PunBehaviour { public GameObject lobbyPanel; //游戏大厅面板 public GameObject roomPanel; //游戏房间面板 public Button backButton; //返回按钮 public Text roomName; //房间名称文本 public GameObject[] Team1; //队伍1面板(显示队伍1信息) public GameObject[] Team2; //队伍2面板(显示队伍2信息) public Button readyButton; //准备/开始游戏按钮 public Text promptMessage; //提示信息 PhotonView pView; int teamSize; Text[] texts; ExitGames.Client.Photon.Hashtable costomProperties; void OnEnable () { pView = GetComponent<PhotonView>(); //获取PhotonView组件 if(!PhotonNetwork.connected)return; roomName.text = "房间:" + PhotonNetwork.room.name; //显示房间名称 promptMessage.text = ""; //提示信息 backButton.onClick.RemoveAllListeners (); //移除返回按钮绑定的所有监听事件 backButton.onClick.AddListener (delegate() { //为返回按钮绑定新的监听事件 PhotonNetwork.LeaveRoom (); //客户端离开游戏房间 lobbyPanel.SetActive (true); //激活游戏大厅面板 roomPanel.SetActive (false); //禁用游戏房间面板 }); teamSize = PhotonNetwork.room.maxPlayers / 2; //计算每队人数 DisableTeamPanel (); //初始化队伍面板 UpdateTeamPanel (false); //更新队伍面板(false表示不显示本地玩家信息) //交替寻找两队空余位置,将玩家信息放置在对应空位置中 for (int i = 0; i < teamSize; i++) { if (!Team1 [i].activeSelf) { //在队伍1找到空余位置 Team1 [i].SetActive (true); //激活对应的队伍信息UI texts = Team1 [i].GetComponentsInChildren<Text> (); texts [0].text = PhotonNetwork.playerName; //显示玩家昵称 if(PhotonNetwork.isMasterClient)texts[1].text="房主"; //如果玩家是MasterClient,玩家状态显示"房主" else texts [1].text = "未准备"; //如果玩家不是MasterClient,玩家状态显示"未准备" costomProperties = new ExitGames.Client.Photon.Hashtable () { //初始化玩家自定义属性 { "Team","Team1" }, //玩家队伍 { "TeamNum",i }, //玩家队伍序号 { "isReady",false }, //玩家准备状态 { "Score",0 } //玩家得分 }; PhotonNetwork.player.SetCustomProperties (costomProperties); //将玩家自定义属性赋予玩家 break; } else if (!Team2 [i].activeSelf) { //在队伍2找到空余位置 Team2 [i].SetActive (true); //激活对应的队伍信息UI texts = Team2 [i].GetComponentsInChildren<Text> (); //显示玩家昵称 if(PhotonNetwork.isMasterClient)texts[1].text="房主"; //如果玩家是MasterClient,玩家状态显示"房主" else texts [1].text = "未准备"; //如果玩家不是MasterClient,玩家状态显示"未准备" costomProperties = new ExitGames.Client.Photon.Hashtable () { //初始化玩家自定义属性 { "Team","Team2" }, //玩家队伍 { "TeamNum",i }, //玩家队伍序号 { "isReady",false }, //玩家准备状态 { "Score",0 } //玩家得分 }; PhotonNetwork.player.SetCustomProperties (costomProperties); //将玩家自定义属性赋予玩家 break; } } ReadyButtonControl (); //设置ReadyButton的按钮事件 } /**覆写IPunCallback回调函数,当玩家属性更改时调用 * 更新队伍面板中显示的玩家信息 */ public override void OnPhotonPlayerPropertiesChanged(object[] playerAndUpdatedProps){ DisableTeamPanel (); //禁用队伍面板 UpdateTeamPanel (true); //根据当前玩家信息在队伍面板显示所有玩家信息(true表示显示本地玩家信息) } /**覆写IPunCallback回调函数,当MasterClient更变时调用 * 设置ReadyButton的按钮事件 */ public override void OnMasterClientSwitched (PhotonPlayer newMasterClient) { ReadyButtonControl (); } /**覆写IPunCallback回调函数,当有玩家离开房间时调用 * 更新队伍面板中显示的玩家信息 */ public override void OnPhotonPlayerDisconnected(PhotonPlayer otherPlayer){ DisableTeamPanel (); //唧哝队伍面板 UpdateTeamPanel (true); //根据当前玩家信息在队伍面板显示所有玩家信息(true表示显示本地玩家信息) } //禁用队伍面板 void DisableTeamPanel(){ for (int i = 0; i < Team1.Length; i++) { Team1 [i].SetActive (false); } for (int i = 0; i < Team2.Length; i++) { Team2 [i].SetActive (false); } } /**在队伍面板显示玩家信息 * 函数参数表示是否显示本地玩家信息 */ void UpdateTeamPanel(bool isUpdateSelf){ GameObject go; foreach (PhotonPlayer p in PhotonNetwork.playerList) { //获取房间里所有玩家信息 if (!isUpdateSelf && p.isLocal) continue; //判断是否更新本地玩家信息 costomProperties = p.customProperties; //获取玩家自定义属性 if (costomProperties ["Team"].Equals ("Team1")) { //判断玩家所属队伍 go = Team1 [(int)costomProperties ["TeamNum"]]; //查询玩家的队伍序号 go.SetActive (true); //激活显示玩家信息的UI texts = go.GetComponentsInChildren<Text> (); //获取显示玩家信息的Text组件 } else { go = Team2 [(int)costomProperties ["TeamNum"]]; go.SetActive (true); texts = go.GetComponentsInChildren<Text> (); } texts [0].text = p.name; //显示玩家姓名 if(p.isMasterClient) //如果玩家是MasterClient texts[1].text="房主"; //玩家状态显示"房主" else if ((bool)costomProperties ["isReady"]) { //如果玩家不是MasterClient,获取玩家的准备状态isReady texts [1].text = "已准备"; //isReady为true,显示"已准备" } else texts [1].text = "未准备"; //isReady为false,显示"未准备" } } //ReadyButton按钮事件设置 void ReadyButtonControl(){ if (PhotonNetwork.isMasterClient) { //如果玩家是MasterClient readyButton.GetComponentInChildren<Text> ().text = "开始游戏"; //ReadyButton显示"开始游戏" readyButton.onClick.RemoveAllListeners (); //移除ReadyButton所有监听事件 readyButton.onClick.AddListener (delegate() { //为ReadyButton绑定新的监听事件 ClickStartGameButton (); //开始游戏 }); } else { //如果玩家不是MasterClient if((bool)PhotonNetwork.player.customProperties["isReady"]) //根据玩家准备状态显示对应的文本信息 readyButton.GetComponentInChildren<Text> ().text = "取消准备"; else readyButton.GetComponentInChildren<Text> ().text = "准备"; readyButton.onClick.RemoveAllListeners (); //移除ReadyButton所有监听事件 readyButton.onClick.AddListener (delegate() { //为ReadyButton绑定新的监听事件 ClickReadyButton (); //切换准备状态 }); } } //"切换队伍"按钮 public void ClickSwitchButton(){ costomProperties = PhotonNetwork.player.customProperties; //获取玩家自定义属性 if ((bool)costomProperties ["isReady"]) { //如果玩家处于准备状态 promptMessage.text="准备状态下不能切换队伍"; //提示信息显示玩家不能切换队伍 return; //结束函数的执行 } bool isSwitched = false; //标记玩家切换队伍是否成功,默认值表示不成功 if (costomProperties ["Team"].ToString ().Equals ("Team1")) { //判断玩家队伍 for (int i = 0; i < teamSize; i++) { //寻找另一支队伍是否有空余位置 if (!Team2 [i].activeSelf) { //如果找到了空缺位置 isSwitched = true; //标记玩家切换队伍成功 Team1 [(int)costomProperties ["TeamNum"]].SetActive (false); //禁用之前显示玩家信息的UI texts = Team2 [i].GetComponentsInChildren<Text> (); //获取切换队伍后,显示玩家信息的Text组件 texts [0].text = PhotonNetwork.playerName; //填入玩家昵称 if(PhotonNetwork.isMasterClient)texts[1].text="房主"; //如果玩家是MasterClient,玩家状态显示"房主" else texts [1].text = "未准备"; //如果玩家不是MasterClient,玩家状态显示"未准备" Team2 [i].SetActive (true); //激活显示玩家信息的UI costomProperties = new ExitGames.Client.Photon.Hashtable () //重新设置玩家的自定义属性 { { "Team","Team2" }, { "TeamNum",i } }; PhotonNetwork.player.SetCustomProperties (costomProperties); break; } } } else if (costomProperties ["Team"].ToString ().Equals ("Team2")) { //判断玩家队伍 for (int i = 0; i < teamSize; i++) { //寻找另一支队伍是否有空余位置 if (!Team1 [i].activeSelf) { //如果找到了空缺位置 isSwitched = true; //标记玩家切换队伍成功 Team2 [(int)(costomProperties ["TeamNum"])].SetActive (false); //禁用之前显示玩家信息的UI texts = Team1 [i].GetComponentsInChildren<Text> (); //获取切换队伍后,显示玩家信息的Text组件 texts [0].text = PhotonNetwork.playerName; //填入玩家昵称 if(PhotonNetwork.isMasterClient)texts[1].text="房主"; //如果玩家是MasterClient,玩家状态显示"房主" else texts [1].text = "未准备"; //如果玩家不是MasterClient,玩家状态显示"未准备" Team1 [i].SetActive (true); //激活显示玩家信息的UI costomProperties = new ExitGames.Client.Photon.Hashtable () //重新设置玩家的自定义属性 { { "Team","Team1" }, { "TeamNum",i } }; PhotonNetwork.player.SetCustomProperties (costomProperties); break; } } } if (!isSwitched) promptMessage.text = "另一队伍已满,无法切换"; //如果玩家切换队伍失败,提示信息显示"另一队伍已满,无法切换" else promptMessage.text = ""; } //准备按钮事件响应函数 public void ClickReadyButton(){ bool isReady = (bool)PhotonNetwork.player.customProperties ["isReady"]; //获取玩家准备状态 costomProperties = new ExitGames.Client.Photon.Hashtable (){ { "isReady",!isReady } }; //重新设置玩家准备状态 PhotonNetwork.player.SetCustomProperties (costomProperties); Text readyButtonText = readyButton.GetComponentInChildren<Text> (); //获取ReadyButton的按钮文本 if(isReady)readyButtonText.text="准备"; //根据玩家点击按钮后的状态,设置按钮文本的显示 else readyButtonText.text="取消准备"; } //开始游戏按钮事件响应函数 public void ClickStartGameButton(){ foreach (PhotonPlayer p in PhotonNetwork.playerList) { //遍历房间内所有玩家 if (p.isLocal) continue; //不检查MasterClient房主的准备状态 if ((bool)p.customProperties ["isReady"] == false) { //如果有人未准备 promptMessage.text = "有人未准备,游戏无法开始"; //提示信息显示"有人未准备,游戏无法开始" return; //结束函数执行 } } promptMessage.text = ""; //清空提示信息 PhotonNetwork.room.open = false; //设置房间的open属性,使游戏大厅的玩家无法加入此房间 pView.RPC ("LoadGameScene", PhotonTargets.All, "GameScene"); //调用RPC,让游戏房间内所有玩家加载场景GameScene,开始游戏 } //RPC函数,玩家加载场景 [PunRPC] public void LoadGameScene(string sceneName){ PhotonNetwork.LoadLevel (sceneName); //加载场景名为sceneName的场景 } }
5.玩家信息
using System.Collections; using System; public class PlayerInfo:IComparable<PlayerInfo>{ //PlayerInfo实现IComparable接口 public string playerName; //玩家姓名 public int playerScore; //玩家得分 //无参构造函数 public PlayerInfo(){ } //构造函数:玩家姓名,玩家得分 public PlayerInfo(string _playerName,int _playerScore){ playerName = _playerName; playerScore = _playerScore; } //实现IComparable接口的CompareTo函数,完成PlayerInfo对象的比较 public int CompareTo(PlayerInfo other){ if (this.playerScore > other.playerScore) return 1; else if (this.playerScore == other.playerScore) return 0; else return -1; } }6.枪械同步
using UnityEngine; using System.Collections; using Photon; public class GunController : PunBehaviour { Vector3 m_position; Quaternion m_rotation; float lerpSpeed = 10.0f; //内插速度 //初始化玩家位置与朝向 void Start(){ m_position = transform.position; m_rotation = transform.rotation; } //序列化发送、获取数据 void OnPhotonSerializeView(PhotonStream stream,PhotonMessageInfo info){ if (stream.isWriting) //本地玩家发送数据 { stream.SendNext (transform.position); stream.SendNext (transform.rotation); } else //远程玩家接收数据 { m_position = (Vector3)stream.ReceiveNext(); m_rotation = (Quaternion)stream.ReceiveNext(); } } void Update () { if (!photonView.isMine) //如果玩家对象不属于本地玩家,需要根据接收的数据更新玩家对象的位置与朝向 { transform.position = Vector3.Lerp (transform.position, m_position, Time.deltaTime * lerpSpeed); //使用Lerp函数实现玩家的平滑移动 transform.rotation = Quaternion.Lerp (transform.rotation, m_rotation, Time.deltaTime * lerpSpeed); //使用Lerp函数实现玩家的平滑转动 } } }
7.游戏管理器
using UnityEngine; using System.Collections; using System.Collections.Generic; using Photon; using UnityEngine.UI; public class GameManager : PunBehaviour { public static GameManager gm; //GameManager静态实例 public enum GameState{ //游戏状态枚举 PreStart, //游戏开始前 Playing, //游戏进行中 GameWin, //游戏胜利 GameLose, //游戏失败 Tie}; //平手 public GameState state = GameState.PreStart; //初始化游戏状态 public Transform[] teamOneSpawnTransform; //队伍1出生位置 public Transform[] teamTwoSpawnTransform; //队伍2出生位置 public float checkplayerTime = 5.0f; //检查玩家加载场景时间 public float gamePlayingTime = 600.0f; //游戏时间 public float gameOverTime = 10.0f; //游戏结束时间 public float spawnTime = 5.0f; //重生时间 public int targetScore = 50; //目标分数 public Text timeLabel; //倒计时时间显示 public Text targetScoreLabel; //目标分数显示 //实时计分板 public Text Team1RealTimeScorePanelScore; public Text Team2RealTimeScorePanelScore; public GameObject scorePanel; //玩家得分榜 public Text teamOneTotal; //队伍1总分 public Text teamTwoTotal; //队伍2总分 public GameObject[] teamOneScorePanel; //队伍1成员得分面板 public GameObject[] teamTwoScorePanel; //队伍2成员得分面板 public Text gameResult; //游戏结束信息 public Slider hpSlider; //玩家血条 public AudioClip gameStartAudio; //游戏开始音效 public AudioClip gameWinAudio; //游戏胜利音效 public AudioClip gameLoseAudio; //游戏失败音效 public AudioClip tieAudio; //平手音效 double startTimer = 0; //倒计时开始时间 double endTimer = 0; //倒计时结束时间 double countDown = 0; //倒计时 int loadedPlayerNum = 0; //已加载场景的玩家个数 int currentScoreOfTeam1 = 0; //队伍1得分 int currentScoreOfTeam2 = 0; //队伍2得分 const float photonCircleTime = 4294967.295f; //Photon服务器循环时间 Camera mainCamera; GameObject localPlayer = null; ExitGames.Client.Photon.Hashtable playerCustomProperties; PlayerHealth playerHealth; //初始化 void Start () { gm = GetComponent<GameManager> (); //初始化GameManager静态实例gm mainCamera = Camera.main; //获取摄像机 photonView.RPC ("ConfirmLoad", PhotonTargets.All); //使用RPC,告知所有玩家有一名玩家已成功加载场景 playerCustomProperties = new ExitGames.Client.Photon.Hashtable{ { "Score",0 } }; //初始化玩家得分 PhotonNetwork.player.SetCustomProperties (playerCustomProperties); targetScoreLabel.text = "目标得分:" + targetScore.ToString (); //显示目标分数 //初始化队伍得分 currentScoreOfTeam1 = 0; currentScoreOfTeam2 = 0; UpdateScores (currentScoreOfTeam1, currentScoreOfTeam2); //更新玩家得分榜 if (PhotonNetwork.isMasterClient) //MasterClient设置游戏开始倒计时 photonView.RPC ("SetTime", PhotonTargets.All, PhotonNetwork.time, checkplayerTime); gameResult.text = ""; //清空游戏结果 scorePanel.SetActive (false); //禁用玩家得分榜 } //RPC函数,增加成功加载场景的玩家个数 [PunRPC] void ConfirmLoad(){ loadedPlayerNum++; } //每帧执行一次,更新倒计时,控制游戏状态 void Update(){ countDown = endTimer - PhotonNetwork.time; //计算倒计时 if (countDown >= photonCircleTime) //防止entTimer值超过Photon服务器循环时间,确保倒计时能正确结束 countDown -= photonCircleTime; UpdateTimeLabel (); //更新倒计时的显示 //游戏状态控制 switch (state) { //如果游戏处于游戏开始前 case GameState.PreStart: if (PhotonNetwork.isMasterClient) { //MasterClient检查倒计时和场景加载人数,控制游戏开始 CheckPlayerConnected (); } break; //如果游戏处于游戏进行中 case GameState.Playing: hpSlider.value = playerHealth.currentHP; //更新玩家生命值血条的显示 #if(!UNITY_ANDROID) scorePanel.SetActive (Input.GetKey (KeyCode.Tab)); //PC使用Tab键显示玩家得分榜 #endif if (PhotonNetwork.isMasterClient) { //MasterClient检查游戏状态 if (currentScoreOfTeam1 >= targetScore) //队伍1达到目标分数,队伍1获胜 photonView.RPC ("EndGame", PhotonTargets.All, "Team1",PhotonNetwork.time); else if (currentScoreOfTeam2 >= targetScore) //队伍2达到目标分数,队伍2获胜 photonView.RPC ("EndGame", PhotonTargets.All, "Team2",PhotonNetwork.time); else if (countDown <= 0.0f) { //游戏倒计时结束,得分高的队伍获胜 if (currentScoreOfTeam1 > currentScoreOfTeam2) photonView.RPC ("EndGame", PhotonTargets.All, "Team1", PhotonNetwork.time); else if (currentScoreOfTeam1 < currentScoreOfTeam2) photonView.RPC ("EndGame", PhotonTargets.All, "Team2",PhotonNetwork.time); else //双方得分相同,平手 photonView.RPC ("EndGame", PhotonTargets.All, "Tie",PhotonNetwork.time); } } break; case GameState.GameWin: //游戏胜利状态,倒计时结束,退出游戏房间 if (countDown <= 0) LeaveRoom (); break; case GameState.GameLose: //游戏结束状态,倒计时结束,退出游戏房间 if (countDown <= 0) LeaveRoom (); break; case GameState.Tie: if (countDown <= 0) //平手状态,倒计时结束,退出游戏房间 LeaveRoom (); break; } } /**IPunCallback回调函数,有玩家断开连接时(离开房间)调用 * MasterClient检查双方人数,更新玩家得分榜的显示 */ public override void OnPhotonPlayerDisconnected(PhotonPlayer other){ if (state != GameState.Playing) //游戏状态不是游戏进行中,结束函数执行 return; if (PhotonNetwork.isMasterClient) { //MasterClient检查 CheckTeamNumber (); //检查两队人数 //更新玩家得分榜 photonView.RPC ("UpdateScores", PhotonTargets.All, currentScoreOfTeam1, currentScoreOfTeam2); } } /**检查加载场景的玩家个数 * 该函数只由MasterClient调用 */ void CheckTeamNumber(){ PhotonPlayer[] players = PhotonNetwork.playerList; //获取房间内玩家列表 int teamOneNum = 0, teamTwoNum = 0; foreach (PhotonPlayer p in players) { //遍历所有玩家,计算两队人数 if (p.customProperties ["Team"].ToString () == "Team1") teamOneNum++; else teamTwoNum++; } //如果有某队伍人数为0,另一队获胜 if (teamOneNum == 0) photonView.RPC ("EndGame", PhotonTargets.All, "Team2",PhotonNetwork.time); else if (teamTwoNum == 0) photonView.RPC ("EndGame", PhotonTargets.All, "Team1",PhotonNetwork.time); } //显示两队实时分数 void UpdateRealTimeScorePanel() { string team1Title = string.Empty; string team2Title = string.Empty; team1Title = string.Format("得分:{0}",currentScoreOfTeam1); team2Title = string.Format("得分:{0}",currentScoreOfTeam2); Team1RealTimeScorePanelScore.text = team1Title; Team2RealTimeScorePanelScore.text = team2Title; } //显示倒计时时间 void UpdateTimeLabel(){ int minute = (int)countDown / 60; int second = (int)countDown % 60; timeLabel.text = minute.ToString ("00") + ":" + second.ToString ("00"); } //检查所有玩家是否已经加载场景 void CheckPlayerConnected(){ if (countDown <=0.0f || loadedPlayerNum == PhotonNetwork.playerList.Length) { //游戏开始倒计时结束,或者所有玩家加载场景 startTimer = PhotonNetwork.time; //游戏开始时间(使用Photon服务器的时间) photonView.RPC ("StartGame",PhotonTargets.All,startTimer); //使用RPC,所有玩家开始游戏 } } //RPC函数,开始游戏 [PunRPC] void StartGame(double timer){ SetTime(timer,gamePlayingTime); //设置游戏进行倒计时时间 gm.state = GameState.Playing; //游戏状态切换到游戏进行状态 InstantiatePlayer (); //创建玩家对象 AudioSource.PlayClipAtPoint(gameStartAudio, localPlayer.transform.position); //播放游戏开始音效 } //RPC函数,设置倒计时时间 [PunRPC] void SetTime(double sTime,float dTime){ startTimer = sTime; endTimer = sTime + dTime; } //生成玩家对象 void InstantiatePlayer(){ playerCustomProperties= PhotonNetwork.player.customProperties; //获取玩家自定义属性 //如果玩家属于队伍1,生成EthanPlayer对象 if (playerCustomProperties ["Team"].ToString ().Equals ("Team1")) { localPlayer = PhotonNetwork.Instantiate ("EthanPlayer", teamOneSpawnTransform [(int)playerCustomProperties ["TeamNum"]].position, Quaternion.identity, 0); } //如果玩家属于队伍2,生成RobotPlayer对象 else if (PhotonNetwork.player.customProperties ["Team"].ToString ().Equals ("Team2")) { localPlayer = PhotonNetwork.Instantiate ("RobotPlayer", teamTwoSpawnTransform [(int)playerCustomProperties ["TeamNum"]].position, Quaternion.identity, 0); } localPlayer.GetComponent<PlayerMove> ().enabled = true; //启用PlayerMove脚本,使玩家对象可以被本地客户端操控 PlayerShoot playerShoot = localPlayer.GetComponent<PlayerShoot> (); //获取玩家对象的PlayerShoot脚本 playerHealth = localPlayer.GetComponent<PlayerHealth> (); //获取玩家对象的PlayerHealth脚本 hpSlider.maxValue = playerHealth.maxHP; //设置显示玩家血量的Slider控件 hpSlider.minValue = 0; hpSlider.value = playerHealth.currentHP; Transform tempTransform = localPlayer.transform; mainCamera.transform.parent = tempTransform; //将场景中的摄像机设为玩家对象的子对象 mainCamera.transform.localPosition = playerShoot.shootingPosition; //设置摄像机的位置,为PlayerShoot脚本中的射击起始位置 mainCamera.transform.localRotation = Quaternion.identity; //设置摄像机的朝向 for (int i = 0; i < tempTransform.childCount; i++) { //将枪械对象设为摄像机的子对象 if (tempTransform.GetChild (i).name.Equals ("Gun")) { tempTransform.GetChild (i).parent = mainCamera.transform; break; } } } /**玩家得分增加函数 * 该函数只由MasterClient调用 */ public void AddScore(int killScore, PhotonPlayer p){ if (!PhotonNetwork.isMasterClient) //如果函数不是由MasterClient调用,结束函数执行 return; int score = (int)p.customProperties ["Score"]; //获取击杀者玩家得分 score += killScore; //增加击杀者玩家得分 playerCustomProperties = new ExitGames.Client.Photon.Hashtable{ { "Score",score } }; p.SetCustomProperties (playerCustomProperties); if (p.customProperties ["Team"].ToString () == "Team1") currentScoreOfTeam1 += killScore; //增加队伍1总分 else currentScoreOfTeam2 += killScore; //增加队伍2总分 //使用RPC,更新所有客户端的玩家得分榜 photonView.RPC ("UpdateScores",PhotonTargets.All,currentScoreOfTeam1,currentScoreOfTeam2); } //RPC函数,更新玩家得分榜 [PunRPC] void UpdateScores(int teamOneScore,int teamTwoScore){ //禁用所有显示玩家得分的条目 foreach (GameObject go in teamOneScorePanel) go.SetActive (false); foreach (GameObject go in teamTwoScorePanel) go.SetActive (false); //更新队伍1、队伍2的总分 currentScoreOfTeam1 = teamOneScore; currentScoreOfTeam2 = teamTwoScore; PhotonPlayer[] players = PhotonNetwork.playerList; //获取房间内所有玩家的信息 List<PlayerInfo> teamOne = new List<PlayerInfo>(); List<PlayerInfo> teamTwo = new List<PlayerInfo>(); PlayerInfo tempPlayer; //遍历房间内所有玩家,将他们的得分根据他们的队伍放入对应的队伍列表中 foreach (PhotonPlayer p in players) { tempPlayer = new PlayerInfo (p.name, (int)p.customProperties ["Score"]); if (p.customProperties ["Team"].ToString () == "Team1") teamOne.Add (tempPlayer); else teamTwo.Add (tempPlayer); } //分别对两队队伍列表排序,按照分数从大到小排序 teamOne.Sort (); teamTwo.Sort (); Text[] texts; int length = teamOne.Count; //依次在玩家得分榜显示两队玩家得分,保证得分高的玩家在得分低的玩家之上 for (int i = 0; i < length; i++) { texts = teamOneScorePanel [i].GetComponentsInChildren<Text> (); texts [0].text = teamOne [i].playerName; texts [1].text = teamOne [i].playerScore.ToString(); teamOneScorePanel [i].SetActive (true); } length = teamTwo.Count; for (int i = 0; i < length; i++) { texts = teamTwoScorePanel [i].GetComponentsInChildren<Text> (); texts [0].text = teamTwo [i].playerName; texts [1].text = teamTwo [i].playerScore.ToString(); teamTwoScorePanel [i].SetActive (true); } //显示两队得分 teamOneTotal.text = "Team1:" + currentScoreOfTeam1.ToString (); teamTwoTotal.text = "Team2:" + currentScoreOfTeam2.ToString (); UpdateRealTimeScorePanel(); //更新实时得分榜 } //游戏结束,更改客户端的游戏状态 [PunRPC] void EndGame(string winTeam,double timer){ //如果两队不是平手,游戏结束信息显示获胜队伍胜利 if (winTeam != "Tie") gameResult.text = winTeam + " Wins!"; if (winTeam == "Tie") //如果两队打平 { gm.state = GameState.Tie; //游戏状态切换为平手状态 AudioSource.PlayClipAtPoint (tieAudio, localPlayer.transform.position); //播放平手音效 gameResult.text = "Tie!"; //游戏结束信息显示"Tie!"表示平手 } else if (winTeam == PhotonNetwork.player.customProperties ["Team"].ToString ()) //如果玩家属于获胜队伍 { gm.state = GameState.GameWin; //游戏状态切换为游戏胜利状态 //播放游戏胜利音效 AudioSource.PlayClipAtPoint (gameWinAudio,localPlayer.transform.position); } else //如果玩家属于失败队伍 { gm.state = GameState.GameLose; //游戏状态切换为游戏失败状态 //播放游戏失败音效 AudioSource.PlayClipAtPoint (gameLoseAudio, localPlayer.transform.position); } scorePanel.SetActive(true); //游戏结束后,显示玩家得分榜 SetTime (timer, gameOverTime); //设置游戏结束倒计时时间 } //本地玩家加分 public void localPlayerAddHealth(int points){ PlayerHealth ph = localPlayer.GetComponent<PlayerHealth> (); ph.requestAddHP (points); } //如果玩家断开与Photon服务器的连接,加载场景GameLobby public override void OnConnectionFail(DisconnectCause cause){ PhotonNetwork.LoadLevel ("GameLobby"); } //显示玩家得分榜,用于移动端的得分榜按钮,代替PC的Tab键 public void ShowScorePanel(){ scorePanel.SetActive (!scorePanel.activeSelf); } //离开房间函数 public void LeaveRoom(){ PhotonNetwork.LeaveRoom (); //玩家离开游戏房间 PhotonNetwork.LoadLevel ("GameLobby"); //加载场景GameLobby } }