游戏场景的同步

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 应用的根目录 就是 deploylog4net.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判断一下codeswitch (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 应用的根目录 就是 deploylog4net.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 应用的根目录 就是 deploylog4net.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 应用的根目录 就是 deploylog4net.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就结束了 谢谢大家!

未完待续。。。

PhotonServer游戏服务器端(四)相关推荐

  1. photon 服务器操作系统,PhotonServer游戏服务器端教程

    老师我是看了泰斗破坏神的服务器,但是在利用Phtonserver从数据库中获取服务器的列表,它无法获取,哪个说ssl主机本地主机不支持 //实现父类的抽象方法 处理收到客户端的请求的方法 ... 追评 ...

  2. 【游戏后端】游戏服务器端开发的一些建议(转载)

    摘要: 本文作为游戏服务器端开发的基本大纲,是游戏实践开发中的总结.第一部分专业基础,用于指导招聘和实习考核, 第二部分游戏入门,讲述游戏服务器端开发的基本要点,第三部分服务端架构,介绍架构设计中的一 ...

  3. 「游戏开发」游戏服务器端开发的一些经验

    本文作为游戏服务器端开发的基本大纲,是游戏实践开发中的总结.第一部分专业基础,用于指导招聘和实习考核, 第二部分游戏入门,讲述游戏服务器端开发的基本要点,第三部分服务端架构,介绍架构设计中的一些基本原 ...

  4. 我给游戏服务器端开发的一些建议

    本文作为游戏服务器端开发的基本大纲,是游戏实践开发中的总结.第一部分专业基础,用于指导招聘和实习考核, 第二部分游戏入门,讲述游戏服务器端开发的基本要点,第三部分服务端架构,介绍架构设计中的一些基本原 ...

  5. 经典游戏服务器端架构

    文章目录 一. 讨论的背景 二. 游戏服务器架构的要素 三.核心的三个架构 四. 游戏服务器模型的进化历程 五.分服模型 1.模型描述 2.调度架构 1.单进程游戏服务器 2.多进程游戏服务器 3.内 ...

  6. 经典游戏服务器端架构概述

    12 经典游戏服务器端架构概述 架构的分析模型 一. 讨论的背景 ​ 现代电子游戏,基本上都会使用一定的网络功能.从验证正版,到多人交互等等,都需要架设一些专用的服务器,以及编写在服务器上的程序.因此 ...

  7. pomelo服务器 性能,Pomelo游戏服务器端开发系列(1)-介绍

    Pomelo框架总结 A fast,scalable,distributed game server framework for Node.js 联系我 Pomelo交流群 @老顽童-NextZeus ...

  8. 游戏服务器端开发要点

    转至:http://jiangwen2011.blog.163.com/blog/static/194801340201181104532377/ 摘要: 本文作为游戏服务器端开发的基本大纲,是游戏实 ...

  9. 游戏服务器端开发的基本大纲

    摘要: 本文作为游戏服务器端开发的基本大纲,是游戏实践开发中的总结.第一部分专业基础,用于指导招聘和实习考核, 第二部分游戏入门,讲述游戏服务器端开发的基本要点,第三部分服务端架构,介绍架构设计中的一 ...

最新文章

  1. CSDN如何编辑数学公式
  2. CellPress | 医学上人工智能的缺失
  3. linux cat pdf文件怎么打开,linux下cat 命令使用详解:显示文件内容
  4. 一个供应商只允许一个报价单
  5. timthumb.php外链,如何解决WordPress多站点不支持timthumb.php?
  6. SpringBoot系列: 单元测试2
  7. Js中fetch方法
  8. 剑指offer---连续子数组的最大和
  9. Android 打造编译时注解解析框架
  10. JStorm如何保证消息不丢失
  11. arcgis开发 多版本之间如何兼容_arcgis api 4.x for js 结合 react 入门开发系列初探篇(附源码下载)...
  12. Android系统联系人全特效实现(下),字母表快速滚动
  13. 类型xxx 无法反序列化。缺乏对应的数据成员。
  14. Centos7 安装 Kubernetes dashboard (安装篇)
  15. maven 强制jdk的版本
  16. 建立强有力的人脉关系
  17. 村田 | 用于人机界面和生命体征检测的压电薄膜传感器
  18. IDEA跳转到上一个下一个方法的快捷键
  19. 浙江凤凰计划:用新零售模式做资本市场敲门砖
  20. Tensorflow.js||使用 CNN 识别手写数字

热门文章

  1. 中级网络工程师2017上半年上午试题答案完整版
  2. 14-EIGRP路由协议详解
  3. jQuery写省市级联
  4. 【python】Twisted网络编程
  5. 前端js预览并且导出pdf文件
  6. matlab 矩阵角标,MATLAB中的矩阵索引
  7. JavaScript - js进阶 - ES6面向对象
  8. 河北2020计算机二级报名时间及考试时间,河北2020年3月计算机二级考试报名时间安排...
  9. 浙江大学,最新任命!
  10. EAI dashgo底盘(不带陀螺仪版本)+SICK激光(选择LMS100)的ROS建图导航基础