unity网络实战开发(丛林战争)-正式开发阶段(014-游戏客户端与服务器端连接搭建)

使用工具:VS2017,unity3d

使用语言:c#

作者:Gemini_xujian

参考:siki老师-《丛林战争》视频教程

上一篇文章中,我已经把服务器端的框架进行了搭建,接下来,我们开始搭建客户端方面的内容。

资源准备:首先,将我们需要用到的资源导入到项目中,分别是之前我们开发的UI框架包,以及场景、音频、DOTween插件、纹理图片等资源。

资源的目录结构如下:

unity网络实战开发(丛林战争)-正式开发阶段(014-游戏客户端与服务器端连接搭建)_第1张图片

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-创建客户端架构基础类

首先我们将每个模块的管理类创建出来:

unity网络实战开发(丛林战争)-正式开发阶段(014-游戏客户端与服务器端连接搭建)_第2张图片unity网络实战开发(丛林战争)-正式开发阶段(014-游戏客户端与服务器端连接搭建)_第3张图片

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中进行处理。

你可能感兴趣的:(unity网络实战开发(丛林战争)-正式开发阶段(014-游戏客户端与服务器端连接搭建))