PhotonServer游戏服务器端(四)

来源:互联网 发布:修复微信闪退的软件 编辑:程序博客网 时间:2024/06/10 06:03

 游戏场景的同步 

1,先新建一个Game场景 放置一个Panel 放置一个胶囊体 调整摄像机,调整到能看到胶囊体在Panel上面

2,给胶囊体添加一个脚本Player代码实现如下:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class Player : MonoBehaviour {    bool IsLocalPlayer = true; //判断是不是 主客户端  只有主客户端才同步位置信息    private SyncPositionRequest syncpoision;    private Vector3 lastPosision=Vector3.zero;void Start () {        syncpoision = GetComponent<SyncPositionRequest>();        if (IsLocalPlayer)        {            GetComponent<Renderer>().material.color = Color.red;            InvokeRepeating("SyncPosition", 2, 0.2f);//一秒同步1/10  (  5次)        }}void SyncPosition()    {        if (Vector3.Distance(transform.position,lastPosision)>0.1f)        {            lastPosision = transform.position;            syncpoision.pos = this.gameObject.transform.position;            syncpoision.DefauitRequest();                  }    }void Update () {        if (IsLocalPlayer)        {            float h = Input.GetAxis("Horizontal");            float v = Input.GetAxis("Vertical");            transform.Translate(new Vector3(h, 0, v)*Time.deltaTime*4);        }}}
然后在服务器端新建一种位置同步信息的状态 修改OpertionColde:

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Common{   public enum  OperationColde:byte  //区分请求和响应的类型    {        Login, //登录        Register, //注册        Default, //默认        SyncPosition // 传递位置信息    }} 
然后 再修改传递的参数 XYZ坐标:

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Common{    public enum ParameterCode:byte //区分传送数据的时候,参数的类型    {       UserName,//用户名       PassWord,//密码             x,y, z//位置信息    }}
然后重新生成  吧新的Common类库导入到unity的文件夹中 然后新建一个SyncPositionRequest    继承自Request 用来发送自己的位置信息 在Inspector面板选择OpCode的类型为SyncPosision :

using System.Collections;using System.Collections.Generic;using UnityEngine;using Common;using ExitGames.Client.Photon;using System;public class SyncPositionRequest:Request  {    [HideInInspector] //隐藏    public Vector3 pos;    public override void DefauitRequest()    {        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);        PhotoEngine.Peer.OpCustom((byte)OpCode, data, true);    }    public override void OnOperationResponse(OperationResponse operationResponse)    {    }}
然后回到客户端进行接收和解析 新建一个类库SyncPositionHandler 继承自BaseHandler :

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Photon.SocketServer;using Common;using Common.DicTool;namespace MyGameServer.Handler{    class SyncPositionHandler : BaseHandler    {        public SyncPositionHandler()        {            Opcode = Common.OperationColde.SyncPosition;        }        public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)        {            // Vector3Data pos =(Vector3Data) DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParameterCode.Position);            float x =(float) DicTool.GetValue<byte, object>(operationRequest.Parameters,(byte) ParameterCode.x);            float y =(float) DicTool.GetValue<byte, object>(operationRequest.Parameters,(byte) ParameterCode.y);            float z =(float) DicTool.GetValue<byte, object>(operationRequest.Parameters,(byte) ParameterCode.z);            peer.x = x;peer.y = y;peer.z = z; //保存在peer中  哪个客户端传过来就保存在哪个客户端的peer中            MyGameServer.log.Info(x+ "        "+ y+"     "+ z);        }    }}

然后在在MyGameServer类中进行SyncPositionHandler的初始化工作
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Photon.SocketServer;using ExitGames.Logging;using ExitGames.Logging.Log4Net;using System.IO;using log4net.Config;using MyGameServer.Model;using MyGameServer.Manager;using Common;using MyGameServer.Handler;namespace MyGameServer{    //所有的Server端 主类都要集成自Applicationbase     //我们使用peerbase,表示和一个客户的的连接    public class MyGameServer : ApplicationBase    {        // log4net日志 先声明一个log对象        public static readonly ILogger log = LogManager.GetCurrentClassLogger();        public static MyGameServer Instances        {            get;private set;        }//单利模式        public Dictionary<OperationColde, BaseHandler> HandlerDic = new Dictionary<OperationColde, BaseHandler>();        //当一个客户端连接        protected override PeerBase CreatePeer(InitRequest initRequest)        {            log.Info("一个客户的连接过来了。。。");            return new ClientPeer(initRequest);        }        //初始化        protected override void Setup()         {            Instances = this; //单利模式赋值            //设置log输出的目录位置 ApplicationRootPath 可以哟来那个来获取PhotonServer 应用的根目录 就是 deploy            log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(this.ApplicationRootPath, "log");            //日志的初始化 连接相对路径和文件名就是配置文件            FileInfo configFileInfo = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));            if (configFileInfo.Exists)            {                //设置一下 日志的工厂                LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance); //让photon知道我们使用的那个日志插件                //利用Config进行读取                XmlConfigurator.ConfigureAndWatch(configFileInfo); //让Log4Net读取配置文件            }            log.Info("Setup Completed!"+"哈哈哈哈我成功啦");//初始化完成   再次启动服务器 发现 log目录下有个txt文件                        InitHandler();        }        public void InitHandler()        {            LoginHandler loginHandler = new LoginHandler();            HandlerDic.Add(loginHandler.Opcode, loginHandler);            DefaultHandler defauftHander = new DefaultHandler();            HandlerDic.Add(defauftHander.Opcode, defauftHander);            RigisterHandler rigisterHandler = new RigisterHandler();            HandlerDic.Add(rigisterHandler.Opcode, rigisterHandler);            SyncPositionHandler syncPositionHandler = new SyncPositionHandler();            HandlerDic.Add(syncPositionHandler.Opcode, syncPositionHandler);        }        //Server端关闭的时候 做一些关闭的处理        protected override void TearDown()        {            log.Info("服务器应用关闭了。。");        }    }}
然后在对当前客户端进行记录 是哪个客户端穿过来的数据信息, 名字 位置  然后 在ClientPeer类中添加 位置信息和当前的名字 用来记录:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Photon.SocketServer;using PhotonHostRuntimeInterfaces;using MyGameServer.Handler;using Common.DicTool;using Common;namespace MyGameServer{    public class ClientPeer : Photon.SocketServer.ClientPeer //    {        public float x, y, z;  //记录自己的坐标        public  string userName;  //标识当前的用户的用户名        public ClientPeer(InitRequest initRequest) : base(initRequest)        {        }        //断开连接 清理工作        protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)        {        }        //处理客户端发起请求        protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)        {            BaseHandler baseHandle = DicTool.GetValue<OperationColde, BaseHandler>(MyGameServer.Instances.HandlerDic, (OperationColde) operationRequest.OperationCode);            if (baseHandle != null)            {                baseHandle.OnOperationRequest(operationRequest, sendParameters, this);            }            else            {             BaseHandler defautHandler=    DicTool.GetValue<OperationColde, BaseHandler>(MyGameServer.Instances.HandlerDic, OperationColde.Default);                defautHandler.OnOperationRequest(operationRequest, sendParameters, this);            }                  }    }}
然后也在客户端中进行添加 是哪个客户端传过来的数据 然后 在PhotonEngine中添加 Username,和在 LoginRegister中添加发送位置姓名的客户端:

using System.Collections;using System.Collections.Generic;using UnityEngine;using ExitGames.Client.Photon; //插件的命名空间using System;using Common;public class PhotoEngine : MonoBehaviour,IPhotonPeerListener {    public static PhotonPeer Peer    {        get        {            return peer;        }    }    public static PhotoEngine _instance;  // 全局单例模式    private static PhotonPeer peer;    public static string Username;//记录当前客户端    private Dictionary<OperationColde, Request> RequesDict = new Dictionary<OperationColde, Request>();  // key使用OperationCode  就可以通过operAtionCode找到对应的Request对象    public void DebugReturn(DebugLevel level, string message)    {    }    //服务器端向客户端发起数据的时候    public void OnEvent(EventData eventData)     {//这个方法会接收所有服务器发来的事件 所以要用switch判断一下code        switch (eventData.Code)        {            case 1:                Debug.Log("收到服务器发过来的事件 ");               Dictionary <byte,object> data3= eventData.Parameters;                object intvalue;                data3.TryGetValue(1, out intvalue);                object stringValue;                data3.TryGetValue(2, out stringValue);                Debug.Log(intvalue.ToString() + "  " + stringValue.ToString());                break;            default:                break;        }    }    //客户端向服务器发起一个请求以后服务器处理完以后 就会给客户端一个相应    public void OnOperationResponse(OperationResponse operationResponse)    {   OperationColde opCpde=  (OperationColde)operationResponse.OperationCode;        Request request = null;       bool temp= RequesDict.TryGetValue(opCpde, out request);        if (temp)        {            request.OnOperationResponse(operationResponse);        }        else        {            Debug.Log("没找到相应的处理对象");        }    }    //状态改变的时候调用该方法  PeerStateValue.。。。    public void OnStatusChanged(StatusCode statusCode)    {        Debug.Log(statusCode);    }    void Awake()    {        if (_instance==null)        {            _instance = this;            DontDestroyOnLoad(this.gameObject);        }        else if (_instance!=this)        {            //所有场景只留一个PhotonEngine 删除多余的              Destroy(this.gameObject);return;        }    }void Start () {        //通过listener来接收服务器端的相应        peer = new PhotonPeer(this,ConnectionProtocol.Udp);        //连接服务器 发起连接        peer.Connect("127.0.0.1:5055", "MyGame1");        }void Update () {                    peer.Service(); //检测有没有 发起请求  无论什么状态下       }    void OnDestroy()    {        if (peer!=null&&(peer.PeerState==PeerStateValue.Connected)) // 当peer不等于空 而且 正在运行的时候        {            peer.Disconnect();//断开连接        }    }    public  void AddReqyest(Request requet)    {        RequesDict.Add(requet.OpCode, requet);    }    public void RemoveRequest(Request requet)    {        RequesDict.Remove(requet.OpCode);          }}



using System;using System.Collections;using System.Collections.Generic;using ExitGames.Client.Photon;using UnityEngine;using Common;public class LoginRequest : Request{    [HideInInspector] //隐藏属性    public string UserName;     [HideInInspector]    public string PassWord;    private LoginPanel loginPanel;    public override void Start()    {        base.Start();        loginPanel = GetComponent<LoginPanel>();    }    public override void DefauitRequest()    {        Dictionary<byte, object> data = new Dictionary<byte, object>();        data.Add((byte)ParameterCode.UserName, UserName);        data.Add((byte)ParameterCode.PassWord, PassWord);        PhotoEngine.Peer.OpCustom((byte)OpCode, data, true);    }    public override void OnOperationResponse(OperationResponse operationResponse)    {        ReturnCode returmCode = (ReturnCode)operationResponse.ReturnCode;        if (returmCode==ReturnCode.Success)        {            PhotoEngine.Username = UserName;         }        loginPanel.OnLiginResponse(returmCode);             }}

这时候 在服务器端要访问所有的客户端。。 就要访问所有客户端的peer  有了Peer 才能发送数据,先把这些peer管理起来
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Photon.SocketServer;using ExitGames.Logging;using ExitGames.Logging.Log4Net;using System.IO;using log4net.Config;using MyGameServer.Model;using MyGameServer.Manager;using Common;using MyGameServer.Handler;namespace MyGameServer{    //所有的Server端 主类都要集成自Applicationbase     //我们使用peerbase,表示和一个客户的的连接    public class MyGameServer : ApplicationBase    {        // log4net日志 先声明一个log对象        public static readonly ILogger log = LogManager.GetCurrentClassLogger();        public List<ClientPeer> peerList = new List<ClientPeer>();        public static MyGameServer Instances        {            get;private set;        }//单利模式        public Dictionary<OperationColde, BaseHandler> HandlerDic = new Dictionary<OperationColde, BaseHandler>();        //当一个客户端连接        protected override PeerBase CreatePeer(InitRequest initRequest)        {            log.Info("一个客户的连接过来了。。。");            // return new ClientPeer(initRequest);            ClientPeer peer = new ClientPeer(initRequest);  //跟之前没有任何更改  只是通过这个集合可以访问到所有的客户端,从而向任何一个客户端发送请求            peerList.Add(peer);            return peer;        }        //初始化        protected override void Setup()         {            Instances = this; //单利模式赋值            //设置log输出的目录位置 ApplicationRootPath 可以哟来那个来获取PhotonServer 应用的根目录 就是 deploy            log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(this.ApplicationRootPath, "log");            //日志的初始化 连接相对路径和文件名就是配置文件            FileInfo configFileInfo = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));            if (configFileInfo.Exists)            {                //设置一下 日志的工厂                LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance); //让photon知道我们使用的那个日志插件                //利用Config进行读取                XmlConfigurator.ConfigureAndWatch(configFileInfo); //让Log4Net读取配置文件            }            log.Info("Setup Completed!"+"哈哈哈哈我成功啦");//初始化完成   再次启动服务器 发现 log目录下有个txt文件                        InitHandler();        }        public void InitHandler()        {            LoginHandler loginHandler = new LoginHandler();            HandlerDic.Add(loginHandler.Opcode, loginHandler);            DefaultHandler defauftHander = new DefaultHandler();            HandlerDic.Add(defauftHander.Opcode, defauftHander);            RigisterHandler rigisterHandler = new RigisterHandler();            HandlerDic.Add(rigisterHandler.Opcode, rigisterHandler);            SyncPositionHandler syncPositionHandler = new SyncPositionHandler();            HandlerDic.Add(syncPositionHandler.Opcode, syncPositionHandler);        }        //Server端关闭的时候 做一些关闭的处理        protected override void TearDown()        {            log.Info("服务器应用关闭了。。");        }    }}
然后 在客户端断开的时候进行断开处理操作:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Photon.SocketServer;using PhotonHostRuntimeInterfaces;using MyGameServer.Handler;using Common.DicTool;using Common;namespace MyGameServer{    public class ClientPeer : Photon.SocketServer.ClientPeer //    {        public float x, y, z;  //记录自己的坐标        public  string userName;  //标识当前的用户的用户名        public ClientPeer(InitRequest initRequest) : base(initRequest)        {        }        //断开连接 清理工作        protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)        {            MyGameServer.Instances.peerList.Remove(this); //当断开连接的时候 就把当前客户端在服务器列表中移除        }        //处理客户端发起请求        protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)        {            BaseHandler baseHandle = DicTool.GetValue<OperationColde, BaseHandler>(MyGameServer.Instances.HandlerDic, (OperationColde) operationRequest.OperationCode);            if (baseHandle != null)            {                baseHandle.OnOperationRequest(operationRequest, sendParameters, this);            }            else            {             BaseHandler defautHandler=    DicTool.GetValue<OperationColde, BaseHandler>(MyGameServer.Instances.HandlerDic, OperationColde.Default);                defautHandler.OnOperationRequest(operationRequest, sendParameters, this);            }        }    }}

3我们在做位置同步的时候分为两部分,第一部分实例化其他客户端的角色,第二部分进行位置的同步,,先把Player作为预制体(prefab)

新建一个脚本SyncPlayerRequest 挂在player上   用来同步其他客户端的角色进来  这个同步 分为两步1,我们发送请求到服务器端,服务器端反馈给我们有多少其他的客户端,让其他新连接的客户端 把我们也创建出来 第二步是是要其他客户端在他们那客户端上把我们的角色创建出来,

先重新 写一个OperationColde的Player的类型:

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Common{   public enum  OperationColde:byte  //区分请求和响应的类型    {        Login, //登录        Register, //注册        Default, //默认        SyncPosition, // 传递位置信息        SyncPlayer //角色    }} 
然后 重新剩 引入Common类库  ,然后    SyncPlayerRequest 继承自Request :

using System;using System.Collections;using System.Collections.Generic;using ExitGames.Client.Photon;using UnityEngine;public class SyncPlayerRequest : Request{   // 同步其他客户端的角色进来    public override void DefauitRequest()    {        PhotoEngine.Peer.OpCustom((byte)OpCode, null, true);    }    public override void OnOperationResponse(OperationResponse operationResponse)    {        throw new NotImplementedException();    }}
然后在Player中新建一个UserName用来区分客户端的角色 和调用 SyncPlayerRequest 的DefauitRequest方法:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class Player : MonoBehaviour {    bool IsLocalPlayer = true; //判断是不是 主客户端  只有主客户端才同步位置信息    private SyncPositionRequest syncpoision;    public string UserName; //有多个用户 用来区分多个角色 同步的是哪一个    private SyncPlayerRequest syncPlayerRequet;    private Vector3 lastPosision=Vector3.zero;void Start () {        if (IsLocalPlayer)        {   syncPlayerRequet = GetComponent<SyncPlayerRequest>();            syncpoision = GetComponent<SyncPositionRequest>();            syncPlayerRequet.DefauitRequest();            GetComponent<Renderer>().material.color = Color.red;            InvokeRepeating("SyncPosition", 2, 0.2f);//一秒同步1/10  (  5次)        }}void SyncPosition()    {        if (Vector3.Distance(transform.position,lastPosision)>0.1f)        {            lastPosision = transform.position;            syncpoision.pos = this.gameObject.transform.position;            syncpoision.DefauitRequest();                  }          }void Update () {        if (IsLocalPlayer)        {            float h = Input.GetAxis("Horizontal");            float v = Input.GetAxis("Vertical");            transform.Translate(new Vector3(h, 0, v)*Time.deltaTime*4);        }}}


4,回到服务器做 相应的请求 处理,返回登录名的用户列表,在Handler中新建一个SyncPlayerHandler 继承自BaseHandler

然后先对SyncPayerHandler进行初始化

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Photon.SocketServer;using ExitGames.Logging;using ExitGames.Logging.Log4Net;using System.IO;using log4net.Config;using MyGameServer.Model;using MyGameServer.Manager;using Common;using MyGameServer.Handler;namespace MyGameServer{    //所有的Server端 主类都要集成自Applicationbase     //我们使用peerbase,表示和一个客户的的连接    public class MyGameServer : ApplicationBase    {        // log4net日志 先声明一个log对象        public static readonly ILogger log = LogManager.GetCurrentClassLogger();        public List<ClientPeer> peerList = new List<ClientPeer>();        public static MyGameServer Instances        {            get;private set;        }//单利模式        public Dictionary<OperationColde, BaseHandler> HandlerDic = new Dictionary<OperationColde, BaseHandler>();        //当一个客户端连接        protected override PeerBase CreatePeer(InitRequest initRequest)        {            log.Info("一个客户的连接过来了。。。");            // return new ClientPeer(initRequest);            ClientPeer peer = new ClientPeer(initRequest);  //跟之前没有任何更改  只是通过这个集合可以访问到所有的客户端,从而向任何一个客户端发送请求            peerList.Add(peer);            return peer;        }        //初始化        protected override void Setup()         {            Instances = this; //单利模式赋值            //设置log输出的目录位置 ApplicationRootPath 可以哟来那个来获取PhotonServer 应用的根目录 就是 deploy            log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(this.ApplicationRootPath, "log");            //日志的初始化 连接相对路径和文件名就是配置文件            FileInfo configFileInfo = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));            if (configFileInfo.Exists)            {                //设置一下 日志的工厂                LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance); //让photon知道我们使用的那个日志插件                //利用Config进行读取                XmlConfigurator.ConfigureAndWatch(configFileInfo); //让Log4Net读取配置文件            }            log.Info("Setup Completed!"+"哈哈哈哈我成功啦");//初始化完成   再次启动服务器 发现 log目录下有个txt文件                        InitHandler();        }        public void InitHandler()        {            LoginHandler loginHandler = new LoginHandler();            HandlerDic.Add(loginHandler.Opcode, loginHandler);            DefaultHandler defauftHander = new DefaultHandler();            HandlerDic.Add(defauftHander.Opcode, defauftHander);            RigisterHandler rigisterHandler = new RigisterHandler();            HandlerDic.Add(rigisterHandler.Opcode, rigisterHandler);            SyncPositionHandler syncPositionHandler = new SyncPositionHandler();            HandlerDic.Add(syncPositionHandler.Opcode, syncPositionHandler);            SyncPlayerHandler syncPlayerHander = new SyncPlayerHandler();            HandlerDic.Add(syncPlayerHander.Opcode, syncPlayerHander);        }        //Server端关闭的时候 做一些关闭的处理        protected override void TearDown()        {            log.Info("服务器应用关闭了。。");        }    }}
然后在ParameterCode 的传递参数类型中增加一个Name的List集合 用来存储传递给客户端

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Common{    public enum ParameterCode:byte //区分传送数据的时候,参数的类型    {       UserName,//用户名       PassWord,//密码             x,y, z,//位置信息       UserNameList //登录的用户名集合    }}
然后重新生成 编写SyncPlayerHandler  通过Xml传递个客户端

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Photon.SocketServer;using Common;using System.Xml.Serialization;//转变成xml来进行传递using System.IO;namespace MyGameServer.Handler{    class SyncPlayerHandler : BaseHandler // 传递已经登录的客户端, 因为已经登录的客户端才是位于世界当中的    {        public SyncPlayerHandler()        {            Opcode = OperationColde.SyncPlayer;        }        public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)        {            //取得所有已经登录 (在线玩家)的USername 做出一个集合传递到客户端            List<string> usernameList = new List<string>();            foreach (ClientPeer TempPeer in MyGameServer.Instances.peerList) //我们当前的Peer也是包含在List里面的 然后我们现在取得其他客户端 当前的peer是不用取得的            {                //所以判断的有两个条件                 //1,已经登录了                //2,不是当前客户端                if (string.IsNullOrEmpty(TempPeer.userName)==false&&TempPeer!=peer)                {                    usernameList.Add(TempPeer.userName);                }             }//传递的参数不能用LIst<String> 集合 就该用 Xml文档 进行序列化和反序列化            usernameList.Add("wqaidikjdn"); //测试用的            StringWriter sw = new StringWriter();            XmlSerializer serializer = new XmlSerializer(typeof(List<string>)); //添加要转化成xml 的类型            serializer.Serialize(sw,usernameList);//进行序列化            sw.Close();            string usernameListString = sw.ToString();            MyGameServer.log.Info(usernameListString);            Dictionary<byte, object> data = new Dictionary<byte, object>();            data.Add((byte)ParameterCode.UserNameList, usernameListString);            OperationResponse respo = new OperationResponse(operationRequest.OperationCode);            respo.Parameters=data;            peer.SendOperationResponse( respo, sendParameters);        }    }}
然后回到客户端处理接收数据 先把重新生成的客户端的Common.dll类库替换掉Unity中的类库。客户端反序列化 

using System;using System.Collections;using System.Collections.Generic;using ExitGames.Client.Photon;using UnityEngine;using Common.DicTool;using Common;using System.IO;//数据的读using System.Xml.Serialization; // 反序列化public class SyncPlayerRequest : Request{   // 同步其他客户端的角色进来    public override void DefauitRequest()    {        PhotoEngine.Peer.OpCustom((byte)OpCode, null, true);    }    public override void OnOperationResponse(OperationResponse operationResponse)    {  string  UserNameListString= (string)  DicTool.GetValue<byte,object>(operationResponse.Parameters,(byte)ParameterCode.UserNameList);        //Debug.Log(UserNameListString);        using (StringReader reader = new StringReader(UserNameListString))        {            XmlSerializer zerializer = new XmlSerializer(typeof(List<string>));         List<string> usernameList=(List<string>) zerializer.Deserialize(reader); //Xml反序列化                                            // 现在返回的是一个List<string>            foreach (string username in usernameList)            {                Debug.Log(username);//输出用户名            }        }             }}


想要吧其他客户的在机器上创建出来的话要实例化游戏物体,实例化的代码放在Player这边:

using System;using System.Collections;using System.Collections.Generic;using ExitGames.Client.Photon;using UnityEngine;using Common.DicTool;using Common;using System.IO;//数据的读using System.Xml.Serialization; // 反序列化public class SyncPlayerRequest : Request{   // 同步其他客户端的角色进来    public override void DefauitRequest()    {        PhotoEngine.Peer.OpCustom((byte)OpCode, null, true);    }    private Player player;    public override void Start()    {        base.Start();          player = GetComponent<Player>();    }    public override void OnOperationResponse(OperationResponse operationResponse)    {       string  UserNameListString= (string)  DicTool.GetValue<byte,object>(operationResponse.Parameters,(byte)ParameterCode.UserNameList);        //Debug.Log(UserNameListString);        using (StringReader reader = new StringReader(UserNameListString))        {            XmlSerializer zerializer = new XmlSerializer(typeof(List<string>));            List<string> usernameList = (List<string>) zerializer.Deserialize(reader); //Xml反序列化                                                                                       // 现在返回的是一个List<string>            player.OnSyncPlayerPosinse(usernameList);            //foreach (string username in usernameList)            //{            //    Debug.Log(username);            //}        }          }}

然后调用Player里面的OnDyncPlayerPosionse方法进行实例化:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class Player : MonoBehaviour {    bool IsLocalPlayer = true; //判断是不是 主客户端  只有主客户端才同步位置信息    private SyncPositionRequest syncpoision;    public string UserName; //有多个用户 用来区分多个角色 同步的是哪一个    private SyncPlayerRequest syncPlayerRequet;    private Vector3 lastPosision=Vector3.zero;    public GameObject PlayerPrefab;    private Dictionary<string, GameObject> palyerDict = new Dictionary<string, GameObject>();void Start () {        if (IsLocalPlayer)        {   syncPlayerRequet = GetComponent<SyncPlayerRequest>();            syncpoision = GetComponent<SyncPositionRequest>();            syncPlayerRequet.DefauitRequest(); //请求其他客户端的信息,吧其他客户端在当前客户端创建出来            GetComponent<Renderer>().material.color = Color.red;            InvokeRepeating("SyncPosition", 2, 0.2f);//一秒同步1/10  (  5次)        }}void SyncPosition()    {        if (Vector3.Distance(transform.position,lastPosision)>0.1f)        {            lastPosision = transform.position;            syncpoision.pos = this.gameObject.transform.position;            syncpoision.DefauitRequest();                  }          }void Update () {        if (IsLocalPlayer)        {            float h = Input.GetAxis("Horizontal");            float v = Input.GetAxis("Vertical");            transform.Translate(new Vector3(h, 0, v)*Time.deltaTime*4);        }}    public  void OnSyncPlayerPosinse(List<string> usernameList)    {        //创建其他客户端的Player角色        foreach (var username in usernameList)        {          GameObject go=  GameObject.Instantiate(PlayerPrefab);            Destroy(go.GetComponent<SyncPlayerRequest>());            Destroy(go.GetComponent<SyncPositionRequest>());            go.GetComponent<Player>().IsLocalPlayer = false;            go.GetComponent<Player>().UserName = username;            palyerDict.Add(username, go);        }    }}


5,当一个新客户端加入的时候 要通知其他客户端 我们要通知其他客户端创建这个新加入的客户端,这个就要在服务器端做了

然后在服务器添加一些代码 因为这个是通过EventData传送的 所有 EventCode 添加一个枚举类型 添加后重新生成 再次替换unity中的Common.dll 

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Common{    public enum EventCode:byte //区分服务器向客户端发送的事件类型    {        NewPlayer,//新的客户端    }}

然后修改服务器中SyncPlayerHandler 中的代码:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Photon.SocketServer;using Common;using System.Xml.Serialization;//转变成xml来进行传递using System.IO;namespace MyGameServer.Handler{    class SyncPlayerHandler : BaseHandler // 传递已经登录的客户端, 因为已经登录的客户端才是位于世界当中的    {        public SyncPlayerHandler()        {            Opcode = OperationColde.SyncPlayer;        }        public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)        {            //取得所有已经登录 (在线玩家)的USername 做出一个集合传递到客户端            List<string> usernameList = new List<string>();            foreach (ClientPeer TempPeer in MyGameServer.Instances.peerList) //我们当前的Peer也是包含在List里面的 然后我们现在取得其他客户端 当前的peer是不用取得的            {                //所以判断的有两个条件                 //1,已经登录了                //2,不是当前客户端                if (string.IsNullOrEmpty(TempPeer.userName)==false&&TempPeer!=peer)                {                    usernameList.Add(TempPeer.userName);                }             }                        StringWriter sw = new StringWriter();            XmlSerializer serializer = new XmlSerializer(typeof(List<string>)); //添加要转化成xml 的类型            serializer.Serialize(sw,usernameList);//进行序列化            sw.Close();            string usernameListString = sw.ToString();            MyGameServer.log.Info(usernameListString);            Dictionary<byte, object> data = new Dictionary<byte, object>();            data.Add((byte)ParameterCode.UserNameList, usernameListString);            //把其他客户端传递给当前客户端            OperationResponse respo = new OperationResponse(operationRequest.OperationCode);            respo.Parameters=data;            peer.SendOperationResponse( respo, sendParameters);            //把当前客户端传递给其他客户端            //告诉其他客户端 有新的客户加入            foreach (ClientPeer tempPeer in MyGameServer.Instances.peerList)            {                if (string.IsNullOrEmpty(tempPeer.userName) == false && tempPeer != peer)                {                    //所以判断的有两个条件                     //1,已经登录了                    //2,不是当前客户端                    EventData ed = new EventData((byte)EventCode.NewPlayer);                    Dictionary<byte, object> data2 = new Dictionary<byte, object>();                    data2.Add((byte)ParameterCode.UserName, peer.userName);                    ed.Parameters=data2;                    tempPeer .SendEvent(ed,sendParameters);                }            }        }    }}

然后下面就在客户端处理EventData事件

先在unityAssets 下新建一个Event文件夹下面创建 BaseEvent 抽象基类;只有接收的方法,然后在PhotonEngine中新建一个EventCode,BaseEvent字典集合  在进行初始化 添加和删除。。代码实现如下:


using System.Collections;using System.Collections.Generic;using UnityEngine;using Common;using ExitGames.Client.Photon;public abstract class BaseEvent : MonoBehaviour {    public EventCode  EventCode; //继承的类 (当前类)需要处理的哪个操作    public abstract void OnEvent(EventData eventData);  //只需要接收方法    public virtual void Start()    {        PhotoEngine._instance.AddEvent(this);    }    public void OnDestroy()    {        PhotoEngine._instance.RemoveEvent(this);      }}


using System.Collections;using System.Collections.Generic;using UnityEngine;using ExitGames.Client.Photon; //插件的命名空间using System;using Common;using Common.DicTool;public class PhotoEngine : MonoBehaviour,IPhotonPeerListener {    public static PhotonPeer Peer    {        get        {            return peer;        }    }    public static PhotoEngine _instance;  // 全局单例模式    private static PhotonPeer peer;    public static string Username;//记录当前客户端    private Dictionary<OperationColde, Request> RequesDict = new Dictionary<OperationColde, Request>();  // key使用OperationCode  就可以通过operAtionCode找到对应的Request对象    private Dictionary<EventCode, BaseEvent> eventDict = new Dictionary<EventCode, BaseEvent>();    public void DebugReturn(DebugLevel level, string message)    {    }    //服务器端向客户端发起数据的时候    public void OnEvent(EventData eventData)     {    EventCode  code =(EventCode) eventData.Code;     BaseEvent e=   DicTool.GetValue<EventCode, BaseEvent>(eventDict, code);        e.OnEvent(eventData);        ////这个方法会接收所有服务器发来的事件 所以要用switch判断一下code        //switch (eventData.Code)        //{        //    case 1:        //        Debug.Log("收到服务器发过来的事件 ");        //       Dictionary <byte,object> data3= eventData.Parameters;        //        object intvalue;        //        data3.TryGetValue(1, out intvalue);        //        object stringValue;        //        data3.TryGetValue(2, out stringValue);        //        Debug.Log(intvalue.ToString() + "  " + stringValue.ToString());        //        break;        //    default:        //        break;        //}    }    //客户端向服务器发起一个请求以后服务器处理完以后 就会给客户端一个相应    public void OnOperationResponse(OperationResponse operationResponse)    {   OperationColde opCpde=  (OperationColde)operationResponse.OperationCode;        Request request = null;       bool temp= RequesDict.TryGetValue(opCpde, out request);        if (temp)        {            request.OnOperationResponse(operationResponse);        }        else        {            Debug.Log("没找到相应的处理对象");        }    }    //状态改变的时候调用该方法  PeerStateValue.。。。    public void OnStatusChanged(StatusCode statusCode)    {        Debug.Log(statusCode);    }    void Awake()    {        if (_instance==null)        {            _instance = this;            DontDestroyOnLoad(this.gameObject);        }        else if (_instance!=this)        {            //所有场景只留一个PhotonEngine 删除多余的              Destroy(this.gameObject);return;        }    }void Start () {        //通过listener来接收服务器端的相应        peer = new PhotonPeer(this,ConnectionProtocol.Udp);        //连接服务器 发起连接        peer.Connect("127.0.0.1:5055", "MyGame1");        }void Update () {                    peer.Service(); //检测有没有 发起请求  无论什么状态下       }    void OnDestroy()    {        if (peer!=null&&(peer.PeerState==PeerStateValue.Connected)) // 当peer不等于空 而且 正在运行的时候        {            peer.Disconnect();//断开连接        }    }    public  void AddReqyest(Request requet)    {        RequesDict.Add(requet.OpCode, requet);    }    public void RemoveRequest(Request requet)    {        RequesDict.Remove(requet.OpCode);        }    public void AddEvent(BaseEvent baseEvent)    {        eventDict.Add(baseEvent.EventCode, baseEvent); //添加客户端的EventData事件    }    public void RemoveEvent(BaseEvent baseEvent)    {        eventDict.Remove(baseEvent.EventCode);    }}

然后新建一个脚本NewPlayerEvent 继承自BaseEvent

using System;using System.Collections;using System.Collections.Generic;using ExitGames.Client.Photon;using UnityEngine;using Common.DicTool;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) DicTool.GetValue<byte, object>(eventData.Parameters, (byte)ParameterCode.UserName);        player.OnNewPlayerEvent(UserName);    }}
然后在角色类中生成Player:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class Player : MonoBehaviour {    bool IsLocalPlayer = true; //判断是不是 主客户端  只有主客户端才同步位置信息    private SyncPositionRequest syncpoision;    public string UserName; //有多个用户 用来区分多个角色 同步的是哪一个    private SyncPlayerRequest syncPlayerRequet;    private Vector3 lastPosision=Vector3.zero;    public GameObject PlayerPrefab;    private Dictionary<string, GameObject> palyerDict = new Dictionary<string, GameObject>();void Start () {        if (IsLocalPlayer)        {   syncPlayerRequet = GetComponent<SyncPlayerRequest>();            syncpoision = GetComponent<SyncPositionRequest>();            syncPlayerRequet.DefauitRequest(); //请求其他客户端的信息,吧其他客户端在当前客户端创建出来            GetComponent<Renderer>().material.color = Color.red;            InvokeRepeating("SyncPosition", 2, 0.2f);//一秒同步1/10  (  5次)        }}void SyncPosition()    {        if (Vector3.Distance(transform.position,lastPosision)>0.1f)        {            lastPosision = transform.position;            syncpoision.pos = this.gameObject.transform.position;            syncpoision.DefauitRequest();        }    }void Update () {        if (IsLocalPlayer)        {            float h = Input.GetAxis("Horizontal");            float v = Input.GetAxis("Vertical");            transform.Translate(new Vector3(h, 0, v)*Time.deltaTime*4);        }}    public  void OnSyncPlayerPosinse(List<string> usernameList)    {        //创建其他客户端的Player角色        foreach (var username in usernameList)        {            OnNewPlayerEvent(username); //两个代码一样            //GameObject go = GameObject.Instantiate(PlayerPrefab);            //Destroy(go.GetComponent<SyncPlayerRequest>());            //Destroy(go.GetComponent<SyncPositionRequest>());            //Destroy(go.GetComponent<NewPlayerEvent>()); //创建第二个用户的时候不需要            //go.GetComponent<Player>().IsLocalPlayer = false;            //go.GetComponent<Player>().UserName = username;            //palyerDict.Add(username, go);        }    }    public void OnNewPlayerEvent(string username)    {        GameObject go = GameObject.Instantiate(PlayerPrefab);        Destroy(go.GetComponent<SyncPlayerRequest>());        Destroy(go.GetComponent<SyncPositionRequest>());        Destroy(go.GetComponent<NewPlayerEvent>()); //创建第二个用户的时候不需要        go.GetComponent<Player>().IsLocalPlayer = false;        go.GetComponent<Player>().UserName = username;        palyerDict.Add(username, go);    }}


6,在服务器端要实现更新位置的话就要用到线程 然后在MyGameServer下创建一个Threads文件夹 下面新建一个SyncPlayerPosition:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;namespace MyGameServer.Threads{   public  class SyncPositionThread    {        private Thread t;       public void Run()        {            t = new Thread(UndatePosition);            t.IsBackground = true; //后台运行            t.Start();        }        public void Stop()        {            t.Abort();        }        private void UndatePosition()        {            Thread.Sleep(5000);            while (true)            {                Thread.Sleep(200);                //进行同步                  //收集当前已经登录的客户端 他的用户名和现在的位置 然后封装成一个集合  把它分发给各个客户端            }        }    }}

然后在初始化的时候开启线程,服务器关闭的时候关闭线程:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using Photon.SocketServer;using ExitGames.Logging;using ExitGames.Logging.Log4Net;using System.IO;using log4net.Config;using MyGameServer.Model;using MyGameServer.Manager;using Common;using MyGameServer.Handler;using MyGameServer.Threads;namespace MyGameServer{    //所有的Server端 主类都要集成自Applicationbase     //我们使用peerbase,表示和一个客户的的连接    public class MyGameServer : ApplicationBase    {        // log4net日志 先声明一个log对象        public static readonly ILogger log = LogManager.GetCurrentClassLogger();        public List<ClientPeer> peerList = new List<ClientPeer>();        private SyncPositionThread syconpositionThread = new SyncPositionThread();        public static MyGameServer Instances        {            get;private set;        }//单利模式        public Dictionary<OperationColde, BaseHandler> HandlerDic = new Dictionary<OperationColde, BaseHandler>();        //当一个客户端连接        protected override PeerBase CreatePeer(InitRequest initRequest)        {            log.Info("一个客户的连接过来了。。。");            // return new ClientPeer(initRequest);            ClientPeer peer = new ClientPeer(initRequest);  //跟之前没有任何更改  只是通过这个集合可以访问到所有的客户端,从而向任何一个客户端发送请求            peerList.Add(peer);            return peer;        }        //初始化        protected override void Setup()         {            Instances = this; //单利模式赋值            //设置log输出的目录位置 ApplicationRootPath 可以哟来那个来获取PhotonServer 应用的根目录 就是 deploy            log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(this.ApplicationRootPath, "log");            //日志的初始化 连接相对路径和文件名就是配置文件            FileInfo configFileInfo = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));            if (configFileInfo.Exists)            {                //设置一下 日志的工厂                LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance); //让photon知道我们使用的那个日志插件                //利用Config进行读取                XmlConfigurator.ConfigureAndWatch(configFileInfo); //让Log4Net读取配置文件            }            log.Info("Setup Completed!"+"哈哈哈哈我成功啦");//初始化完成   再次启动服务器 发现 log目录下有个txt文件                        InitHandler();            syconpositionThread.Run();        }        public void InitHandler()        {            LoginHandler loginHandler = new LoginHandler();            HandlerDic.Add(loginHandler.Opcode, loginHandler);            DefaultHandler defauftHander = new DefaultHandler();            HandlerDic.Add(defauftHander.Opcode, defauftHander);            RigisterHandler rigisterHandler = new RigisterHandler();            HandlerDic.Add(rigisterHandler.Opcode, rigisterHandler);            SyncPositionHandler syncPositionHandler = new SyncPositionHandler();            HandlerDic.Add(syncPositionHandler.Opcode, syncPositionHandler);            SyncPlayerHandler syncPlayerHander = new SyncPlayerHandler();            HandlerDic.Add(syncPlayerHander.Opcode, syncPlayerHander);        }        //Server端关闭的时候 做一些关闭的处理        protected override void TearDown()        {            log.Info("服务器应用关闭了。。");            syconpositionThread.Stop();        }    }}
然后再在Common下创建一个PlayerData类 用来传送给客户端位置和名字等信息

先修改 Vector3Data 中的一些属性:

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Common{    [Serializable] //指定序列化    public  class Vector3Data    {        public float x { get; set; }        public float y { get; set; }        public float z { get; set; }    }}

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Common{    [Serializable]   public  class PlayerData    {        public Vector3Data pos { get; set; }        public string UserName { get; set; }    }}


然后再 EventCode里面新添加一个类型用来吧位置传给客户端:

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Common{    public enum EventCode:byte //区分服务器向客户端发送的事件类型    {        NewPlayer,//新的客户端        SyncPosition, //用来同步位置    }}



然后在SyncPositionThread中吧位置信息和用户名发送给客户端 
using Common;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Xml.Serialization;using System.IO;using Photon.SocketServer;namespace MyGameServer.Threads{   public  class SyncPositionThread    {        private Thread t;       public void Run()        {            t = new Thread(UndatePosition);            t.IsBackground = true; //后台运行            t.Start();        }        public void Stop()        {            t.Abort();        }        private void UndatePosition()        {            Thread.Sleep(5000);            while (true)            {                Thread.Sleep(200);                //进行同步                  //收集当前已经登录的客户端 他的用户名和现在的位置 然后封装成一个集合  把它分发给各个客户端                SendPosition();            }        }        private void SendPosition()         {//先封装            List<PlayerData> playerDataList = new List<PlayerData>();            foreach (ClientPeer peer in MyGameServer.Instances.peerList)            {                if (string.IsNullOrEmpty(peer.userName)==false)                {                    PlayerData playerdata = new PlayerData();                    playerdata.UserName = peer.userName;                    playerdata.pos = new Vector3Data { x = peer.x, y = peer.y, z = peer.z };                    playerDataList.Add(playerdata);                }            }            StringWriter sw = new StringWriter();            XmlSerializer serializer = new XmlSerializer(typeof(List<PlayerData>));            serializer.Serialize(sw,playerDataList);            sw.Close();            string playerDataListString = sw.ToString();            Dictionary<byte, object> data = new Dictionary<byte, object>();            data.Add((byte)ParameterCode.PlayerDataList, playerDataListString);            foreach (ClientPeer peer in MyGameServer.Instances.peerList)            {                if (string.IsNullOrEmpty(peer.userName) == false)                { //吧位置信息发送给已经登录的客户端                    EventData ed = new EventData((byte)EventCode.SyncPosition);                    ed.Parameters = data;                    peer.SendEvent(ed, new SendParameters()); //发送个客户端                }            }        }    }}
然后回到客户端进行解析 
using System;using System.Collections;using System.Collections.Generic;using ExitGames.Client.Photon;using UnityEngine;using Common;using Common.DicTool;using System.Xml.Serialization;using System.IO;public class SyncPositionEvent : BaseEvent{    private Player player;    public override void Start()    {        base.Start();        player = GetComponent<Player>();    }    public override void OnEvent(EventData eventData)    {     string playerDataListString=(string)   DicTool.GetValue<byte, object>(eventData.Parameters, (byte)ParameterCode.PlayerDataList);        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中修改同步位置信息:

using Common;using System.Collections;using System.Collections.Generic;using UnityEngine;using Common.DicTool;public class Player : MonoBehaviour {    bool IsLocalPlayer = true; //判断是不是 主客户端  只有主客户端才同步位置信息    private SyncPositionRequest syncpoision;    public string UserName; //有多个用户 用来区分多个角色 同步的是哪一个    private SyncPlayerRequest syncPlayerRequet;    private Vector3 lastPosision=Vector3.zero;    public GameObject PlayerPrefab;    private Dictionary<string, GameObject> playerDict = new Dictionary<string, GameObject>();void Start () {        if (IsLocalPlayer)        {   syncPlayerRequet = GetComponent<SyncPlayerRequest>();            syncpoision = GetComponent<SyncPositionRequest>();            syncPlayerRequet.DefauitRequest(); //请求其他客户端的信息,吧其他客户端在当前客户端创建出来            GetComponent<Renderer>().material.color = Color.red;            InvokeRepeating("SyncPosition", 2, 0.2f);//一秒同步1/10  (  5次)        }}void SyncPosition()    {        if (Vector3.Distance(transform.position,lastPosision)>0.1f)        {            lastPosision = transform.position;            syncpoision.pos = this.gameObject.transform.position;            syncpoision.DefauitRequest();        }    }void Update () {        if (IsLocalPlayer)        {            float h = Input.GetAxis("Horizontal");            float v = Input.GetAxis("Vertical");            transform.Translate(new Vector3(h, 0, v)*Time.deltaTime*4);        }}    public  void OnSyncPlayerPosinse(List<string> usernameList)    {        //创建其他客户端的Player角色        foreach (var username in usernameList)        {            OnNewPlayerEvent(username); //两个代码一样            //GameObject go = GameObject.Instantiate(PlayerPrefab);            //Destroy(go.GetComponent<SyncPlayerRequest>());            //Destroy( go.GetComponent<SyncPositionRequest>());        //Destroy(go.GetComponent<SyncPositionEvent>());            //Destroy(go.GetComponent<NewPlayerEvent>()); //创建第二个用户的时候不需要            //go.GetComponent<Player>().IsLocalPlayer = false;            //go.GetComponent<Player>().UserName = username;            //palyerDict.Add(username, go);        }    }    public void OnNewPlayerEvent(string username)    {                GameObject go = GameObject.Instantiate(PlayerPrefab);        DestroyImmediate(go.GetComponent<SyncPlayerRequest>());        DestroyImmediate(go.GetComponent<SyncPositionRequest>());        DestroyImmediate(go.GetComponent<SyncPositionEvent>());        DestroyImmediate(go.GetComponent<NewPlayerEvent>()); //创建第二个用户的时候不需要        go.GetComponent<Player>().IsLocalPlayer = false;        go.GetComponent<Player>().UserName = username;        playerDict.Add(username, go);    }    public  void OnSyncPositionEvent(List<PlayerData> playerData)    {        foreach (PlayerData pd in playerData)        {          GameObject go=  DicTool.GetValue<string, GameObject>(playerDict, pd.UserName);            if (go!=null)                       go.transform.position = new Vector3() { x = pd.pos.x, y = pd.pos.y, z = pd.pos.z };        }    }}

修复BGU  

把Player的控制放在单独的一个物体身上

我们新建一个空节点 名字命名为PlayerController 然后把 Player上挂的脚本全部复制给PlayerContriller,然后再修改Player.cs中的内容:

using Common;using System.Collections;using System.Collections.Generic;using UnityEngine;using Common.DicTool;public class Player : MonoBehaviour {    public GameObject player;    bool IsLocalPlayer = true; //判断是不是 主客户端  只有主客户端才同步位置信息    private SyncPositionRequest syncpoision;    public string UserName; //有多个用户 用来区分多个角色 同步的是哪一个    private SyncPlayerRequest syncPlayerRequet;    private Vector3 lastPosision=Vector3.zero;    public GameObject PlayerPrefab;    private Dictionary<string, GameObject> playerDict = new Dictionary<string, GameObject>();void Start () {        if (IsLocalPlayer)        {   syncPlayerRequet = GetComponent<SyncPlayerRequest>();            syncpoision = GetComponent<SyncPositionRequest>();            syncPlayerRequet.DefauitRequest(); //请求其他客户端的信息,吧其他客户端在当前客户端创建出来            player.GetComponent<Renderer>().material.color = Color.red;            InvokeRepeating("SyncPosition", 2, 0.2f);//一秒同步1/10  (  5次)        }}void SyncPosition()    {        if (Vector3.Distance(player.transform.position,lastPosision)>0.1f)        {            lastPosision = player. transform.position;            syncpoision.pos = player.transform.position;            syncpoision.DefauitRequest();        }    }void Update () {        //if (IsLocalPlayer)        //{            float h = Input.GetAxis("Horizontal");            float v = Input.GetAxis("Vertical");        player. transform.Translate(new Vector3(h, 0, v)*Time.deltaTime*4);       // }}    public  void OnSyncPlayerPosinse(List<string> usernameList)    {        //创建其他客户端的Player角色        foreach (var username in usernameList)        {            OnNewPlayerEvent(username); //两个代码一样            //GameObject go = GameObject.Instantiate(PlayerPrefab);            //Destroy(go.GetComponent<SyncPlayerRequest>());            //Destroy( go.GetComponent<SyncPositionRequest>());        //Destroy(go.GetComponent<SyncPositionEvent>());            //Destroy(go.GetComponent<NewPlayerEvent>()); //创建第二个用户的时候不需要            //go.GetComponent<Player>().IsLocalPlayer = false;            //go.GetComponent<Player>().UserName = username;            //palyerDict.Add(username, go);        }    }    public void OnNewPlayerEvent(string username)    {                GameObject go = GameObject.Instantiate(PlayerPrefab);                     playerDict.Add(username, go);    }    public  void OnSyncPositionEvent(List<PlayerData> playerData)    {        foreach (PlayerData pd in playerData)        {          GameObject go=  DicTool.GetValue<string, GameObject>(playerDict, pd.UserName);            if (go!=null)                       go.transform.position = new Vector3() { x = pd.pos.x, y = pd.pos.y, z = pd.pos.z };        }    }}


然后 Apply一下一预制体  这个demo就结束了 谢谢大家!

未完待续。。。




阅读全文
0 0
原创粉丝点击