(三)中主要完成了服务器端的基础功能开发,本篇博客主要针对客户端进行功能开发。
0.客户端的基础准备工作
客户端的开发在Unity的场景中进行,首先导入UI框架和游戏环境的unitypackage包,
下载地址:https://download.csdn.net/download/s1314_jhc/10566458
并在Asset下创建一个Resources文件夹,用以存放资源素材。并建立一个UIPanel文件夹以存放所有的UI面板,如下图
打开Map文件夹下的Scene,导入场景,可以看到场景的环境效果还是十分不错的。
1.基本客户端框架
客户端的大致架构如下(其中GameFacade负责对整体脚本进行管理):
根据上图创建出所需的脚本文件
首先创建BaseManage,对于这个脚本,不需要继承自MonoBehavior,因为不需要作为组件存在,
继承自MonoBehavior的优点和缺点:https://blog.csdn.net/flyfish0113/article/details/50565217
代码如下(主要是控制初始化)
public class BaseManage {
protected GameFacade facade; //提供一个对GameFacade的引用,当一个Manage想调用另一个Manage中的方法时,需要以GameFacade为中介,限定为protected方便子类调用
public BaseManage(GameFacade facade)
{
this.facade = facade;
}
public virtual void OnInit() { }
public virtual void OnDestroy() { }
}
对于UIManage中,我们让它继承自BaseManage,并取消原有的单例模式,如下。
public class UIManager:BaseManage {
public UIManager(GameFacade facade):base(facade){} //继承父类的构造方法
}
同理所有的Manage都继承自BaseManage,并在GameFacade下统一管理。
public class GameFacade : MonoBehaviour {
private UIManager uiMng;
private AudioManage audioMng;
private CameraManage cameraMng;
private PlayerManage playerMng;
private RequestManage requestMng;
private ClientManage clientMng;
// Use this for initialization
void Start () {
InitMng();
}
void InitMng()
{
uiMng = new UIManager(this); //别的Manage初始化的销毁方法类似,不赘述
}
private void DestroyMng()
{
uiMng.OnDestroy();
}
}
void OnDestroy() //监听Destroy事件
{
DestroyMng();
}
以上就完成了客户端的基本框架搭建,接下来处理网络模块,即RequestManage和ClientManage,这两个功能完善后才能处理UIManage(控制登陆,需要借助服务器端访问数据库)
2.ClientManage与服务器端的连接
由于服务端与客户端收发消息的方法类似,因此服务端中Message类中的代码可以作为参考,在Scripts>Net中新建一个Message类,将代码进行拷贝。由于Message类中包含服务器端的一个Common共享类,我们将Common编译生成,并将dll复制到Unity目录下(dll包含对RequestCode和ActionCode的定义)。
在ClientManage中添加一个方法,负责数据的发送
public void SendRequest(RequestCode requestCode, ActionCode actionCode, string data)
{
byte[] bytes = Message.PackData(requestCode, actionCode, data);
clientSocket.Send(bytes);
}
其中Message.PackData()方法对原有服务器端的方法进行了修改,添加了ActionCode的一个参数,代码如下
这里注意:服务器端向客户端发送数据时,需要数据长度+RequestCode+数据三个部分。相反,客户端向服务器端发送数据则需要数据长度数据长度+RequestCode+ActionCode+数据四个部分。参考(三)中1.4中的图 https://blog.csdn.net/s1314_jhc/article/details/81152269
public static byte[] PackData(RequestCode requestData,ActionCode actionCode, string data)
{
byte[] requestCodeByte = BitConverter.GetBytes((int)requestData); //获取个部分的数据长度并进行拼接
byte[] actionCodeByte = BitConverter.GetBytes((int)actionCode);
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
int dataAmount = requestCodeByte.Length + actionCodeByte.Length + dataBytes.Length;
byte[] dataAmountBytes = BitConverter.GetBytes(dataAmount);
return dataAmountBytes.Concat(requestCodeByte).ToArray<byte>() //从前往后进行拼接
.Concat(actionCodeByte).ToArray<byte>()
.Concat(dataBytes).ToArray<byte>();
}
至此,客户端向服务器端发送数据的功能就实现了。
对于服务器端发送的消息,客户端解析方法如下,在ClientManage加入代码
private Message msg = new Message();
public override void OnInit()
{
base.OnInit();
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try //考虑到连接可能会失败,进行异常捕捉
{
clientSocket.Connect(IP, PORT);
StartListen(); //开始监听
}catch(Exception e)
{
Debug.LogWarning("warning:无法连接到服务器端,请检查网络!" + e);
}
}
public void StartListen()
{
clientSocket.BeginReceive(msg.Data,msg.GetStartIdx,msg.RemainSize, SocketFlags.None, ReceiveCallBack, null);
}
private void ReceiveCallBack(IAsyncResult ar)
{
try
{
int count = clientSocket.EndReceive(ar); //接收到的数据
msg.ReadMsg(count, OnProcessDataCallBack);
}
catch (Exception e)
{
Debug.Log("消息接收出现异常:"+e);
}
}
private void OnProcessDataCallBack(RequestCode requestCode,string data)
{
//todo:该函数负责解析服务器端传递的数据,需要完善RequestCode后才能继续。
}
(上)部分主要梳理了客户端的整体架构以及客户端收发消息的处理,(下)部分将会对客户端进行完善,完善之后会上传游戏工程的代码。