游戏开发-丛林战争制作2

20.如何解析我们客户端的信息并且交给ControllerManager来进行处理

首先是Message类,参数分别是数据的长度和一个回掉的函数委托,用来重复解析数据,这里的话前四个是数据长度,后面4-8是RequestCode,8-12是ActionCode,12以后是数据,我们按照这个来重复对数据进行解析

publicvoid ReadMessage(intnewDateAmount,Action<RequestCode,ActionCode,string> ProcessDataCallBack)

       {

            startIndex += newDateAmount;

            while (true)

            {

                if (startIndex <= 4)

                {

                    return;

                }

                //读取数组的长度,自动省略前四个字节长度

                int count = BitConverter.ToInt32(data, 0);

                if (startIndex - 4 >= count)

                {

                    //解析的是传递的RequestCode

                    RequestCode requestCode =(RequestCode)BitConverter.ToInt32(data, 4);

                    //解析ActionCode

                    ActionCode actionCode =(ActionCode)BitConverter.ToInt32(data, 8);

                    //解析我们的数据,从12开始,解析的长度为count-8

                    String s =Encoding.UTF8.GetString(data, 12, count-8);

                    //回调解析

                    ProcessDataCallBack(requestCode, actionCode,s);

                    Array.Copy(data, count + 4,data, 0, startIndex - count - 4);

                    startIndex -= (count + 4);

                }

                else { break; }

 

            }

        }

然后我们要在Sever里提供一个中介方法来调用ControllerManager里的HandelRequest来处理对客户端发送信息的解析

 

publicvoidHandleRequest(RequestCode requestCode, ActionCode actionCode, string data, Clientclient)

       {

            //这个作为中介,用来作为client和ControllerManager的中间商

            controllerManager.HandleRequest(requestCode,actionCode, data, client);

       }

然后我们在客户端里重复调用ReadMessage完成对信息的解析

publicvoidReceiveCallBack(IAsyncResult ar)

       {

            try

            {

                int count = clientSocket.EndReceive(ar);

               if (count == 0)

                {

                    Close();

                }

                //使用message对client接收到的信息进行解析,这里分别传递的参数是数据长度和回调的方法

               message.ReadMessage(count,OnProcessMessage);

                Start();

            }

 

            catch (Exception e)

            {

                Close();

                Console.WriteLine(e);

            }

 

       }

       //回调信息的处理,这里使用sever里的HandleRequest来处理对客户端的响应,为了避免耦合没有直接使用ControllerManager

       publicvoid OnProcessMessage(RequestCoderequestCode,ActionCode actionCode,string data)

       {

            sever.HandleRequest(requestCode,actionCode, data, this);

       }

21.打包解析我们客户端发送的数据

1.我们在Message里提供一个工具类来打包我们的客户端发送的数据

   publicstaticbyte[]PackData(RequestCode requestCode,string data)

       {

            //这个方法是用来打包数据

            byte[] requestCodeBytes = BitConverter.GetBytes((int)requestCode);

            byte[] dataBytes = Encoding.UTF8.GetBytes(data);

            int dataAmount = requestCodeBytes.Length + dataBytes.Length;

            byte[] dataAmountBytes = BitConverter.GetBytes(dataAmount);

            byte[]newdata=dataAmountBytes.Concat(requestCodeBytes).Concat(dataBytes) asbyte[];

            return newdata;

 

 

 

       }

2.在客户端里向socket发送打包后的信息

publicvoid SendMessage(RequestCoderequestCode,string data)

       {

            byte[]sendData= Message.PackData(requestCode, data);

            clientSocket.Send(sendData);

       }

3.在服务器中介中调用这个客户端发送消息的方法

publicvoid SendMessage(Clientclient,RequestCode requestCode,string data)

       {

           

            client.SendMessage(requestCode,data);

       }

4.ControllerManager判断是否需要向客户端发起回应,如果需要,就会传递对应信息

//通过sever端进行中介来发送数据

            sever.SendMessage(client,requestCode, o asstring);

22.创建ConnHelper,处理客户端的连接和关闭

1.我们在工具类里创建和数据库连接与关闭的方法

namespace游戏服务器.Tool

