游戏场景的同步
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();
if (IsLocalPlayer)
{
GetComponent().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 data = new Dictionary();
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(operationRequest.Parameters, (byte)ParameterCode.Position);
float x =(float) DicTool.GetValue(operationRequest.Parameters,(byte) ParameterCode.x);
float y =(float) DicTool.GetValue(operationRequest.Parameters,(byte) ParameterCode.y);
float z =(float) DicTool.GetValue(operationRequest.Parameters,(byte) ParameterCode.z);
peer.x = x;peer.y = y;peer.z = z; //保存在peer中 哪个客户端传过来就保存在哪个客户端的peer中
MyGameServer.log.Info(x+ " "+ y+" "+ z);
}
}
}
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 HandlerDic = new Dictionary();
//当一个客户端连接
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(MyGameServer.Instances.HandlerDic, (OperationColde) operationRequest.OperationCode);
if (baseHandle != null)
{
baseHandle.OnOperationRequest(operationRequest, sendParameters, this);
}
else
{
BaseHandler defautHandler= DicTool.GetValue(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 RequesDict = new Dictionary(); // 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 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();
}
public override void DefauitRequest()
{
Dictionary data = new Dictionary();
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);
}
}
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 peerList = new List();
public static MyGameServer Instances
{
get;private set;
}//单利模式
public Dictionary HandlerDic = new Dictionary();
//当一个客户端连接
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(MyGameServer.Instances.HandlerDic, (OperationColde) operationRequest.OperationCode);
if (baseHandle != null)
{
baseHandle.OnOperationRequest(operationRequest, sendParameters, this);
}
else
{
BaseHandler defautHandler= DicTool.GetValue(MyGameServer.Instances.HandlerDic, OperationColde.Default);
defautHandler.OnOperationRequest(operationRequest, sendParameters, this);
}
}
}
}
新建一个脚本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();
syncpoision = GetComponent();
syncPlayerRequet.DefauitRequest();
GetComponent().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 peerList = new List();
public static MyGameServer Instances
{
get;private set;
}//单利模式
public Dictionary HandlerDic = new Dictionary();
//当一个客户端连接
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 usernameList = new List();
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 集合 就该用 Xml文档 进行序列化和反序列化
usernameList.Add("wqaidikjdn"); //测试用的
StringWriter sw = new StringWriter();
XmlSerializer serializer = new XmlSerializer(typeof(List)); //添加要转化成xml 的类型
serializer.Serialize(sw,usernameList);//进行序列化
sw.Close();
string usernameListString = sw.ToString();
MyGameServer.log.Info(usernameListString);
Dictionary data = new Dictionary();
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(operationResponse.Parameters,(byte)ParameterCode.UserNameList);
//Debug.Log(UserNameListString);
using (StringReader reader = new StringReader(UserNameListString))
{
XmlSerializer zerializer = new XmlSerializer(typeof(List));
List usernameList=(List) zerializer.Deserialize(reader); //Xml反序列化
// 现在返回的是一个List
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();
}
public override void OnOperationResponse(OperationResponse operationResponse)
{
string UserNameListString= (string) DicTool.GetValue(operationResponse.Parameters,(byte)ParameterCode.UserNameList);
//Debug.Log(UserNameListString);
using (StringReader reader = new StringReader(UserNameListString))
{
XmlSerializer zerializer = new XmlSerializer(typeof(List));
List usernameList = (List) zerializer.Deserialize(reader); //Xml反序列化
// 现在返回的是一个List
player.OnSyncPlayerPosinse(usernameList);
//foreach (string username in usernameList)
//{
// Debug.Log(username);
//}
}
}
}
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 palyerDict = new Dictionary();
void Start () {
if (IsLocalPlayer)
{ syncPlayerRequet = GetComponent();
syncpoision = GetComponent();
syncPlayerRequet.DefauitRequest(); //请求其他客户端的信息,吧其他客户端在当前客户端创建出来
GetComponent().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 usernameList)
{
//创建其他客户端的Player角色
foreach (var username in usernameList)
{
GameObject go= GameObject.Instantiate(PlayerPrefab);
Destroy(go.GetComponent());
Destroy(go.GetComponent());
go.GetComponent().IsLocalPlayer = false;
go.GetComponent().UserName = username;
palyerDict.Add(username, go);
}
}
}
然后在服务器添加一些代码 因为这个是通过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 usernameList = new List();
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)); //添加要转化成xml 的类型
serializer.Serialize(sw,usernameList);//进行序列化
sw.Close();
string usernameListString = sw.ToString();
MyGameServer.log.Info(usernameListString);
Dictionary data = new Dictionary();
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 data2 = new Dictionary();
data2.Add((byte)ParameterCode.UserName, peer.userName);
ed.Parameters=data2;
tempPeer .SendEvent(ed,sendParameters);
}
}
}
}
}
先在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 RequesDict = new Dictionary(); // key使用OperationCode 就可以通过operAtionCode找到对应的Request对象
private Dictionary eventDict = new Dictionary();
public void DebugReturn(DebugLevel level, string message)
{
}
//服务器端向客户端发起数据的时候
public void OnEvent(EventData eventData)
{
EventCode code =(EventCode) eventData.Code;
BaseEvent e= DicTool.GetValue(eventDict, code);
e.OnEvent(eventData);
////这个方法会接收所有服务器发来的事件 所以要用switch判断一下code
//switch (eventData.Code)
//{
// case 1:
// Debug.Log("收到服务器发过来的事件 ");
// Dictionary 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);
}
}
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();
}
public override void OnEvent(EventData eventData)
{
string UserName =(string) DicTool.GetValue(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 palyerDict = new Dictionary();
void Start () {
if (IsLocalPlayer)
{ syncPlayerRequet = GetComponent();
syncpoision = GetComponent();
syncPlayerRequet.DefauitRequest(); //请求其他客户端的信息,吧其他客户端在当前客户端创建出来
GetComponent().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 usernameList)
{
//创建其他客户端的Player角色
foreach (var username in usernameList)
{
OnNewPlayerEvent(username); //两个代码一样
//GameObject go = GameObject.Instantiate(PlayerPrefab);
//Destroy(go.GetComponent());
//Destroy(go.GetComponent());
//Destroy(go.GetComponent()); //创建第二个用户的时候不需要
//go.GetComponent().IsLocalPlayer = false;
//go.GetComponent().UserName = username;
//palyerDict.Add(username, go);
}
}
public void OnNewPlayerEvent(string username)
{
GameObject go = GameObject.Instantiate(PlayerPrefab);
Destroy(go.GetComponent());
Destroy(go.GetComponent());
Destroy(go.GetComponent()); //创建第二个用户的时候不需要
go.GetComponent().IsLocalPlayer = false;
go.GetComponent().UserName = username;
palyerDict.Add(username, go);
}
}
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 peerList = new List();
private SyncPositionThread syconpositionThread = new SyncPositionThread();
public static MyGameServer Instances
{
get;private set;
}//单利模式
public Dictionary HandlerDic = new Dictionary();
//当一个客户端连接
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 playerDataList = new List();
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));
serializer.Serialize(sw,playerDataList);
sw.Close();
string playerDataListString = sw.ToString();
Dictionary data = new Dictionary();
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();
}
public override void OnEvent(EventData eventData)
{
string playerDataListString=(string) DicTool.GetValue(eventData.Parameters, (byte)ParameterCode.PlayerDataList);
using(StringReader reader=new StringReader(playerDataListString))
{
XmlSerializer serializer = new XmlSerializer(typeof(List));
List playerDataList = (List)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 playerDict = new Dictionary();
void Start () {
if (IsLocalPlayer)
{ syncPlayerRequet = GetComponent();
syncpoision = GetComponent();
syncPlayerRequet.DefauitRequest(); //请求其他客户端的信息,吧其他客户端在当前客户端创建出来
GetComponent().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 usernameList)
{
//创建其他客户端的Player角色
foreach (var username in usernameList)
{
OnNewPlayerEvent(username); //两个代码一样
//GameObject go = GameObject.Instantiate(PlayerPrefab);
//Destroy(go.GetComponent());
//Destroy( go.GetComponent());
//Destroy(go.GetComponent());
//Destroy(go.GetComponent()); //创建第二个用户的时候不需要
//go.GetComponent().IsLocalPlayer = false;
//go.GetComponent().UserName = username;
//palyerDict.Add(username, go);
}
}
public void OnNewPlayerEvent(string username)
{
GameObject go = GameObject.Instantiate(PlayerPrefab);
DestroyImmediate(go.GetComponent());
DestroyImmediate(go.GetComponent());
DestroyImmediate(go.GetComponent());
DestroyImmediate(go.GetComponent()); //创建第二个用户的时候不需要
go.GetComponent().IsLocalPlayer = false;
go.GetComponent().UserName = username;
playerDict.Add(username, go);
}
public void OnSyncPositionEvent(List playerData)
{
foreach (PlayerData pd in playerData)
{
GameObject go= DicTool.GetValue(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 playerDict = new Dictionary();
void Start () {
if (IsLocalPlayer)
{ syncPlayerRequet = GetComponent();
syncpoision = GetComponent();
syncPlayerRequet.DefauitRequest(); //请求其他客户端的信息,吧其他客户端在当前客户端创建出来
player.GetComponent().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 usernameList)
{
//创建其他客户端的Player角色
foreach (var username in usernameList)
{
OnNewPlayerEvent(username); //两个代码一样
//GameObject go = GameObject.Instantiate(PlayerPrefab);
//Destroy(go.GetComponent());
//Destroy( go.GetComponent());
//Destroy(go.GetComponent());
//Destroy(go.GetComponent()); //创建第二个用户的时候不需要
//go.GetComponent().IsLocalPlayer = false;
//go.GetComponent().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)
{
foreach (PlayerData pd in playerData)
{
GameObject go= DicTool.GetValue(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就结束了 谢谢大家!
未完待续。。。