下面的代碼內(nèi)容還是根據(jù)上一篇文章的代碼做更改和添加內(nèi)容
首先我們搭建一個簡單的場景.把游戲物體Player做成一個預(yù)制物體Prefab.,創(chuàng)建一個空物體用來掛載各個請求發(fā)送和響應(yīng)的腳本.
首先我們在PlayerController上創(chuàng)建一個腳本Player,打開腳本寫入控制物體移動的代碼.
using System.Collections.Generic;
using UnityEngine;
using Common;
using Common.Tools;
public class Player : MonoBehaviour {
void Start () {
//設(shè)置本地的Player的顏色設(shè)置成綠色
player. GetComponent<Renderer>().material.color = Color.green;
}
void Update () {
//只有本地的Player贞瞒,可以控制移動
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
player.transform.Translate(new Vector3(h,0,v)*Time.deltaTime*4);
}
這樣Player移動的代碼就完成了,并且運(yùn)行起來本地的Player為綠色.
同步主角的位置信息并發(fā)送給服務(wù)器端
我們要實現(xiàn)位置信息的同步就需要獲取的主角的位置信息然后時時發(fā)送給服務(wù)器,再將位置信息由服務(wù)器同步到各個客戶端去趁曼,這樣我們的位置信息的同步才算完成军浆。首先需要發(fā)送請求就需要OperationCode作為編號發(fā)送給服務(wù)器,所以現(xiàn)在在服務(wù)器里面的Common項目下添加一些OperationCode挡闰、EventCode乒融、ParameterCode等等的枚舉類型
namespace Common
{
public enum EventCode :byte//區(qū)分服務(wù)器向客戶端發(fā)送的事件的類型
{
NewPlayer,
SyncPosition
}
}
namespace Common
{
public enum OperationCode:byte//區(qū)分請求和響應(yīng)的類型
{
Login,
Register,
Default,
SyncPosition,
SyncPlayer
}
}
namespace Common
{
public enum ParameterCode:byte//區(qū)分傳送數(shù)據(jù)時候參數(shù)的類型
{
Username,
Password,
Position,
x,y,z,
UsernameList,
PlayerDataList
}
}
namespace Common
{
public enum ReturnCode:short//區(qū)分請求返回值,成功或者失敗
{
Success,
Failed
}
}
using System;
namespace Common
{
[Serializable]
public class PlayerData
{
public Vector3Data pos { get; set; }
public string Username { get; set; }
}
}
using System;
namespace Common
{
[Serializable]
public class Vector3Data
{
public float x { get; set; }
public float y { get; set; }
public float z { get; set; }
}
}
然后重新生成下服務(wù)器摄悯,還是把Common.dll重新添加到我們的Unity里面,這樣我們下面所需要的所有枚舉類型都添加進(jìn)去了赞季,下面可以放心使用.下面就要開始做位置的同步了.
首先創(chuàng)建一個請求的腳本SyncPositionRequest,然后繼承自Request,實現(xiàn)里面的抽象類,我們把位置信息x,y,z都單獨(dú)發(fā)送個給服務(wù)器,把OpCode設(shè)置為SyncPosition
using System;
using System.Collections.Generic;
using ExitGames.Client.Photon;
using UnityEngine;
using Common;
public class SyncPositionRequest : Request {
[HideInInspector]
public Vector3 pos;
//發(fā)起位置信息請求
public override void DefaultRequse()
{
//把位置信息x,y,z傳遞給服務(wù)器端
Dictionary<byte, object> data = new Dictionary<byte, object>();
data.Add((byte)ParameterCode.x,pos.x);
data.Add((byte)ParameterCode.y, pos.y);
data.Add((byte)ParameterCode.z, pos.z);
PhotonEngine.Peer.OpCustom((byte)OpCode, data, true);//把Player位置傳遞給服務(wù)器
}
public override void OnOperationResponse(OperationResponse operationResponse)
{
throw new NotImplementedException();
}
}
下面就要發(fā)起這個位置同步請求的方法的調(diào)用了奢驯。我們把這個調(diào)用給Player,因為位置信息是需要時時同步的所以這個方法我們也需要時時去調(diào)用申钩。這個時候就需要使用InvokeRepeating()方法
在腳本Player的Start方法里面添加
using System.Collections.Generic;
using UnityEngine;
using Common;
using Common.Tools;
public class Player : MonoBehaviour {
private SyncPositionRequest SyncPosRequest;
private Vector3 lastPosition = Vector3.zero;
private float moveOffset = 0.1f;
void Start () {
//設(shè)置本地的Player的顏色設(shè)置成綠色
player. GetComponent<Renderer>().material.color = Color.green;
SyncPosRequest = GetComponent<SyncPositionRequest>();
//參數(shù)一 方法名,參數(shù)二 從等多久后開始執(zhí)行這個方法 參數(shù)三 同步的時間速率瘪阁。這里一秒同步十次
InvokeRepeating("SyncPosition", 3, 1 / 10f);//重復(fù)調(diào)用某個方法
}
void Update () {
//只有本地的Player撒遣,可以控制移動
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
player.transform.Translate(new Vector3(h,0,v)*Time.deltaTime*4);
}
//位置信息時時更新
void SyncPosition()
{
//如果玩家的位置當(dāng)前玩家的位置和上玩家上一個的位置距離大于0.1,就表示玩家移動了管跺,就需要他位置的同步
if (Vector3.Distance(player.transform.position, lastPosition) > moveOffset)
{
lastPosition = player.transform.position;
SyncPosRequest.pos = player.transform.position;//把這個位置信息傳遞給SyncPosRequest
SyncPosRequest.DefaultRequse();//調(diào)用位置信息同步的請求
}
}
}
這樣位置請求就發(fā)送給了服務(wù)器义黎,下面我們就可以去服務(wù)器接收這些位置信息了。在服務(wù)器端的Handler文件夾下創(chuàng)建SyncPositionHandler類并繼承自BaseHandler這個類并實現(xiàn)里面的抽象方法,然后我們我們到MyGameServer類里面將SyncPositionHandler放入集合里面管理起來豁跑,和前面的注冊請求登陸請求操作一樣廉涕。在MyGameServer的InitHandler方法里面添加
//用來初始化Handler
public void InitHandler()
{
SyncPositionHandler syncPositionHandler = new SyncPositionHandler();
HandlerDict.Add(syncPositionHandler.opCode, syncPositionHandler);
}
然后再ClientPeer類里面添加,因為ClientPeer是跟客戶端對應(yīng)的,最后我們還是要把位置信息給各個客戶端狐蜕,所以這里把位置信息保存到ClientPeer里面壶愤。
public float x, y, z;
最后在SyncPositionHandler類里面寫入客戶端請求的操作并接收數(shù)據(jù),代碼如下
using Photon.SocketServer;
using Common;
using Common.Tools;
namespace MyGameServer.Handler
{
class SyncPositionHandler : BaseHandler
{
public SyncPositionHandler()
{
opCode = OperationCode.SyncPosition;
}
//獲取客戶端位置請求的處理的代碼
public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)
{
//接收位置并保持起來
float x = (float)DictTool.GetValue<byte, object>(operationRequest.Parameters,(byte)ParameterCode.x);
float y = (float)DictTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParameterCode.y);
float z = (float)DictTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParameterCode.z);
peer.x = x;peer.y = y;peer.z = z;//把位置數(shù)據(jù)傳遞給Clientpeer保存管理起來
MyGameServer.log.Info(x + "--" + y+"--"+z);//輸出測試
}
}
}
這樣我們就完成了服務(wù)器端的數(shù)據(jù)接收馏鹤,最后我就打印輸出測試征椒,重新生成服務(wù)器端,運(yùn)行客戶端湃累,然后在服務(wù)器端移動Player的位置勃救,看看在服務(wù)器端的日志輸出結(jié)果,我們可以看到,我們在客戶端一移動治力,服務(wù)器端就會輸出位置信息蒙秒,這樣我們的位置就傳遞給了服務(wù)器。客戶端服務(wù)器保存登陸的用戶名并在服務(wù)器把所有連接的客戶端用集合管理起來
下面我們要在客戶端和服務(wù)器里面都保存用戶登錄進(jìn)去的用戶名宵统,客戶端里面我們保存到PhotonEngine腳本里面,首先在PhotonEngine里面創(chuàng)建一個變量
public static string username;//保持當(dāng)前用戶的用戶名
然后LoginRequest腳本里面的OnOperationResponse()方法去獲取到這個用戶名晕讲,在這個方法里修改成以下代碼
//得到響應(yīng)
public override void OnOperationResponse(OperationResponse operationResponse)
{
ReturnCode returnCode = (ReturnCode)operationResponse.ReturnCode;
if (returnCode == ReturnCode.Success)//如果登陸成功保持當(dāng)前用戶的用戶名
{
PhotonEngine.username = Username;
}
loginpanel.OnLoginResponse(returnCode);
}
這樣我們的用戶名就保存到了PhotonEngine里面,后面我們就要在服務(wù)器里面保持登陸的用戶名了马澈,首先在ClientPeer類里面創(chuàng)建變量
public string username;
然后在LoginHandler類里面的OnOperationRequest方法里面去獲取到用戶名
//存放所有的Client客戶端
public List<ClientPeer> peerlist = new List<ClientPeer>();//通過這個集合可以訪問到所有客戶端的Peer勤婚,從而向任何一個客戶端發(fā)送數(shù)據(jù)
//當(dāng)一個客戶端請求連接的時候,服務(wù)器端就會調(diào)用這個方法
//我們使用peerbase,表示和一個客戶端的鏈接,然后photon就會把這些鏈接管理起來
protected override PeerBase CreatePeer(InitRequest initRequest)
{
log.Info("一個客戶端連接進(jìn)來了涤伐!");
ClientPeer peer = new ClientPeer(initRequest);//每鏈接一個客戶端過來我們就把這個客戶端存儲起來添加到List里面
peerlist.Add(peer);
return peer;
}
這樣我們每一次有客戶端連接進(jìn)來的時候我們都保存起來,然后再客戶端斷開連接的時候我們?nèi)デ蹇账斜4嫫饋淼目蛻舳司托辛寺ǎ贑lientPeer里面有一個斷開連接的方法,我們寫入
//處理客戶端斷開連接的后續(xù)工作
protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
{
MyGameServer.Instance.peerlist.Remove(this);//斷開連接的時候List里面移除當(dāng)前的ClientPeer客戶端
}
同步其他客戶端的角色(請求連接其他客戶端的角色)
首先在客戶端創(chuàng)建一個請求的腳本SyncPlayerRequest,繼承自Request凝果,并實現(xiàn)其抽象方法,OpCode選擇SyncPlayer.
using System.Collections.Generic;
using ExitGames.Client.Photon;
using Common;
using Common.Tools;
using System.IO;
using System.Xml.Serialization;
public class SyncPlayerRequest : Request
{
private Player player;
public override void Start()
{
base.Start();
player = GetComponent<Player>();
}
//發(fā)起請求
public override void DefaultRequse()
{
PhotonEngine.Peer.OpCustom((byte)OpCode, null, true);//把Player位置傳遞給服務(wù)器
}
//處理服務(wù)器響應(yīng)給客戶端的數(shù)據(jù)
public override void OnOperationResponse(OperationResponse operationResponse)
{
string usernameListString=(string)DictTool.GetValue<byte, object>(operationResponse.Parameters,(byte)ParameterCode.UsernameList);
//通過xml反序列化接收服務(wù)器傳輸過來的List數(shù)據(jù)
using (StringReader reader = new StringReader(usernameListString))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<string>));
List<string> usernameList= (List<string>)serializer.Deserialize(reader);//表示讀取字符串
player.OnSyncPlayerResponse(usernameList);
}
}
}
然后去Player腳本里面調(diào)用一下這兩個方法
using System.Collections.Generic;
using ExitGames.Client.Photon;
using Common;
using Common.Tools;
using System.IO;
using System.Xml.Serialization;
public class SyncPlayerRequest : Request
{
private Player player;
public override void Start()
{
base.Start();
player = GetComponent<Player>();
}
//發(fā)起請求
public override void DefaultRequse()
{
PhotonEngine.Peer.OpCustom((byte)OpCode, null, true);//把Player位置傳遞給服務(wù)器
}
//處理服務(wù)器響應(yīng)給客戶端的數(shù)據(jù)
public override void OnOperationResponse(OperationResponse operationResponse)
{
//接收xml格式的字符串
string usernameListString=(string)DictTool.GetValue<byte, object>(operationResponse.Parameters,(byte)ParameterCode.UsernameList);
//通過xml反序列化解析傳輸過來的List數(shù)據(jù) 接受完后關(guān)閉
using (StringReader reader = new StringReader(usernameListString))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<string>));
List<string> usernameList= (List<string>)serializer.Deserialize(reader);//表示讀取字符串
player.OnSyncPlayerResponse(usernameList);
}
}
}
這樣就完成了客戶端這邊的請求并收到了服務(wù)器的響應(yīng)祝迂,下面我們要去服務(wù)器端去接受這個請求并且回應(yīng)數(shù)據(jù)給客戶端,首先在服務(wù)器端Handler文件夾下創(chuàng)建一個SyncPlayerHandler類并繼承自BaseHandler抽象類,然后實現(xiàn)里面的抽象方法器净,接著在MyGaneServer類里面把SyncPlayerHandler交給集合管理起來
//用來初始化Handler
public void InitHandler()
{
LoginHandler loginHandler = new LoginHandler();
HandlerDict.Add(loginHandler.opCode,loginHandler);
DefaultHandler defaultHandler = new DefaultHandler();
HandlerDict.Add(defaultHandler.opCode,defaultHandler);
RegisterHandler registerHandler = new RegisterHandler();
HandlerDict.Add(registerHandler.opCode, registerHandler);
SyncPositionHandler syncPositionHandler = new SyncPositionHandler();
HandlerDict.Add(syncPositionHandler.opCode, syncPositionHandler);
SyncPlayerHandler syncPlayerHandler = new SyncPlayerHandler();
HandlerDict.Add(syncPlayerHandler.opCode,syncPlayerHandler);
}
這樣就把所有的請求數(shù)據(jù)都管理起來型雳。下面我們要去SyncPlayerHandler類里面處理請求了,因為發(fā)送的數(shù)據(jù)不支持List<>類型所以我們需要將List類型通過Xml序列化和反序列化的方式給其進(jìn)行數(shù)據(jù)的傳輸
using System.Collections.Generic;
using Photon.SocketServer;
using Common;
using System.Xml.Serialization;
using System.IO;
namespace MyGameServer.Handler
{
class SyncPlayerHandler : BaseHandler
{
public SyncPlayerHandler()
{
opCode = OperationCode.SyncPlayer;
}
//獲取其他客戶端相對應(yīng)的用戶名請求的處理代碼
public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)
{
//取得所有已經(jīng)登陸(在線玩家)的用戶名
List<string> usernameList = new List<string>();
foreach (ClientPeer tempPeer in MyGameServer.Instance.peerlist)
{
//string.IsNullOrEmpty(tempPeer.username);//如果用戶名為空表示沒有登陸
//如果連接過來的客戶端已經(jīng)登陸了有用戶名了并且這個客戶端不是當(dāng)前的客戶端
if (string.IsNullOrEmpty(tempPeer.username )== false&&tempPeer!=peer)
{
//把這些客戶端的Usernam添加到集合里面
usernameList.Add(tempPeer.username);
}
}
//通過xml序列化進(jìn)行數(shù)據(jù)傳輸,傳輸給客戶端
StringWriter sw = new StringWriter();
XmlSerializer serlizer = new XmlSerializer(typeof(List<string>));
serlizer.Serialize(sw,usernameList);
sw.Close();
string usernameListString = sw.ToString();
//給客戶端響應(yīng)
Dictionary<byte, object> data = new Dictionary<byte, object>();
data.Add((byte)ParameterCode.UsernameList, usernameListString);
OperationResponse response = new OperationResponse(operationRequest.OperationCode);
response.Parameters = data;
peer.SendOperationResponse(response, sendParameters);
}
}
}
下面我們就可以來測試下看看結(jié)果掌动,首先打包一到電腦上四啰,然后先運(yùn)行電腦上的客戶端移動下位置,然后我們再運(yùn)行Unity3D里面的客戶端登陸上去粗恢,這時候就可以看到我們Unity里面的其他客戶端已經(jīng)同步了進(jìn)來柑晒,這時候我們就完成了一大半了。
服務(wù)器直接給客戶端發(fā)消息告訴其他客戶端有新客戶端接收進(jìn)來
首先我們在服務(wù)器的SyncPlayerHandler類的OnOperationRequest()方法里面添加代碼眷射,告訴其他客戶端有新的客戶端進(jìn)來
那么服務(wù)器端發(fā)送事件后我們就要去客戶端去接收這個事件了匙赞,首先在客戶端創(chuàng)建一個腳本BaseEvent用作事件的公共基類.里面的方法和Request腳本里面是差不多的佛掖。
using Common;
using ExitGames.Client.Photon;
using UnityEngine;
public abstract class BaseEvent : MonoBehaviour
{
public EventCode EvCode;
public abstract void OnEvent(EventData eventData);//接收服務(wù)器發(fā)送過來的數(shù)據(jù)與消息
//當(dāng)這個組件初始化的時候添加這個Request
public virtual void Start()
{
PhotonEngine.Instance.AddEvent(this);
}
//當(dāng)這個組件被銷毀的時候移除這個Request
public void OnDestroy()
{
PhotonEngine.Instance.RemoveEvent(this);
}
}
然后再PhotonEngine里面修改里面的代碼和添加代碼,如下
using System.Collections.Generic;
using UnityEngine;
using ExitGames.Client.Photon;
using Common;
using Common.Tools;
public class PhotonEngine : MonoBehaviour,IPhotonPeerListener {
public static PhotonEngine Instance;
public static PhotonPeer Peer//讓外界可以訪問我們的PhotonPeer
{
get
{
return peer;
}
}
//創(chuàng)建一個字典涌庭,根據(jù)OperationCode去找到所有相對應(yīng)的Request對象
private Dictionary<OperationCode, Request> RequestDict = new Dictionary<OperationCode, Request>();
private Dictionary<EventCode, BaseEvent> EventDict = new Dictionary<EventCode, BaseEvent>();
public static string username;//保存當(dāng)前用戶的用戶名
private static PhotonPeer peer;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(this.gameObject);
}else if (Instance != this)
{
Destroy(this.gameObject);return;
}
}
// Use this for initialization
void Start () {
//連接服務(wù)器端
//通過Listender連接服務(wù)器端的響應(yīng)
//第一個參數(shù) 指定一個Licensed(監(jiān)聽器) ,第二個參數(shù)使用什么協(xié)議
peer = new PhotonPeer(this,ConnectionProtocol.Udp);
//連接 UDP的 Ip地址:端口號芥被,Application的名字
peer.Connect("127.0.0.1:5055", "MyGame1");
}
// Update is called once per frame
void Update () {
peer.Service();//需要一直調(diào)用Service方法,時時處理跟服務(wù)器端的連接
}
//當(dāng)游戲關(guān)閉的時候(停止運(yùn)行)調(diào)用OnDestroy
private void OnDestroy()
{
//如果peer不等于空并且狀態(tài)為正在連接
if (peer != null && peer.PeerState == PeerStateValue.Connected)
{
peer.Disconnect();//斷開連接
}
}
//
public void DebugReturn(DebugLevel level, string message)
{
}
//如果客戶端沒有發(fā)起請求,但是服務(wù)器端向客戶端通知一些事情的時候就會通過OnEvent來進(jìn)行響應(yīng)
public void OnEvent(EventData eventData)
{
EventCode code = (EventCode)eventData.Code;
BaseEvent e= DictTool.GetValue<EventCode, BaseEvent>(EventDict, code);
e.OnEvent(eventData);
}
//當(dāng)我們在客戶端向服務(wù)器端發(fā)起請求后坐榆,服務(wù)器端接受處理這個請求給客戶端一個響應(yīng)就會在這個方法里進(jìn)行處理
public void OnOperationResponse(OperationResponse operationResponse)
{
OperationCode opCode = (OperationCode)operationResponse.OperationCode;//得到響應(yīng)的OperationCode
Request request = null;
bool temp = RequestDict.TryGetValue(opCode, out request);//是否得到這個響應(yīng)
// 如果得到這個響應(yīng)
if (temp)
{
request.OnOperationResponse(operationResponse);//處理Request里面的響應(yīng)
}
else
{
Debug.Log("沒有找到對應(yīng)的響應(yīng)處理對象");
}
}
//如果連接狀態(tài)發(fā)生改變的時候就會觸發(fā)這個方法拴魄。
//連接狀態(tài)有五種,正在連接中(PeerStateValue.Connecting)席镀,已經(jīng)連接上(PeerStateValue.Connected)匹中,正在斷開連接中( PeerStateValue.Disconnecting),已經(jīng)斷開連接(PeerStateValue.Disconnected)豪诲,正在進(jìn)行初始化(PeerStateValue.InitializingApplication)
public void OnStatusChanged(StatusCode statusCode) {
}
//添加Requst
public void AddRequst(Request requst)
{
RequestDict.Add(requst.OpCode, requst);
}
//移除Requst
public void RemoveRequst(Request request)
{
RequestDict.Remove(request.OpCode);
}
//添加Event事件
public void AddEvent(BaseEvent Event)
{
EventDict.Add(Event.EvCode, Event);
}
//移除Event事件
public void RemoveEvent(BaseEvent Event)
{
EventDict.Remove(Event.EvCode);
}
}
這樣事件的公共基類就完成了顶捷,事件的公共基類完成后,我們開始創(chuàng)建服務(wù)器端發(fā)送過來對應(yīng)的事件處理了屎篱,在客戶端中創(chuàng)建一個腳本NewPlayerEvent,讓它繼承自我們剛剛創(chuàng)建的BaseEvent,并實現(xiàn)里面的抽象方法
using ExitGames.Client.Photon;
using Common.Tools;
using Common;
public class NewPlayerEvent : BaseEvent {
private Player player;
public override void Start()
{
base.Start();
player = GetComponent<Player>();
}
public override void OnEvent(EventData eventData)
{
string username = (string)DictTool.GetValue<byte, object>(eventData.Parameters,(byte) ParameterCode.Username);
player.OnNewPlayerEvent(username);
}
}
這樣就完成了服赎,這樣只要有新客戶端加入進(jìn)來,服務(wù)器就會發(fā)送消息交播,然后我們的客戶端就會實例化出新的角色重虑。這樣就完成了角色的同步,但是它的位置信息是沒有同步的堪侯。接下來就要吧前面在服務(wù)器中接收到的位置信息去同步到各個客戶端
把服務(wù)器端接收到的位置信息同步到各個客戶端
因為位置的發(fā)送我們是需要一直不斷的去發(fā)送給各個客戶端位置信息嚎尤,所以我們這里放在一個線程中去做,所以在服務(wù)器中創(chuàng)建一個文件夾Threads伍宦,用來存放所有的線程,然后在Threads文件夾下面創(chuàng)建一個類SyncPositionThread,創(chuàng)建OK后乏梁,在SyncPositionThread寫入下面的代碼
using Common;
using System.Xml.Serialization;
using System.IO;
using Photon.SocketServer;
namespace MyGameServer.Threads
{
class SyncPositionThread
{
private Thread t;
//啟動線程的方法
public void Run()
{
t = new Thread(UpdataPosition);//UpdataPosition表示線程要啟動的方法
t.IsBackground = true;//后臺運(yùn)行
t.Start();//啟動線程
}
private void UpdataPosition()
{
}
//關(guān)閉線程
public void Stop()
{
t.Abort();//終止線程
}
}
}
然后我們?nèi)yGameServer這個類里面去啟動線程,所以在MyGameServer里面添加下面的代碼,在初始化的時候其啟動這個線程,服務(wù)器關(guān)閉的時候線程也去關(guān)閉了.
private SyncPositionThread syncPositinThread = new SyncPositionThread();
//初始化(當(dāng)整個服務(wù)器啟動起來的時候調(diào)用這個初始化)
protected override void Setup()
{
syncPositinThread.Run();
}
//server端關(guān)閉的時候
protected override void TearDown()
{
syncPositinThread.Stop();
log.Info("關(guān)閉了服務(wù)器");
}
然后我們再去SyncPositionThread類里面去把所有客戶端的位置信息發(fā)送到各個客戶端次洼,在SyncPositionThread添加以下代碼
using System.Collections.Generic;
using System.Threading;
using Common;
using System.Xml.Serialization;
using System.IO;
using Photon.SocketServer;
namespace MyGameServer.Threads
{
class SyncPositionThread
{
private Thread t;
//啟動線程的方法
public void Run()
{
t = new Thread(UpdataPosition);//UpdataPosition表示線程要啟動的方法
t.IsBackground = true;//后臺運(yùn)行
t.Start();//啟動線程
}
private void UpdataPosition()
{
Thread.Sleep(5000);//開始的時候休息5秒開始同步
while (true)//死循環(huán)
{
Thread.Sleep(100);//沒隔0.1秒同步一次位置信息
//進(jìn)行同步
SendPosition();
}
}
//把所有客戶端的位置信息發(fā)送到各個客戶端
//封裝位置信息,封裝到字典里,然后利用Xml序列化去發(fā)送
private void SendPosition()
{
//裝載PlayerData里面的信息
List<PlayerData> playerDatraList = new List<PlayerData>();
foreach (ClientPeer peer in MyGameServer.Instance.peerlist)//遍歷所有客戶段
{
if (string.IsNullOrEmpty(peer.username)==false)//取得當(dāng)前已經(jīng)登陸的客戶端
{
PlayerData playerdata = new PlayerData();
playerdata.Username = peer.username;//設(shè)置playerdata里面的username
playerdata.pos = new Vector3Data() { x = peer.x, y = peer.y, z = peer.z };//設(shè)置playerdata里面的Position
playerDatraList.Add(playerdata);//把playerdata放入集合
}
}
//進(jìn)行Xml序列化成String
StringWriter sw = new StringWriter();
XmlSerializer serializer = new XmlSerializer(typeof(List<PlayerData>));
serializer.Serialize(sw,playerDatraList);
sw.Close();
string playerDataListString = sw.ToString();
Dictionary<byte, object> data = new Dictionary<byte, object>();
data.Add((byte)ParameterCode.PlayerDataList,playerDataListString);//把所有的playerDataListString裝載進(jìn)字典里面
//把Xml序列化的信息裝在字典里發(fā)送給各個客戶端
foreach (ClientPeer peer in MyGameServer.Instance.peerlist)
{
if (string.IsNullOrEmpty(peer.username) == false)
{
EventData ed = new EventData((byte)EventCode.SyncPosition);
ed.Parameters = data;
peer.SendEvent(ed,new SendParameters());
}
}
}
//關(guān)閉線程
public void Stop()
{
t.Abort();//終止線程
}
}
}
這樣我們就把服務(wù)器端的所有客戶端的位置信息都同步到了各個客戶端去了遇骑,接下來只需要去客戶端去接受這些數(shù)據(jù)就OK了卖毁。首先我們在客戶端添加先對應(yīng)的事件腳本SyncPlayerEvent繼承自BaseEvent并實現(xiàn)里面的抽象方法,然后寫入接受服務(wù)器端發(fā)數(shù)據(jù)
using System.Collections.Generic;
using ExitGames.Client.Photon;
using Common.Tools;
using Common;
using System.IO;
using System.Xml.Serialization;
public class SyncPlayerEvent : BaseEvent
{
private Player player;
public override void Start()
{
base.Start();
player = GetComponent<Player>();
}
public override void OnEvent(EventData eventData)
{
string playerDataListString=(string)DictTool.GetValue<byte, object>(eventData.Parameters, (byte)ParameterCode.PlayerDataList);
//進(jìn)行反序列化接收數(shù)據(jù)
using (StringReader reader = new StringReader(playerDataListString))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<PlayerData>));
List<PlayerData> playerDataList=(List<PlayerData>)serializer.Deserialize(reader);
player.OnSyncPositionEvent(playerDataList);
}
}
}
然后在Player腳本里面添加OnSyncPositionEvent()方法。落萎,在這個方法里面寫入代碼
public void OnSyncPositionEvent(List<PlayerData> playerDataList)
{
foreach (PlayerData pd in playerDataList)//遍歷所有的數(shù)據(jù)
{
GameObject go = DictTool.GetValue<string, GameObject>(playerDic, pd.Username);//根據(jù)傳遞過來的Username去找到所對應(yīng)的實例化出來的Player
//如果查找到了相應(yīng)的角色亥啦,就把相應(yīng)的位置信息賦值給這個角色的position
if (go != null)
{
go.transform.position = new Vector3() { x = pd.pos.x, y = pd.pos.y, z = pd.pos.z };
}
}
}
這樣我們整個位置的同步也完成了,下面我們就可以來測試下了,這樣就完成了位置的同步练链。