{

   classConnHelper

   {

       //这个是用来处理服务器连接的

       conststring CONNECTSTRING = "Database=game;Data Source=localhost;port=3306;UserId=root;Password=1234;";

 

       publicstatic MySqlConnection Connect()

       {

            //这里创建和数据库的连接

            MySqlConnection conn = newMySqlConnection(CONNECTSTRING);

            try

            {

                conn.Open();

                return conn;

            }

            catch (Exception e)

           {

                Console.WriteLine(e);

                returnnull;

            }

       }

       publicstaticvoidCloseConnect(MySqlConnection conn)

       {

            if (conn != null)

            {

                conn.Close();

           }

            else

            {

                Console.WriteLine("conn不能为空");

            }

       }

 

   }

}

2.在客户端的开始和关闭时调用这个工具类中的方法

publicvoid Close()

       {

           ConnHelper.CloseConnect(sqlConnection);

            if (clientSocket != null)

            {

                clientSocket.Close();

                sever.RemoverClient(this);

            }

       }

publicClient(Socket clientSocket, Sever sever)

       {

            //持有一个对socket的引用

            clientSocket = this.clientSocket;

            this.sever = sever;

            //创建客户端时候开始和数据库的连接

 

           sqlConnection=ConnHelper.Connect();

       }

22.引入我们的UI框架并且搭建我们的项目框架

首先创建我们的GameFacade,这个是唯一在场景中进行交互的脚本,然后我们创建我们的BaseManager,这个是我们各种Manager的基类,我们先在里面提供两个虚方法,实例化和销毁,后续再进行扩展,然后我们先将UIManager继承一下BaseManager

publicclassBaseManager  {

 

    public  virtualvoid OnInit() { }

   publicvirtualvoid  OnDestory() { }

}

23.搭建客户端的基本框架

   这里我们先创建上图中的各个Manager并且将它们实例化出来,这里实例化的操作我们放在GameFacade中进行

publicclassGameFacade : MonoBehaviour {

   //这个脚本是用来和Unity进行交互的

   // Use this for initialization

   privateAudioManager audioManager;

   privateCameraManager cameraManager;

   privateRequestManager requestManager;

   privatePlayerManager playerManager;

   privateClientManager clientManager;

    void Start () {

       InitManager();

    }

   

    // Update is called once per frame

    void Update () {

       

    }

   publicvoid InitManager()

   {

       audioManager = new AudioManager();

       cameraManager = new CameraManager();

       requestManager = new RequestManager();

       playerManager = new PlayerManager();

       clientManager = new ClientManager();

 

       audioManager.OnInit();

       cameraManager.OnInit();

       requestManager.OnInit();

       playerManager.OnInit();

       clientManager.OnInit();

   }

   publicvoid DestoryManager()

   {

       audioManager.OnDestory();

       cameraManager.OnDestory();

       requestManager.OnDestory();

       playerManager.OnDestory();

       clientManager.OnDestory();

   }

   privatevoid Ondestory()

   {

       DestoryManager();

   }

}

24.初步创建ClientManager并且和服务器端建立连接

这里就是提供IP地址和端口号,重写父类的初始化和销毁方法,用来进行开始连接和断开连接的操作

publicclassClientManager : BaseManager {

   privateconststring IP = "127.0.0.1";

   privateconstint Port = 6688;

   private SocketclientSocket;

   publicoverridevoid OnInit()

   {

       base.OnInit();

       //创建客户端socket和服务器连接

       clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);

       try

       {

            //和客户端建立连接

            clientSocket.Connect(IP, Port);

       }

       catch(Exceptione)

       {

            Console.WriteLine("无法与服务器进行连接" + e);

       }

   }

   publicoverridevoid OnDestory()

   {

       base.OnDestory();

       try

       {

            clientSocket.Close();

       }

       catch(Exception e)

       {

            Console.WriteLine("无法断开服务器进行连接" + e);

       }

   }

 

 

}

25,引入Common的dll文件和Message类

 

这里我们客户端和服务器端信息的发送大体类似,因此我们可以把服务器端的Message类直接引用到我们的Unity里,也用来做信息的处理

 这里我们先要删除之前的Common,重新生成一个以NetFramWork2.0为基础的共享类,然后将它的dll文件导入到我们的unity中的Plugins下,这样我们的Unity就可以成功调用它了

