使用工具:VS2017,unity3d
使用语言:c#
作者:Gemini_xujian
参考:siki老师-《丛林战争》视频教程
上一篇文章中,我已经把服务器端的框架进行了搭建,接下来,我们开始搭建客户端方面的内容。
资源准备:首先,将我们需要用到的资源导入到项目中,分别是之前我们开发的UI框架包,以及场景、音频、DOTween插件、纹理图片等资源。
资源的目录结构如下:
01-创建客户端架构基础类:
首先,我们创建一个名为GameFacade的类,这个类作为所有类的中介类,降低耦合性。
然后创建一个基础管理类BaseManager,用来作为每个模块的管理类的基类。
basemanager类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//基础管理类
public class BaseManager {
public virtual void OnInit() { }
public virtual void OnDestory() { }
}
我们需要管理的模块有UI、摄像机、音频、角色、网络请求,在我们导入的UI框架中,我们已经创建了UI的管理类,我们需要对UI的管理类进行一些修改,以符合我们的整体架构。
UIManager类:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class UIManager:BaseManager {
///
/// 单例模式的核心
/// 1,定义一个静态的对象 在外界访问 在内部构造
/// 2,构造方法私有化
//private static UIManager _instance;
//public static UIManager Instance
//{
// get
// {
// if (_instance == null)
// {
// _instance = new UIManager();
// }
// return _instance;
// }
//}
private Transform canvasTransform;
private Transform CanvasTransform
{
get
{
if (canvasTransform == null)
{
canvasTransform = GameObject.Find("Canvas").transform;
}
return canvasTransform;
}
}
private Dictionary panelPathDict;//存储所有面板Prefab的路径
private Dictionary panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
private Stack panelStack;
public UIManager()
{
ParseUIPanelTypeJson();
}
///
/// 把某个页面入栈, 把某个页面显示在界面上
///
public void PushPanel(UIPanelType panelType)
{
if (panelStack == null)
panelStack = new Stack();
//判断一下栈里面是否有页面
if (panelStack.Count > 0)
{
BasePanel topPanel = panelStack.Peek();
topPanel.OnPause();
}
BasePanel panel = GetPanel(panelType);
panel.OnEnter();
panelStack.Push(panel);
}
///
/// 出栈 ,把页面从界面上移除
///
public void PopPanel()
{
if (panelStack == null)
panelStack = new Stack();
if (panelStack.Count <= 0) return;
//关闭栈顶页面的显示
BasePanel topPanel = panelStack.Pop();
topPanel.OnExit();
if (panelStack.Count <= 0) return;
BasePanel topPanel2 = panelStack.Peek();
topPanel2.OnResume();
}
///
/// 根据面板类型 得到实例化的面板
///
///
private BasePanel GetPanel(UIPanelType panelType)
{
if (panelDict == null)
{
panelDict = new Dictionary();
}
//BasePanel panel;
//panelDict.TryGetValue(panelType, out panel);//TODO
BasePanel panel = panelDict.TryGet(panelType);
if (panel == null)
{
//如果找不到,那么就找这个面板的prefab的路径,然后去根据prefab去实例化面板
//string path;
//panelPathDict.TryGetValue(panelType, out path);
string path = panelPathDict.TryGet(panelType);
GameObject instPanel = GameObject.Instantiate(Resources.Load(path)) as GameObject;
instPanel.transform.SetParent(CanvasTransform,false);
panelDict.Add(panelType, instPanel.GetComponent());
return instPanel.GetComponent();
}
else
{
return panel;
}
}
[Serializable]
class UIPanelTypeJson
{
public List infoList;
}
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary();
TextAsset ta = Resources.Load("UIPanelType");
UIPanelTypeJson jsonObject = JsonUtility.FromJson(ta.text);
foreach (UIPanelInfo info in jsonObject.infoList)
{
//Debug.Log(info.panelType);
panelPathDict.Add(info.panelType, info.path);
}
}
///
/// just for test
///
public void Test()
{
string path ;
panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);
Debug.Log(path);
}
}
这是我们导入的UIMnager类中代码内容,相比我们之前开发UI框架时不同的是,我们将UIManager的单例模式取消注释掉了,并将此类的构造函数权限公开。
02-创建客户端架构基础类
首先我们将每个模块的管理类创建出来:
AudioManager类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioManager : BaseManager {
}
其他模块的管理类也是类似的,都是要继承自basemanager类,然后,我们在创建好所有模块的管理类之后,需要在中介类中队所有的模块管理类有一个统一的管理。代码如下:
GameFacade类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameFacade : MonoBehaviour {
private UIManager uiMng;
private AudioManager audioMng;
private PlayerManager playerMng;
private RequestManager reqeustMng;
private CameraManager cameraMng;
private ClientManager clientMng;
// Use this for initialization
void Start () {
InitManager();
}
// Update is called once per frame
void Update () {
}
private void InitManager()
{
uiMng = new UIManager();
audioMng = new AudioManager();
playerMng = new PlayerManager();
reqeustMng = new RequestManager();
cameraMng = new CameraManager();
clientMng = new ClientManager();
uiMng.OnInit();
audioMng.OnInit();
playerMng.OnInit();
reqeustMng.OnInit();
cameraMng.OnInit();
clientMng.OnInit();
}
private void DestroyManager()
{
uiMng.OnDestory();
audioMng.OnDestory();
playerMng.OnDestory();
reqeustMng.OnDestory();
cameraMng.OnDestory();
clientMng.OnDestory();
}
private void OnDestroy()
{
DestroyManager();
}
}
我们在此脚本中,对所有的管理类统一进行了初始化和销毁。
03-开发ClientManager,跟服务器端连接的建立和关闭
在服务器端,我们需要有一个主函数开始执行我们的程序,我们在program类中的静态入口方法中开始服务器端的监听:
using GameServer.Servers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameServer
{
class Program
{
static void Main(string[] args)
{
Server server = new Server("127.0.0.1", 6789);
}
}
}
初始化的方法中我们使用了本机的IP和一个自定义的端口号。
接下来我们需要完善我们的clientmanager类,完成与服务器端的连接工作。
clientmanager代码如下所示:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
public class ClientManager:BaseManager {
private const string IP="127.0.0.1";
private const int PORT = 6789;
private Socket clientSocket;
public override void OnInit()
{
base.OnInit();
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
clientSocket.Connect(IP, PORT);
}
catch (Exception e)
{
Debug.Log("无法连接到服务器端,请检查网络:"+e);
}
}
public override void OnDestory()
{
base.OnDestory();
try
{
clientSocket.Close();
}
catch (Exception e)
{
Debug.Log("无法关闭与服务器端的连接:"+e);
}
}
}
在clientmanager中,我们首先建立了一个与服务器的连接,然后增加了关闭连接的方法。并进行了异常处理。
04-共享工程的dll的导入问题和Message类的导入
将我们在服务器端开发过程中使用的common项目的DLL文件拷贝到unity的plugins文件夹下(自行创建此目录),然后创建一个名为Message的脚本,将服务器端的Message脚本复制一份替换我们客户端的Message类。
客户端Message类如下:
using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GameServer.Servers
{
class Message
{
private byte[] data = new byte[1024];//用来存储现在的数据,需要足够大
private int startIndex = 0;//用来保存当前已经存取的数据位置
public byte[] Data
{
get
{
return data;
}
}
public int StartIndex
{
get
{
return startIndex;
}
}
public int RemainSizs
{
get
{
return data.Length - startIndex;
}
}
////更新索引
//public void AddCount(int count)
//{
// startIndex += count;
//}
///
/// 解析数据
///
public void ReadMessage(int newDataAmount,Action processDataCallback)
{
startIndex += newDataAmount;
while (true)
{
if (startIndex <= 4) return;
int count = BitConverter.ToInt32(data, 0);
if (startIndex - 4 >= count)
{
RequestCode requestCode= (RequestCode)BitConverter.ToInt32(data, 4);//得到requestcode
ActionCode actionCode = (ActionCode)BitConverter.ToInt32(data,8);//得到actioncode
string s = Encoding.UTF8.GetString(data, 12, count-8);//得到数据
processDataCallback(requestCode, actionCode, s);
Array.Copy(data, count+4, data, 0, startIndex - 4 - count);
startIndex -= count + 4;
}
else
{
break;
}
}
}
//数据包装
public static byte[] PackData(ActionCode actionCode,string data)
{
byte[] requestCodeBytes = BitConverter.GetBytes((int)actionCode);//将actioncode转换成字节数组
byte[] dataBytes = Encoding.UTF8.GetBytes(data);//将数据转换成byte数组
int dataAmount = requestCodeBytes.Length + dataBytes.Length;//得到数据长度
byte[] dataAmountBytes = BitConverter.GetBytes(dataAmount);//将数据长度转换成byte数组
byte[] newBytes = dataAmountBytes.Concat(requestCodeBytes).ToArray();
return newBytes.Concat(dataBytes).ToArray();
}
}
}
05-开发客户端向服务器端请求的发送功能
先上修改的代码:
clientmanager类:
using Common;
using GameServer.Servers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
public class ClientManager:BaseManager {
private const string IP="127.0.0.1";
private const int PORT = 6789;
private Socket clientSocket;
public override void OnInit()
{
base.OnInit();
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
clientSocket.Connect(IP, PORT);
}
catch (Exception e)
{
Debug.Log("无法连接到服务器端,请检查网络:"+e);
}
}
public void SendRequest( RequestCode requestCode,ActionCode actionCode,string data)
{
byte[] bytes = Message.PackData(requestCode, actionCode, data);
clientSocket.Send(bytes);
}
public override void OnDestory()
{
base.OnDestory();
try
{
clientSocket.Close();
}
catch (Exception e)
{
Debug.Log("无法关闭与服务器端的连接:"+e);
}
}
}
message类:
using Common;
using System;
using System.Linq;
using System.Text;
namespace GameServer.Servers
{
class Message
{
private byte[] data = new byte[1024];//用来存储现在的数据,需要足够大
private int startIndex = 0;//用来保存当前已经存取的数据位置
public byte[] Data
{
get
{
return data;
}
}
public int StartIndex
{
get
{
return startIndex;
}
}
public int RemainSizs
{
get
{
return data.Length - startIndex;
}
}
////更新索引
//public void AddCount(int count)
//{
// startIndex += count;
//}
///
/// 解析数据
///
public void ReadMessage(int newDataAmount, Action processDataCallback)
{
startIndex += newDataAmount;
while (true)
{
if (startIndex <= 4) return;
int count = BitConverter.ToInt32(data, 0);
if (startIndex - 4 >= count)
{
RequestCode requestCode = (RequestCode)BitConverter.ToInt32(data, 4);//得到requestcode
ActionCode actionCode = (ActionCode)BitConverter.ToInt32(data, 8);//得到actioncode
string s = Encoding.UTF8.GetString(data, 12, count - 8);//得到数据
processDataCallback(requestCode, actionCode, s);
Array.Copy(data, count+4, data, 0, startIndex - 4 - count);
startIndex -= count + 4;
}
else
{
break;
}
}
}
//数据包装
public static byte[] PackData(RequestCode requestCode,ActionCode actionCode, string data)
{
byte[] requestCodeBytes = BitConverter.GetBytes((int)requestCode);
byte[] actionCodeBytes = BitConverter.GetBytes((int)actionCode);//将actioncode转换成字节数组
byte[] dataBytes = Encoding.UTF8.GetBytes(data);//将数据转换成byte数组
int dataAmount = requestCodeBytes.Length + dataBytes.Length+actionCodeBytes.Length;//得到数据长度
byte[] dataAmountBytes = BitConverter.GetBytes(dataAmount);//将数据长度转换成byte数组
//byte[] newBytes = dataAmountBytes.Concat(requestCodeBytes).ToArray();
//return newBytes.Concat(dataBytes).ToArray();
return dataAmountBytes.Concat(requestCodeBytes).ToArray()
.Concat(actionCodeBytes).ToArray()
.Concat(dataBytes).ToArray();
}
}
}
首先,我们在clientmanager中添加一个sendreqeust方法用来发送请求,然后在message类中队打包数据的方法进行修改,因为我们需要将打包好的数据发送到服务器端,所以,我们需要有请求类型和事件类型以及数据信息,所以在原来的基础上做出来一些调整。
06-接收服务器端的消息并解析
为了能够接收到服务器端的消息,我们需要在clientmanager中写一个用来接收消息的方法,并在message中对数据做出解析。
先上代码:
clientmanager类:
using Common;
using GameServer.Servers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
public class ClientManager:BaseManager {
private const string IP="127.0.0.1";
private const int PORT = 6789;
private Socket clientSocket;
private Message msg=new Message();
public ClientManager(GameFacade facade) : base(facade)
{
}
public override void OnInit()
{
base.OnInit();
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
clientSocket.Connect(IP, PORT);
Start();
}
catch (Exception e)
{
Debug.Log("无法连接到服务器端,请检查网络:"+e);
}
}
void Start()
{
clientSocket.BeginReceive(msg.Data, msg.StartIndex, msg.RemainSizs, SocketFlags.None,ReceiveCallBack,null);
}
private void ReceiveCallBack(IAsyncResult ar)
{
try
{
int count = clientSocket.EndReceive(ar);
msg.ReadMessage(count, OnPressDataCallBack);
Start();
}
catch (Exception e)
{
Debug.Log(e);
}
}
private void OnPressDataCallBack(ActionCode actionCode,string data)
{
}
public void SendRequest( RequestCode requestCode,ActionCode actionCode,string data)
{
byte[] bytes = Message.PackData(requestCode, actionCode, data);
clientSocket.Send(bytes);
}
public override void OnDestory()
{
base.OnDestory();
try
{
clientSocket.Close();
}
catch (Exception e)
{
Debug.Log("无法关闭与服务器端的连接:"+e);
}
}
}
message类:
using Common;
using System;
using System.Linq;
using System.Text;
namespace GameServer.Servers
{
class Message
{
private byte[] data = new byte[1024];//用来存储现在的数据,需要足够大
private int startIndex = 0;//用来保存当前已经存取的数据位置
public byte[] Data
{
get
{
return data;
}
}
public int StartIndex
{
get
{
return startIndex;
}
}
public int RemainSizs
{
get
{
return data.Length - startIndex;
}
}
////更新索引
//public void AddCount(int count)
//{
// startIndex += count;
//}
///
/// 解析数据
///
public void ReadMessage(int newDataAmount, Action processDataCallback)
{
startIndex += newDataAmount;
while (true)
{
if (startIndex <= 4) return;
int count = BitConverter.ToInt32(data, 0);
if (startIndex - 4 >= count)
{
ActionCode actionCode = (ActionCode)BitConverter.ToInt32(data, 4);//得到actioncode
string s = Encoding.UTF8.GetString(data, 8, count - 4);//得到数据
processDataCallback(actionCode, s);
Array.Copy(data, count+4, data, 0, startIndex - 4 - count);
startIndex -= count + 4;
}
else
{
break;
}
}
}
//数据包装
public static byte[] PackData(RequestCode requestCode,ActionCode actionCode, string data)
{
byte[] requestCodeBytes = BitConverter.GetBytes((int)requestCode);
byte[] actionCodeBytes = BitConverter.GetBytes((int)actionCode);//将actioncode转换成字节数组
byte[] dataBytes = Encoding.UTF8.GetBytes(data);//将数据转换成byte数组
int dataAmount = requestCodeBytes.Length + dataBytes.Length+actionCodeBytes.Length;//得到数据长度
byte[] dataAmountBytes = BitConverter.GetBytes(dataAmount);//将数据长度转换成byte数组
//byte[] newBytes = dataAmountBytes.Concat(requestCodeBytes).ToArray();
//return newBytes.Concat(dataBytes).ToArray();
return dataAmountBytes.Concat(requestCodeBytes).ToArray()
.Concat(actionCodeBytes).ToArray()
.Concat(dataBytes).ToArray();
}
}
}
在clientmanager中,我们添加了一个start方法,并在此脚本初始化时进行了调用,在start方法中,我们进行了异步接收消息的处理。
在message类中,我们修改了接收到的消息的数据结构,因为在客户端中,服务器发送过来的数据只包含了actioncode值,所以我们需要做出相应的修改,在readmessage方法中,我认为理解困难一点的是copy函数的使用,这句代码中的参数分别表示源数据,开始复制的位置,目标数据,开始粘贴的位置,总共复制的数据长度,在readmessage方法中,这句话表示的意思是:因为我们已经处理了一条完整的数据信息,所以,如果while条件满足,那么我们可以继续处理下一条数据信息,所以,我们需要先将处理完的数据替换成新的部分,第二个参数count+4是(actioncode长度+信息数据的长度)+记录本条数据指令长度的数据的长度。
07-修改BaseManager
在basemanager中添加一个对gamefacade的引用,并在构造方法中赋值,然后在每一个使用basemanager作为父类的脚本的构造方法进行重写。在gamefacade类中需要在初始化各个模块的manager时进行赋值。而UIManager类需要对构造方法进行修改,删除无参的构造方法,并将原构造方法中的代码复制粘贴到带参数的构造方法中。
basemanager类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//基础管理类
public class BaseManager {
protected GameFacade facade;
public BaseManager(GameFacade facade)
{
this.facade = facade;
}
public virtual void OnInit() { }
public virtual void OnDestory() { }
}
AudioManager类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioManager : BaseManager
{
public AudioManager(GameFacade facade) : base(facade)
{
}
}
gamefacade类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameFacade : MonoBehaviour {
private UIManager uiMng;
private AudioManager audioMng;
private PlayerManager playerMng;
private RequestManager reqeustMng;
private CameraManager cameraMng;
private ClientManager clientMng;
// Use this for initialization
void Start () {
InitManager();
}
// Update is called once per frame
void Update () {
}
private void InitManager()
{
uiMng = new UIManager(this);
audioMng = new AudioManager(this);
playerMng = new PlayerManager(this);
reqeustMng = new RequestManager(this);
cameraMng = new CameraManager(this);
clientMng = new ClientManager(this);
uiMng.OnInit();
audioMng.OnInit();
playerMng.OnInit();
reqeustMng.OnInit();
cameraMng.OnInit();
clientMng.OnInit();
}
private void DestroyManager()
{
uiMng.OnDestory();
audioMng.OnDestory();
playerMng.OnDestory();
reqeustMng.OnDestory();
cameraMng.OnDestory();
clientMng.OnDestory();
}
private void OnDestroy()
{
DestroyManager();
}
}
UImanager类:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class UIManager:BaseManager {
///
/// 单例模式的核心
/// 1,定义一个静态的对象 在外界访问 在内部构造
/// 2,构造方法私有化
//private static UIManager _instance;
//public static UIManager Instance
//{
// get
// {
// if (_instance == null)
// {
// _instance = new UIManager();
// }
// return _instance;
// }
//}
private Transform canvasTransform;
private Transform CanvasTransform
{
get
{
if (canvasTransform == null)
{
canvasTransform = GameObject.Find("Canvas").transform;
}
return canvasTransform;
}
}
private Dictionary panelPathDict;//存储所有面板Prefab的路径
private Dictionary panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
private Stack panelStack;
public UIManager(GameFacade facade) : base(facade)
{
ParseUIPanelTypeJson();
}
///
/// 把某个页面入栈, 把某个页面显示在界面上
///
public void PushPanel(UIPanelType panelType)
{
if (panelStack == null)
panelStack = new Stack();
//判断一下栈里面是否有页面
if (panelStack.Count > 0)
{
BasePanel topPanel = panelStack.Peek();
topPanel.OnPause();
}
BasePanel panel = GetPanel(panelType);
panel.OnEnter();
panelStack.Push(panel);
}
///
/// 出栈 ,把页面从界面上移除
///
public void PopPanel()
{
if (panelStack == null)
panelStack = new Stack();
if (panelStack.Count <= 0) return;
//关闭栈顶页面的显示
BasePanel topPanel = panelStack.Pop();
topPanel.OnExit();
if (panelStack.Count <= 0) return;
BasePanel topPanel2 = panelStack.Peek();
topPanel2.OnResume();
}
///
/// 根据面板类型 得到实例化的面板
///
///
private BasePanel GetPanel(UIPanelType panelType)
{
if (panelDict == null)
{
panelDict = new Dictionary();
}
//BasePanel panel;
//panelDict.TryGetValue(panelType, out panel);//TODO
BasePanel panel = panelDict.TryGet(panelType);
if (panel == null)
{
//如果找不到,那么就找这个面板的prefab的路径,然后去根据prefab去实例化面板
//string path;
//panelPathDict.TryGetValue(panelType, out path);
string path = panelPathDict.TryGet(panelType);
GameObject instPanel = GameObject.Instantiate(Resources.Load(path)) as GameObject;
instPanel.transform.SetParent(CanvasTransform,false);
panelDict.Add(panelType, instPanel.GetComponent());
return instPanel.GetComponent();
}
else
{
return panel;
}
}
[Serializable]
class UIPanelTypeJson
{
public List infoList;
}
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionary();
TextAsset ta = Resources.Load("UIPanelType");
UIPanelTypeJson jsonObject = JsonUtility.FromJson(ta.text);
foreach (UIPanelInfo info in jsonObject.infoList)
{
//Debug.Log(info.panelType);
panelPathDict.Add(info.panelType, info.path);
}
}
///
/// just for test
///
public void Test()
{
string path ;
panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);
Debug.Log(path);
}
}
08-创建BaseRequest请求的基类
我们在unity中创建一个request的文件夹,然后创建一个名为BaseRequest的脚本,脚本中做了对Awake方法的虚方法,方便我们在所有以baserequest为基类的脚本中继承并重写。并且在baserequest中,我们定义了两个虚方法,分别用来发送请求和对请求进行响应。最后写了一个销毁的虚方法,用来处理request的销毁。
BaseRequest类:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BaseRequest : MonoBehaviour {
// Use this for initialization
public virtual void Awake () {
}
//请求的发起
public virtual void SendRequest()
{
}
//请求的响应
public virtual void OnResponse(string data)
{
}
public virtual void OnDestroy()
{
}
}
09-Request对象的管理
首先贴出修改的脚本代码:
RequestManager类:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RequestManager : BaseManager
{
public RequestManager(GameFacade facade) : base(facade)
{
}
private Dictionary reqeustDict = new Dictionary();//用来管理所有的request
public void AddRequest(ActionCode actionCode,BaseRequest request)
{
reqeustDict.Add(actionCode, request);
}
public void RemoveRequest(ActionCode actionCode)
{
reqeustDict.Remove(actionCode);
}
}
GameFacade类:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameFacade : MonoBehaviour {
private static GameFacade _instance;
public static GameFacade Instance
{
get
{
return _instance;
}
}
private UIManager uiMng;
private AudioManager audioMng;
private PlayerManager playerMng;
private RequestManager requestMng;
private CameraManager cameraMng;
private ClientManager clientMng;
private void Awake()
{
if (_instance != null)
{
Destroy(this.gameObject);
return;
}
_instance = this;
}
// Use this for initialization
void Start () {
InitManager();
}
// Update is called once per frame
void Update () {
}
private void InitManager()
{
uiMng = new UIManager(this);
audioMng = new AudioManager(this);
playerMng = new PlayerManager(this);
requestMng = new RequestManager(this);
cameraMng = new CameraManager(this);
clientMng = new ClientManager(this);
uiMng.OnInit();
audioMng.OnInit();
playerMng.OnInit();
requestMng.OnInit();
cameraMng.OnInit();
clientMng.OnInit();
}
private void DestroyManager()
{
uiMng.OnDestory();
audioMng.OnDestory();
playerMng.OnDestory();
requestMng.OnDestory();
cameraMng.OnDestory();
clientMng.OnDestory();
}
private void OnDestroy()
{
DestroyManager();
}
public void AddRequest(ActionCode actionCode,BaseRequest request)
{
requestMng.AddRequest(actionCode,request);
}
public void RemoveRequest(ActionCode actionCode)
{
requestMng.RemoveRequest(actionCode);
}
}
BaseRequest类:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BaseRequest : MonoBehaviour {
private ActionCode actionCode=ActionCode.None;
// Use this for initialization
public virtual void Awake () {
GameFacade.Instance.AddRequest(actionCode,this);
}
//请求的发起
public virtual void SendRequest()
{
}
//请求的响应
public virtual void OnResponse(string data)
{
}
public virtual void OnDestroy()
{
}
}
说明:在baserequest类中,我们定义了一个actioncode,用来判断当前处理的请求类型,接着,我们在requestmanager中定义了一个字典用来存储所有的请求类,并实现了处理字典的添加和删除的方法,在gamefacade中,我们对requestmanager的添加删除方法进行了转发,在baserequest类中进行了调用。
10-把消息转发给对应的Request处理
先贴代码:
clientmanager:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameFacade : MonoBehaviour {
private static GameFacade _instance;
public static GameFacade Instance
{
get
{
return _instance;
}
}
private UIManager uiMng;
private AudioManager audioMng;
private PlayerManager playerMng;
private RequestManager requestMng;
private CameraManager cameraMng;
private ClientManager clientMng;
private void Awake()
{
if (_instance != null)
{
Destroy(this.gameObject);
return;
}
_instance = this;
}
// Use this for initialization
void Start () {
InitManager();
}
// Update is called once per frame
void Update () {
}
private void InitManager()
{
uiMng = new UIManager(this);
audioMng = new AudioManager(this);
playerMng = new PlayerManager(this);
requestMng = new RequestManager(this);
cameraMng = new CameraManager(this);
clientMng = new ClientManager(this);
uiMng.OnInit();
audioMng.OnInit();
playerMng.OnInit();
requestMng.OnInit();
cameraMng.OnInit();
clientMng.OnInit();
}
private void DestroyManager()
{
uiMng.OnDestory();
audioMng.OnDestory();
playerMng.OnDestory();
requestMng.OnDestory();
cameraMng.OnDestory();
clientMng.OnDestory();
}
private void OnDestroy()
{
DestroyManager();
}
public void AddRequest(ActionCode actionCode,BaseRequest request)
{
requestMng.AddRequest(actionCode,request);
}
public void RemoveRequest(ActionCode actionCode)
{
requestMng.RemoveRequest(actionCode);
}
public void HandleResponse(ActionCode actionCode,string data)
{
requestMng.HandleResponse(actionCode, data);
}
}
gamefacade:
using Common;
using GameServer.Servers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
public class ClientManager:BaseManager {
private const string IP="127.0.0.1";
private const int PORT = 6789;
private Socket clientSocket;
private Message msg=new Message();
public ClientManager(GameFacade facade) : base(facade)
{
}
public override void OnInit()
{
base.OnInit();
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
clientSocket.Connect(IP, PORT);
Start();
}
catch (Exception e)
{
Debug.Log("无法连接到服务器端,请检查网络:"+e);
}
}
void Start()
{
clientSocket.BeginReceive(msg.Data, msg.StartIndex, msg.RemainSizs, SocketFlags.None,ReceiveCallBack,null);
}
private void ReceiveCallBack(IAsyncResult ar)
{
try
{
int count = clientSocket.EndReceive(ar);
msg.ReadMessage(count, OnPressDataCallBack);
Start();
}
catch (Exception e)
{
Debug.Log(e);
}
}
private void OnPressDataCallBack(ActionCode actionCode,string data)
{
facade.HandleResponse(actionCode, data);
}
public void SendRequest( RequestCode requestCode,ActionCode actionCode,string data)
{
byte[] bytes = Message.PackData(requestCode, actionCode, data);
clientSocket.Send(bytes);
}
public override void OnDestory()
{
base.OnDestory();
try
{
clientSocket.Close();
}
catch (Exception e)
{
Debug.Log("无法关闭与服务器端的连接:"+e);
}
}
}
requestmanager:
using Common;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RequestManager : BaseManager
{
public RequestManager(GameFacade facade) : base(facade)
{
}
private Dictionary reqeustDict = new Dictionary();//用来管理所有的request
public void AddRequest(ActionCode requestCode,BaseRequest request)
{
reqeustDict.Add(requestCode, request);
}
public void RemoveRequest(ActionCode actionCode)
{
reqeustDict.Remove(actionCode);
}
public void HandleResponse(ActionCode actionCode,string data)
{
BaseRequest request= reqeustDict.TryGet(actionCode);
if (request == null)
{
Debug.LogWarning("无法得到actioncode["+actionCode+"]对应的request类:");
return;
}
request.OnResponse(data);
}
}
说明:我们在requestmanager中通过handleresponse方法把消息转发给了对应的request进行处理,并在gamefacade中对此方法进行了转发,最终在clientmanager中进行了调用,此过程的意义是:当接收到服务器端发送过来的数据信息后,我们首先对数据进行解析,然后将解析好的数据发送到facade,facade帮我们转发给requestmanager,然后requestmanager将数据发送到了相应的request中进行处理。