1.加入游戏
BattleFieldReuqest里面JoinRequest:把告诉服务器我要加入了,发送的数据里面key是type,值是join。
然后BattleFieldHandler里面对code判断,如果code是join,就返回player的Index给客户端,并且推送Event给所有客户端。
客户端在解析数据,拿到plauerIndex,并且在对应点实例化出来这个玩家。并且将index赋值给其PlayerMove脚本,并且将isMe设置为true。添加到客户端的PlayerList里面。
在处理Event方面,其他玩家判断type是不是join,然后把新的玩家添加到场景中。
2.同屏移动。
如果当前是自己,按下键盘获取到轴,向发起移动请求MoveRequest,发Horizatal和verital的数值,0或者+-1。
然后服务端对code判断,如果code是move,就发送event给所有客户端。
然后客户端处理Event,如果type是move,就解析数据,并且调用MovePlayer方法,MovePlayer根据index找到要移动的那个物体,去移动。(这里主要要更新一下坐标,((只要更新一下就行了!不要一直更新
3.攻击
如果当前是自己,点击攻击,遍历客户端的玩家列表,如果item不是自己并且距离小于xxx,就播放攻击动画,并且在攻击动画结束的那一帧向服务端发起Attack请求,里面包含被攻击玩家的index。
然后服务端根据code判断,如果code是attack,就将被攻击玩家的id发送Event给所有客户端。
客户端处理Event,判断code是否为Attack,然后拿到index,调用attack方法:遍历玩家列表,谁的index等于当前index,谁就扣血,被攻击,在其出实例化攻击特效。
_____________________________________________________________________________
客户端
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;
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");
}
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)
{
DicTool.GetValue(RequestDic, (OperationCode)eventData.Code).
OnEvent(eventData);
return;
if ((byte)OperationCode.Chat == eventData.Code)
{
var data = eventData.Parameters;
object intValue;
data.TryGetValue((byte)ParaCode.Chat, out intValue);
Debug.Log("收到服务器:" + intValue+ eventData.Code);
}
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)
{
DicTool.GetValue(RequestDic, (OperationCode)operationResponse.OperationCode).
OnOprationRespionse(operationResponse);
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);
}
}
BattleFieldRequest.cs:
using System.Collections;
using System.Collections.Generic;
using ExitGames.Client.Photon;
using UnityEngine;
using Common;
using System;
public class BattleFieldRequest : Request
{
public static BattleFieldRequest Instance;
private int curX;
private int curY;
private void Awake()
{
base.Awake();
if (Instance == null)
{
Instance = this;
}
}
public override void DefaultRequest()
{
}
public override void OnOprationRespionse(OperationResponse operationResponse)
{
//解析数据
if (operationResponse.ReturnCode == (byte)ReturnCode.Success)
{
int playerIndex = (int)DicTool.GetValue<byte, object>(operationResponse.Parameters, (byte)ParaCode.BF_Join);
Debug.Log("playerIndex:" + playerIndex);
BattleFieldManager.Instance.InitBattleField(playerIndex);
}
else
{
Debug.Log("请求失败");
}
}
internal void MoveRequest(int posX, int posY, Vector3 pos)
{
//在更新操作的时候,才发送请求
if (curX == posX && curY == posY) return;
curX = posX;
curY = posY;
//构造参数
var data = new Dictionary<byte, object>();
data.Add((byte)ParaCode.ParaType, ParaCode.BF_Move);
data.Add((byte)ParaCode.BF_Move, BattleFieldManager.Instance.MyPlayerIndex.ToString() + "," + posX + "," + posY + "," + pos.x + "," + pos.y + "," + pos.z);
//发送
PhotonEngine.peer.OpCustom((byte)OpCode, data, true);
}
internal void AttackRequest(int playerIndex)
{
//构造参数
var data = new Dictionary<byte, object>();
//构造参数
data.Add((byte)ParaCode.ParaType, ParaCode.BF_Att);
data.Add((byte)ParaCode.BF_Att, playerIndex);
//发送
PhotonEngine.peer.OpCustom((byte)OpCode, data, true);
}
void Start()
{
}
void Update()
{
}
public void JoinRequest()
{
var data = new Dictionary<byte, object>();
//key 是 本次请求类型 ,value 是 加入战场
data.Add((byte)ParaCode.ParaType, ParaCode.BF_Join);
PhotonEngine.peer.OpCustom((byte)OpCode, data, true);
}
public override void OnEvent(EventData data)
{
//解析数据
ParaCode type = (ParaCode)DicTool.GetValue<byte, object>(data.Parameters, (byte)ParaCode.ParaType);
// Debug.Log("收到服务器:" + type);
if (type == ParaCode.BF_Join)
{
string allPlayer = (string)DicTool.GetValue<byte, object>(data.Parameters, (byte)ParaCode.BF_Join);
// Debug.Log("收到服务器:" + allPlayer);
BattleFieldManager.Instance.AddPlayer(allPlayer);
}
else if (type == ParaCode.BF_Move)
{
string para = (string)DicTool.GetValue<byte, object>(data.Parameters, (byte)ParaCode.BF_Move);
// Debug.Log("收到服务器BF_Move:" + para);
var list = para.Split(',');
BattleFieldManager.Instance.MovePlayer(Convert.ToInt16(list[0]), Convert.ToInt16(list[1]), Convert.ToInt16(list[2]), float.Parse(list[3]), float.Parse(list[4]), float.Parse(list[5]));
}
else if (type == ParaCode.BF_Att)
{
int para = (int)DicTool.GetValue<byte, object>(data.Parameters, (byte)ParaCode.BF_Att);
Debug.Log("收到服务器BF_Move:" + para);
BattleFieldManager.Instance.AttPlayer(para);
}
}
}
PlayerMove.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class PlayerMove : MonoBehaviour
{
public int playerIndex;
public bool isMe = false;
internal short DirX;
internal short DirY;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
//把指令发送给服务器
if (isMe)
{
//获取输入轴
int posX = (int)Input.GetAxis("Horizontal");
int posY = (int)Input.GetAxis("Vertical");
BattleFieldRequest.Instance.MoveRequest(posX, posY, transform.position);
if (Input.GetKeyDown(KeyCode.Mouse0))
{
//获取到攻击对象,如果有,发送攻击指令
foreach (var item in BattleFieldManager.Instance.playerList)
{
if (item != this)
{
var dis = Vector3.Distance(item.transform.position, this.transform.position);
if (dis < 1f)
{
//切换动画状态机
GetComponent<Animator>().SetTrigger("attack");
attIndex = item.playerIndex;
GetComponent<Transform>().LookAt(item.GetComponent<Transform>().position);
}
}
}
}
}
Move(DirX, DirY);
}
public int attIndex;
[HideInInspector]
public int HP = 100;
public void OnAttackAnimation()
{
//发送攻击请求
BattleFieldRequest.Instance.AttackRequest(attIndex);
}
public void Move(int x, int y)
{
var speed = new Vector3(x, 0, y);
GetComponent<Transform>().LookAt(GetComponent<Transform>().position + speed);
//控制 nav agent移动
GetComponent<NavMeshAgent>().velocity = new Vector3(x, 0, y) / 2.4f;
//切换动画状态机
GetComponent<Animator>().SetFloat("speed", speed.magnitude);
}
}
BattleFieldManager.cs:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BattleFieldManager : MonoBehaviour
{
public static BattleFieldManager Instance;
public int MyPlayerIndex = -1;
public GameObject Hero;
public List<Transform> H1Pos;
public List<PlayerMove> playerList = new List<PlayerMove>();
public GameObject attFx;
private void Awake()
{
Instance = this;
}
void Start()
{
}
// Update is called once per frame
void Update()
{
}
internal void InitBattleField(int playerIndex)
{
//初始化场景
MyPlayerIndex = playerIndex;
//开始实例化
PlayerMove p = (Instantiate(Hero, H1Pos[MyPlayerIndex - 1].position, Quaternion.identity) as GameObject).GetComponent<PlayerMove>();
p.playerIndex = MyPlayerIndex;
p.isMe = true;
playerList.Add(p);
}
internal void AddPlayer(string allPlayer)
{
//解析
var arr = allPlayer.Split(',');
foreach (var item in arr)//遍历在场玩家的index
{
try//最后多了一个空格,所以转换成int会失败,不过这里忽略不计
{
//转换成int
int i = Convert.ToInt16(item);
if (i != MyPlayerIndex)
{
print("不是自己:" + i);
PlayerMove p = (Instantiate(Hero, H1Pos[i - 1].position, Quaternion.identity) as GameObject).GetComponent<PlayerMove>();
p.playerIndex = i;
playerList.Add(p);
}
}
catch (Exception)
{
}
}
}
internal void MovePlayer(short index, short x, short y, float Px, float Py, float Pz)
{
foreach (var item in playerList)
{
//拿到对应index的对象
if (item.playerIndex == index)
{
//设置移动方向
item.DirX = x;
item.DirY = y;
item.transform.position = new Vector3(Px, Py, Pz);
}
}
}
internal void AttPlayer(int index)
{
//扣血
foreach (var item in playerList)
{
if (item.playerIndex == index)
{
item.HP -= 20;
print(index + " 被攻击,剩余血量 " + item.HP);
//播放特效fx
Instantiate(attFx, item.transform.position, Quaternion.identity);
}
}
}
}
———————————————————————————————————————————
服务端
GameModel.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RRGameServer
{
class GameModel
{
public static GameModel Instance = new GameModel();
public List<ClientPeer> PeerList = new List<ClientPeer>();//用户保存所有的peer,一个客户端连接就会有一个peer
}
}
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();
//当有一个客户端连接上以后,就会执行此方法
protected override PeerBase CreatePeer(InitRequest initRequest)
{
log.Info("一个客户端连接");
var p = new ClientPeer(initRequest);
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()
{
//一开始就全部初始化
BattleFieldHandler bh = new BattleFieldHandler();
HandlerDic.Add(bh.OpCode, bh);
}
}
}
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 playerIndex;
public int id;
public ClientPeer(InitRequest initRequest) : base(initRequest)
{
GameModel.Instance.PeerList.Add(this);
}
//当每个客户端断开时
protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail)
{
GameModel.Instance.PeerList.Remove(this);
}
//当客户端发起请求的时候
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;
//}
}
}
}
BattleFieldHandler.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 BattleFieldHandler : BaseHandler
{
public BattleFieldHandler()
{
OpCode = Common.OperationCode.BattleField;
}
public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, ClientPeer peer)
{
//取出key为ParaType的参数,判断一下我们是什么类型的请求
ParaCode code = (ParaCode)DicTool.GetValue<byte, object>(operationRequest.Parameters,(byte)ParaCode.ParaType);
if (code == ParaCode.BF_Join)
{
//加入的时候,发送PlayerIndex给对方。根据当前玩家的数量返回序号
OperationResponse response = new OperationResponse(operationRequest.OperationCode);
//获取序号
peer.playerIndex = GameModel.Instance.PeerList.Count;
//参数创建一个dic
var data2 = new Dictionary<byte, object>();
data2.Add((byte)ParaCode.BF_Join, peer.playerIndex);
response.ReturnCode = (short)ReturnCode.Success;
response.Parameters = data2;
peer.SendOperationResponse(response, sendParameters);
//广播到所有客户端,有新玩家加入
//获取所有玩家的序列号,保存为一条字符串
var allplayer = "";
foreach (var peerItem in GameModel.Instance.PeerList)
{
allplayer += peerItem.playerIndex + ",";
}//1,2,
//推送一个Event
EventData ed = new EventData((byte)operationRequest.OperationCode);
var data3 = new Dictionary<byte, object>();
//注意,这里2个参数
data3.Add((byte)ParaCode.ParaType, ParaCode.BF_Join);
data3.Add((byte)ParaCode.BF_Join, allplayer);
ed.Parameters = data3;
foreach (var peerItem in GameModel.Instance.PeerList)
{
peerItem.SendEvent(ed, new SendParameters());
}
}
else if (code == ParaCode.BF_Move)
{
string p = (string)DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.BF_Move);
//转发给所有客户端
EventData ed = new EventData((byte)operationRequest.OperationCode);
var data3 = new Dictionary<byte, object>();
//注意,这里2个参数
data3.Add((byte)ParaCode.ParaType, ParaCode.BF_Move);
data3.Add((byte)ParaCode.BF_Move,p);
ed.Parameters = data3;
foreach (var peerItem in GameModel.Instance.PeerList)
{
peerItem.SendEvent(ed, new SendParameters());
}
}
else if (code == ParaCode.BF_Att)
{
int p = (int)DicTool.GetValue<byte, object>(operationRequest.Parameters, (byte)ParaCode.BF_Att);
//转发给所有客户端
EventData ed = new EventData((byte)operationRequest.OperationCode);
var data3 = new Dictionary<byte, object>();
//注意,这里2个参数
data3.Add((byte)ParaCode.ParaType, ParaCode.BF_Att);
data3.Add((byte)ParaCode.BF_Att, p);
ed.Parameters = data3;
foreach (var peerItem in GameModel.Instance.PeerList)
{
peerItem.SendEvent(ed, new SendParameters());
}
}
}
}
}
_____________________________________________________________________________
协议:
Class1.cs:
namespace Common
{
public enum OperationCode : byte
{
Login,
SignIn,
Chat,
Show,
Move,
BattleField
}
public enum ParaCode : byte
{
UserName,
Password,
Chat,
Show,
Move,
ParaType,
BF_Join,
BF_Move,
BF_Att
}
public enum ReturnCode : short
{
Success,
Failed
}
}
|