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, //用来同步位置 }}
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游戏服务器端(四)
- PhotonServer游戏服务器端(一)
- PhotonServer游戏服务器端(二)
- PhotonServer游戏服务器端(三)
- PhotonServer-MyGameServer服务器端
- PhotonServer MMO游戏开发
- PhotonServer游戏服务器
- PhotonServer游戏服务器【完整版】
- 网络在线游戏开发心得(服务器端)(四)
- 自己做游戏(一)-PhotonServer配置
- PhotonServer游戏服务器部署及日志输出
- PhotonServer学习笔记(一)
- 使用PhotonServer和Unity建立一个棋牌类游戏实例(一)构建photon服务器基础架构
- 使用PhotonServer和Unity建立一个棋牌类游戏实例(二)构建photon服务器与数据库的交互
- MMORPG游戏服务器端设计(续)
- 经典游戏服务器端架构(一)
- 经典游戏服务器端架构概述 (1)
- 使用PhotonServer和Unity建立一个棋牌类游戏实例(三)Unity客户端的注册请求和服务器的注册请求响应
- js:return;return true;return false;区别
- gradle 项目从eclipse 迁移到idea笔记
- Matlab求解代数多项式方程组
- Oracle 11g——SQL 嵌套查询
- POJ 1830 开关问题
- PhotonServer游戏服务器端(四)
- MYSQL+InnoDB(备份与恢复)
- onSaveInstanceState应用 保存首页tab位置
- apache下实现301永久性重定向的方法
- SpringMVC架构---SpringMVC学习笔记(一)
- ES6语法入门——下
- Word转换成pdf文件之修改pdf虚拟打印机设置
- OpenCV探索之路(二十三):特征检测和特征匹配方法汇总
- chrome://命令大全