26.客户端向服务器发送信息

上节我们引入了Message类,这里我们重写一下它的打包方法使得它能打包客户端发送的信息,用来组拼数据长度,requestCode,ActionCode,data

publicstaticbyte[]PackData(RequestCode requestCode,ActionCode actionCode, string data)

       {

            //这个方法是用来打包数据

            byte[] requestCodeBytes = BitConverter.GetBytes((int)requestCode);

            byte[] actionCodeBytes = BitConverter.GetBytes((int)actionCode);

            byte[] dataBytes = Encoding.UTF8.GetBytes(data);

            int dataAmount = requestCodeBytes.Length +dataBytes.Length+actionCodeBytes.Length;

            byte[] dataAmountBytes = BitConverter.GetBytes(dataAmount);

            //byte[]newdata = dataAmountBytes.Concat(requestCodeBytes).ToArray<byte>();

            //returnnewdata.Concat(dataBytes).ToArray<byte>();

            return dataAmountBytes.Concat(requestCodeBytes).ToArray<byte>().

               Concat(actionCodeBytes).ToArray<byte>().

                Concat(dataBytes).ToArray<byte>();

 

 

 

        }

然后我们呢在ClientMannage里调用这个方法并且提供一个发送给服务器的方法

  publicvoidSendRequest(RequestCode requestCode,ActionCode actionCode,string data)

   {

      byte[]sendData=Message.PackData(requestCode, actionCode, data);

       clientSocket.Send(sendData);

    }

27.客户端接受服务器发送过来的消息并且解析

  这里的话,我们还是用之前提到的异步接受的方法来解析服务器端发送的数据

这里我们创建一个Start的方法并且在建立连接之后就调用它

   privatevoid Start()

   {

       //开始建立和服务器端的连接

       clientSocket.BeginReceive(msg.Data, msg.StartIndex, msg.RemainLength,SocketFlags.None, ReceiveCallBack, null);

 

   }

   privatevoidReceiveCallBack(IAsyncResult ar)

   {

       //异步接受服务器传递消息,并且解析它

       try

       {

            int count=clientSocket.EndReceive(ar);

            msg.ReadMessage(count,OnProcessCallBack);

       }

       catch(Exceptione)

       {

            Debug.Log(e);

       }

 

 

 

   }

   privatevoidOnProcessCallBack(RequestCode requestCode,string data)

   {

       //这是一个回调函数

 

    }

这里我们修改了一下之前Message里的ReadMessage这个方法

   publicvoid ReadMessage(int newDateAmount,Action<RequestCode,  string>ProcessDataCallBack)

       {

            startIndex += newDateAmount;

            while (true)

            {

                if (startIndex <= 4)

                {

                    return;

                }

                //读取数组的长度,自动省略前四个字节长度

                int count = BitConverter.ToInt32(data, 0);

                if (startIndex - 4 >= count)

                {

                    //解析的是传递的RequestCode

                    RequestCode requestCode =(RequestCode)BitConverter.ToInt32(data, 4);

                    //解析ActionCode

                   

                    //解析我们的数据,从8开始,解析的长度为count-4

                    String s =Encoding.UTF8.GetString(data, 8, count - 4);

                    //回调解析

                    ProcessDataCallBack(requestCode,  s);

                    Array.Copy(data, count + 4,data, 0, startIndex - count - 4);

                    startIndex -= (count + 4);

                }

                else { break; }

 

            }

        }

当然如何解析,我们放在下一节讲,这里就是完成了客户端接受服务器端发送信息的搭建

26.修改BaseManager,使得所有Manager都和GameFacade进行连接,然后每个Manager都提供一个对应的构造方法,继承自父类即可

protectedGameFacade facade;

   publicBaseManager(GameFacade facade)

   {

       this.facade= facade;

    }

27.创建BaseRequest来处理客户端对服务器的请求

publicclassBaseRequest : MonoBehaviour {

   privateRequestCode requestCode = RequestCode.None;

    publicvirtualvoid Awake()

   {

 

   }

   

   

}

28.创建RequestManager来管理Request

