1.Show Request.cs作用是发送给客户端自己的id
2.PlayerMagener.cs来管理所有的Player
其中Add方法,可以将玩家添加到玩家字典里面,以及有显示自己的请求。
这边的显示自己的功能由PlayerController.cs里面刚开始就直接加进去。
3.服务端处理Show的请求,ShowHandler.cs:拿到id,并且Event推送给其他的所有客户端。
4.客户端处理Show的Event,PhontonEngine.cs中:拿到新加入的玩家的id,并且调用ShowOther方法。
其中ShowOther方法在PlayerManager中,通过instantiate实例化的方式生成,并且将新显示出来的玩家加入到玩家字典中。
到此为止,就完成了能看到其他玩家的功能了
———————————————————————————————————————————
接着是移动:
1.MoveRequest是发送给客户端自己实时的坐标,通过PlayerManeger的GetMe方法去拿。
2.在FixUpdate里面,不断调用MoveRequest,不断的发送自己的坐标。
而下面的Update里面是一个控制自己移动的方法
3.服务端MoveHandler.cs:拿到准备移动的物体的位置和他的id。然后Event事件发送给其他的所有客户端,发送将要移动的物体的位置和id。
4.服务端处理Move的Event:拿到xyz和id,放在intValue里面,调用PlayerManeger的MoveOther方法,去更新刚才Move的物体的坐标。(因为Move的Request是每隔0.02s发一次的,因为放在FixUpdate里面了嘛,所以对坐标更新的情况也是0.02s更新一次,如果再短一点,我觉得这个就是帧同步了吧(大概或许hhhh)
PlayerManeger里面的MoveOther方法:通过id拿到字典里面的玩家,然后直接更新其坐标。
PlayerManager的好处:将player都管理起来了,有一个字典来存储所有的Player。一个Add方法来添加Player,并且如果me为null就显示自己。一个GetMe方法来获取自己。一个ShoweOtherPlayer来显示其他玩家。一个MoveOtherPlayer方法来显示其他玩家的移动。
_____________________________________________________________________________
最后附上这几个类的完整代码:
(如果配置文件你们也要的话,从我前面的文章里面找就行了。)
客户端
ShowRequest.cs:
using System.Collections;
using System.Collections.Generic;
using Common;
using ExitGames.Client.Photon;
using UnityEngine;
public class ShowRequest : Request
{
public override void DefaultRequest()
{
//构造参数
var data = new Dictionary<byte, object>();
//构造参数
data.Add((byte)ParaCode.Show, PlayerManager.Instance.GetMe().id);
//发送
PhotonEngine.peer.OpCustom((byte)OpCode, data, true);
}
public override void OnOprationRespionse(OperationResponse operationResponse)
{
if (operationResponse.ReturnCode == (byte)ReturnCode.Success)
{
Debug.Log("Show成功");
}
else
{
Debug.Log("Show失败");
}
}
}
MoveRequest.cs
using System.Collections;
using System.Collections.Generic;
using Common;
using ExitGames.Client.Photon;
using UnityEngine;
public class MoveRequest : Request
{
public override void DefaultRequest()
{
//拿到自己
var me = PlayerManager.Instance.GetMe().GetComponent<Transform>();
//构造参数
var data = new Dictionary<byte, object>();
string location = me.position.x + "," + me.position.y + "," + me.position.z;
//构造参数
data.Add((byte)ParaCode.Move, location);
//print(location);
//发送
PhotonEngine.peer.OpCustom((byte)OpCode, data, true);
}
public override void OnOprationRespionse(OperationResponse operationResponse)
{
//
}
}
PlayerController.cs:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour {
[HideInInspector]
public int id;
private void Awake()
{
id = Random.Range(10000, 99999);
}
// Use this for initialization
void Start () {
PlayerManager.Instance.AddPlayer(this);
}
public int i = 0;
private void FixedUpdate()
{
if (id == PlayerManager.Instance.GetMe().id)
{
//发送自己的坐标
PhotonEngine.Instance.GetRequest(OperationCode.Move).DefaultRequest();
}
}
// Update is called once per frame
void Update () {
if (id == PlayerManager.Instance.GetMe().id)
{
GetComponent<Transform>().Translate(new Vector3(Input.GetAxis("Horizontal")/30, 0, 0));
}
}
}
PlayerManager.cs:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerManager : MonoBehaviour {
public static PlayerManager Instance;
private PlayerController me;
public Dictionary<int, PlayerController> PlayDic = new Dictionary<int, PlayerController>();
public GameObject PlayerPf;
private void Awake()
{
Instance = this;
}
public void AddPlayer(PlayerController player)
{
if (me == null)
{
me = player;
//发送给服务器,显示自己
GameObject.Find("GameManager").GetComponent<ShowRequest>().DefaultRequest();
}
PlayDic.Add(player.id, player);
}
public PlayerController GetMe()
{
return me;
}
public void ShowOtherPlayer(int id)
{
if (id == me.id) return;
GameObject player = Instantiate(PlayerPf);
player.GetComponent<PlayerController>().id = id;
PlayDic.Add(id, player.GetComponent<PlayerController>());
}
public void MoveOtherPlayer(string param)
{
//解析参数 x,y,z,id
string[] list = param.Split(',');
int id = int.Parse(list[3]);
Vector3 v = new Vector3(float.Parse(list[0]), float.Parse(list[1]), float.Parse(list[2]));
//print("准备移动id:" + id + " location:" + v);
//设置对应id的player的坐标
DicTool.GetValue(PlayDic, id).GetComponent<Transform>().position = v;
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
PhotonEngine.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ExitGames.Client.Photon;
using Common;
public class PhotonEngine : MonoBehaviour,IPhotonPeerListener {
public static PhotonEngine Instance;
//用字典来缓存每次的请求和码
//这里就能体现出来我们Request抽象类的好处了,不然每次这边都是不同的类型,使用抽象类就统一了
private Dictionary<OperationCode, Request> RequestDic = new Dictionary<OperationCode, Request>();
public static PhotonPeer peer;
private void Awake()
{
if (Instance == null)
{
Instance = this;
//当前Gameobject不会随场景的销毁而销毁
DontDestroyOnLoad(this.gameObject);
}
}
void Start () {
peer = new PhotonPeer(this,ConnectionProtocol.Udp);
peer.Connect("127.0.0.1:5055", "MyGame6");//通过ip连接到到对应的我们config中配置的Allpication
}
void Update () {
peer.Service();//开启服务
}
private void OnDestroy()//销毁的时候
{
if(peer!=null&&peer.PeerState == PeerStateValue.Connected)//如果存在且保持连接状态
{
peer.Disconnect();//就断开连接
}
}
public void DebugReturn(DebugLevel level, string message)
{
}
public void OnEvent(EventData eventData)
{
if ((byte)OperationCode.Chat == eventData.Code)
{
var data = eventData.Parameters;
object intValue;
data.TryGetValue((byte)ParaCode.Chat, out intValue);
Debug.Log("收到服务器:"+intValue);
}
else if ((byte)OperationCode.Show == eventData.Code)
{
var data = eventData.Parameters;
object intValue;
data.TryGetValue((byte)ParaCode.Show, out intValue);
Debug.Log("收到Show:" + intValue);
PlayerManager.Instance.ShowOtherPlayer((int)intValue);
}
else if ((byte)OperationCode.Move == eventData.Code)
{
if (PlayerManager.Instance == null)//判空
return;
var data = eventData.Parameters;
object intValue;
data.TryGetValue((byte)ParaCode.Move, out intValue);
Debug.Log("收到Move:" + intValue);
PlayerManager.Instance.MoveOtherPlayer((string)intValue);
}
return;
switch (eventData.Code)
{
//case 1:
// //解析数据
// var data = eventData.Parameters;
// object intValue, StringValue;
// data.TryGetValue(1, out intValue);
// data.TryGetValue(2, out StringValue);
// Debug.Log("收到服务器的响应Event推送,OpCode:1" + intValue.ToString() + ":" + StringValue.ToString());
// break;
default:
break;
}
}
public void OnOperationResponse(OperationResponse operationResponse)
{
//Request request = null;
//此处就是工具类
DicTool.GetValue(RequestDic, (OperationCode)operationResponse.OperationCode)
.OnOprationRespionse(operationResponse);
//下面注释的代码可以用上方工具类代替
//bool b =RequestDic.TryGetValue((OperationCode)operationResponse.OperationCode, out request);
//if (b)
//{
// request.OnOprationRespionse(operationResponse);
//}
//else
//{
// Debug.LogError("未找到对应code的请求request");
//}
return;
//switch (operationResponse.OperationCode)
//{
// case 1:
// Debug.Log("收到服务器的响应,OpCode:1");
// //解析数据
// var data = operationResponse.Parameters;
// object intValue;
// data.TryGetValue(1, out intValue);
// object StringValue;
// data.TryGetValue(2, out StringValue);
// Debug.Log("收到客户端的请求,OpCode:1" + intValue.ToString() + ":" + StringValue.ToString());
// break;
// default:
// break;
// }
}
public void OnStatusChanged(StatusCode statusCode)
{
Debug.LogError(statusCode);
}
//private Dictionary<OperationCode, Request> RequestDic = new Dictionary<OperationCode, Request>();
public void AddRequest(Request r)
{
RequestDic.Add(r.OpCode, r);
}
public void RemoveRequest(Request r)
{
RequestDic.Remove(r.OpCode);
}
public Request GetRequest(OperationCode code)
{
return DicTool.GetValue(RequestDic, code);
}
}
———————————————————————————————————————————服务器
ShowHandler.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using Photon.SocketServer;
namespace RRGameServer.Handler
{
class ShowHandler : BaseHandler
{
public ShowHandler()
{
OpCode = Common.OperationCode.Show;
}
public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)
{
int id = (int)DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.Show);
MyGameServer.log.Info("收到Show的ID:" + id);
peer.id = id;
//返回:已经收到消息
OperationResponse response = new OperationResponse(operationRequest.OperationCode);
response.ReturnCode = (short)Common.ReturnCode.Success;
peer.SendOperationResponse(response, sendParameters);
//发送给其他的客户端 OnEvent
//发给自己
//推送一个Event
EventData ed = new EventData((byte)OperationCode.Show);//操作码
var data2 = new Dictionary<byte, object>();
data2.Add((byte)ParaCode.Show, id);//参数码
ed.Parameters = data2;
//peer.SendEvent(ed, new SendParameters());
foreach (var peerItem in MyGameServer.Instance.PeerList)
{
peerItem.SendEvent(ed, new SendParameters());
}
}
}
}
MoveHandler.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using Photon.SocketServer;
namespace RRGameServer.Handler
{
class MoveHandler : BaseHandler
{
public MoveHandler()
{
OpCode = Common.OperationCode.Move;
}
public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)
{
string location= (string)DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.Move);
MyGameServer.log.Info("收到Move的location:" + location);
//返回:已经收到消息
OperationResponse response = new OperationResponse(operationRequest.OperationCode);
response.ReturnCode = (short)Common.ReturnCode.Success;
peer.SendOperationResponse(response, sendParameters);
//发送给其他的客户端 OnEvent
//发给自己
//推送一个Event
EventData ed = new EventData((byte)OperationCode.Move);//操作码
var data2 = new Dictionary<byte, object>();
data2.Add((byte)ParaCode.Move, location+","+peer.id);//参数码
ed.Parameters = data2;
//peer.SendEvent(ed, new SendParameters());
foreach (var peerItem in MyGameServer.Instance.PeerList)
{
if (peer == peerItem) continue;
peerItem.SendEvent(ed, new SendParameters());
}
}
}
}
ClientPeer.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using Photon.SocketServer;
using PhotonHostRuntimeInterfaces;
using RRGameServer.Handler;
namespace RRGameServer
{
public class ClientPeer : Photon.SocketServer.ClientPeer
{
public int id;
public ClientPeer(InitRequest initRequest) : base(initRequest)
{
}
//当每个客户端断开时
protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
{
}
//当客户端发起请求的时候
protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
{
BaseHandler handler = DicTool.GetValue(MyGameServer.Instance.HandlerDic, (OperationCode)operationRequest.OperationCode);
if (handler != null)
{
handler.OnOperationRequest(operationRequest, sendParameters,this);
}
else
{
MyGameServer.log.Info("找不到操作码:" + (OperationCode)operationRequest.OperationCode);
}
return;
//switch (operationRequest.OperationCode)
//{
// case 1:
// //解析数据
// var data = operationRequest.Parameters;
// object intValue;
// data.TryGetValue(1, out intValue);
// object StringValue;
// data.TryGetValue(2, out StringValue);
// //输出参数
// MyGameServer.log.Info("收到客户端的请求,opcode:1"+intValue.ToString()+":"+StringValue.ToString());
// //返回响应
// OperationResponse opResponse = new OperationResponse(operationRequest.OperationCode);
// //构造参数
// var data2 = new Dictionary<byte, object>();
// data2.Add(1, 100);
// data2.Add(2, "这是服务端做的响应");
// opResponse.SetParameters(data2);
// //返回code,为发送过来的code,返回的参数,为发送过来的参数
// SendOperationResponse(opResponse, sendParameters);
// //推送一个Event
// EventData ed = new EventData(1);
// ed.Parameters = data2;
// SendEvent(ed, new SendParameters());
// break;
// default:
// break;
//}
}
}
}
MyGameServer.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using ExitGames.Logging;
using ExitGames.Logging.Log4Net;
using log4net.Config;
using Photon.SocketServer;
using RRGameServer.Handler;
using RRGameServer.Manager;
namespace RRGameServer
{
//所有的Server,都要继承ApplicationBase,然后实现ApplicationBase的三个方法
public class MyGameServer : ApplicationBase
{
public static MyGameServer Instance;
public static readonly ILogger log = LogManager.GetCurrentClassLogger();
public List<PeerBase> PeerList = new List<PeerBase>();//用户保存所有的peer,一个客户端连接就会有一个peer
//当有一个客户端连接上以后,就会执行此方法
protected override PeerBase CreatePeer(InitRequest initRequest)
{
log.Info("一个客户端连接");
var p = new ClientPeer(initRequest);
PeerList.Add(p);
return p;
}
//服务器初始化函数
protected override void Setup()
{
Instance = this;
log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(
Path.Combine(this.ApplicationRootPath, "bin_Win64"), "log");
FileInfo configFileInfo = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));
if (configFileInfo.Exists)
{
LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance);//photon知道日志输出
XmlConfigurator.ConfigureAndWatch(configFileInfo);//读取配置
}
log.Info("服务器启动啦");
//log.Info(UserManager.Instance.GetUserById(7).Username);
InitHandler();//初始化
}
//服务器关闭函数
protected override void TearDown()
{
log.Info("服务器关闭啦");
}
public Dictionary<OperationCode, BaseHandler> HandlerDic = new Dictionary<OperationCode, BaseHandler>();
public void InitHandler()
{
//一开始就全部初始化
LoginHandler LH = new LoginHandler();
//log.Info("初始化操作码:" + LH.OpCode);
HandlerDic.Add(LH.OpCode, LH);
SignHandler handler = new SignHandler();
HandlerDic.Add(handler.OpCode, handler);
ChatHandler chathandler = new ChatHandler();
HandlerDic.Add(chathandler.OpCode, chathandler);
ShowHandler showhandler = new ShowHandler();
HandlerDic.Add(showhandler.OpCode, showhandler);
MoveHandler movehandler = new MoveHandler();
HandlerDic.Add(movehandler.OpCode, movehandler);
}
}
}