首先我们在RequestManager里创建一个字典用来存储客户端创建的Request,并且创建一个移除和添加Request的方法

publicclassRequestManager : BaseManager {

 

   publicRequestManager(GameFacade facade) : base(facade) { }

   privateDictionary<RequestCode, BaseRequest> requestDictionary = newDictionary<RequestCode, BaseRequest>();

 

   //添加Request

   publicvoidAddRequest(RequestCode requestCode,BaseRequest baseRequest)

   {

       requestDictionary.Add(requestCode, baseRequest);

   }

   //移除Request

   publicvoidRemoveRequest(RequestCode requestCode)

   {

       requestDictionary.Remove(requestCode);

   }

 

 

 

}

然后我们以GameFacaed作为中介者,提供与BaseRequest的接口

publicclassBaseRequest : MonoBehaviour {

   privateRequestCode requestCode = RequestCode.None;

    publicvirtualvoid Awake()

   {

       GameFacade._instance.AddRequest(requestCode, this);

   }

   publicvirtualvoid Destory()

   {

       GameFacade._instance.RemoveRequest(requestCode);

   }

   publicvirtualvoid SendRequest() { }

   publicvirtualvoid Response(string data) { }

   

   

}

然后我们在BaseRequest里调用GameFacade中的方法,并且提供一个接受和发送Request的虚方法

publicclassBaseRequest : MonoBehaviour {

   privateRequestCode requestCode = RequestCode.None;

    publicvirtualvoid Awake()

   {

       GameFacade._instance.AddRequest(requestCode, this);

   }

   publicvirtualvoid Destory()

   {

       GameFacade._instance.RemoveRequest(requestCode);

   }

   publicvirtualvoid SendRequest() { }

   publicvirtualvoid Response(string data) { }

   

   

}

29.将消息转发给对应的Request进行处理

首先,我们在RequestManager里提供一个方法来获取到对应的RequestCode,如果获取到,就调用baseRequest中的OnRespone对信息进行回复

 

 

publicvoidHandleResponse(RequestCode requestCode,string data)

   {

       //获得对应的Response

       BaseRequest baseRequest = requestDictionary.TryGet<RequestCode,BaseRequest>(requestCode);

       //回复对应的数据

       if(baseRequest == null)

       {

            Debug.LogWarning("无法得到对应的RequestCode" + requestCode);

       }

       baseRequest.OnResponse(data);

 

 

    }

在GameFaced中提供一个调用的接口

   publicvoidHandleResponse(RequestCode requestCode,string data)

   {

       requestManager.HandleResponse(requestCode, data);

    }

}

 这个方法是是在ClientManager里实现的,当客户端接收到信息后,异步调用这个方法,选取对应RequestCode,这样的话,我们就实现了客户端读取信息的同时选择到对应的RequestCode

   privatevoidOnProcessCallBack(RequestCode requestCode,string data)

    {

       //这是一个回调函数

       facade.HandleResponse(requestCode, data);

       

 

 

    }

30.开发登陆按钮

这里创建UIText,给它添加button组件,Transation改成Animation,给它添加上高亮时候变动大小的动画,就算初步开发我们的登录按钮了

31.设计UI界面

这里都是UGUI的一些基本操作,就不多赘述了,这里的话,我们引入了掉色板,当我们设计UI时,选取对应的颜色进行使用,会使得色调更加协调

32.创建我们面板管理脚本

这些脚本都继承自BasePanel,用来管理各个对应的面板,接下来的面板控制会放在这里面进行

 

33.修改一下我们Ui框架的json文件和对应面板的枚举类型,并且将我们的面板都放在这个路径下

publicenumUIPanelType  {

   Login,

   Regist,

   Start,

   Message

}

{

"infoList":

[

{"panelTypeString":"Regist",

"path":"UIPanel/RegistPanel"},

 

{"panelTypeString":"Message",

"path":"UIPanel/MessagePanel"},

 

{"panelTypeString":"Start",

"path":"UIPanel/StartPanel"},

 

{"panelTypeString":"Login",

"path":"UIPanel/LoginPanel"},

 

 

}

猜你喜欢

转载自blog.csdn.net/qq_36574586/article/details/78875802
今日推荐