zoukankan      html  css  js  c++  java
  • 仿LOL项目开发第八天

    仿LOL项目开发第八天

                                                                           by 草帽

    这节我们继续上节所讲的内容,上节我们初始化好了LoginWindow,当我们点击确认选择服务器按钮的时候,就发送服务器id给游戏服务器。

    这里就开始涉及到客户端需要跟服务器的网络通信,所以我们得先来写个客户端的Socket框架:

    NetworkManager.cs单例脚本来管理。

    因为我之前做过了这个脚本,所以我特意封装了自己的网路管理network.dll,直接就可以拿来用。

    至于这里面的脚本,有兴趣的童鞋可以自己去反编译下看看。

    network.dll下载地址

    这个dll下载完成之后,我们新建一个文件夹,取名为Plugin,然后把这个dll拖到里面去。

    接着,开始正式的编写NetworkManager脚本:

    using UnityEngine;
    using System.Collections;
    using System;
    using Game;
    public class NetworkManager : Singleton<NetworkManager>
    {
        private WWW m_wwwRequest = null;
        private float m_fLastTimeForHeartBeat = 0f;
        public static string serverURL = "http://127.0.0.1/LOLGameDemo/serverUrl.xml";
        public static string serverIP = "127.0.0.1";
        public static int serverPort = 8000;
        public CNetProcessor m_netProcessor = null;
        public CNetwork m_network = null;
        private IXLog m_log = XLog.GetLog<NetworkManager>();
        public SocketState NetState
        {
            get
            {
                SocketState result;
                if (null != this.m_network)
                {
                    SocketState state = this.m_network.GetState();
                    result = state;
                }
                else
                {
                    result = SocketState.State_Closed;
                }
                return result;
            }
        }
        public void Init()
        {
            CRegister.RegistProtocol();
            CNetObserver cNetObserver = new CNetObserver();
            this.m_netProcessor = new CNetProcessor();
            this.m_netProcessor.Observer = cNetObserver;
            cNetObserver.oProc = this.m_netProcessor;
            this.m_network = new CNetwork();
            CPacketBreaker oBreaker = new CPacketBreaker();
            string newFullPath = SystemConfig.ResourceFolder;
            if (!this.m_network.Init(this.m_netProcessor, oBreaker, 65536u, 65536u, newFullPath, true))
            {
                this.m_log.Fatal("oNet.Init Error");
            }
            else
            {
                this.m_log.Debug("oNet.Init Success!");
                this.m_netProcessor.Network = this.m_network;
            }
        }
        public void FixedUpdate()
        {
            try
            {
                if (this.m_netProcessor != null && null != this.m_netProcessor.Network)
                {
                    this.m_netProcessor.Network.ProcessMsg();
                    //this.m_netProcessor.Network.CheckHeartBeat(CommonDefine.IsMobilePlatform);
                }
            }
            catch (Exception ex)
            {
                this.m_log.Fatal(ex.ToString());
            }
        }
        private IEnumerator ConnectToURL()
        {
            Debug.Log("ConnectToURL:" + NetworkManager.serverURL);
            this.m_wwwRequest = new WWW(NetworkManager.serverURL);
            bool flag = false;
            for (int i = 0; i < 10; i++)
            {
                yield return new WaitForSeconds(0.5f);
                if (this.m_wwwRequest.isDone)
                {
                    if (string.IsNullOrEmpty(this.m_wwwRequest.error))
                    {
                        if (!string.IsNullOrEmpty(this.m_wwwRequest.text))
                        {
                            string[] array = this.m_wwwRequest.text.Split(new char[]
    						{
    							':'
    						});
                            if (array.Length == 2)
                            {
                                NetworkManager.serverIP = array[0];//游戏网关服务器ip地址
                                NetworkManager.serverPort = Convert.ToInt32(array[1]);
                                flag = true;
                                this.RealConnectToServer();
                            }
                            else
                            {
                                Debug.LogError("m_wwwRequest.text=:" + this.m_wwwRequest.text);
                            }
                        }
                    }
                    else
                    {
                        Debug.LogError(this.m_wwwRequest.error.ToString());
                    }
                    break;
                }
            }
            if (null != this.m_wwwRequest)
            {
                this.m_wwwRequest.Dispose();
                this.m_wwwRequest = null;
            }
            if (!flag)
            {
                /*if (Singleton<Login>.singleton.IsLogined)
                {
                    Singleton<ReConnect>.singleton.OnConnectToURLFailed();
                }
                else
                {
                    Singleton<Login>.singleton.OnConnectToURLFailed();
                }*/
            }
            yield break;
        }
        private void RealConnectToServer()
        {
            this.m_log.Info(string.Format("ConnectToServer:{0}:{1}", NetworkManager.serverIP, NetworkManager.serverPort));
            if (!this.m_netProcessor.Network.Connect(NetworkManager.serverIP, NetworkManager.serverPort))
            {
                Debug.Log("Failed");
                /*if (!Singleton<Login>.singleton.IsLogined)
                {
                    Singleton<Login>.singleton.OnConnectFailed();
                }
                else
                {
                    Singleton<ReConnect>.singleton.OnConnectFailed();
                }*/
            }
            else
            {
                this.m_log.Info("Connecting...");
            }
        }
        public void ConnectToServer()
        {
            if (string.IsNullOrEmpty(NetworkManager.serverURL))
            {
                this.m_log.Error("serverURL IsNullOrEmpty");
            }
            LOLGameDriver.Instance.StartCoroutine(this.ConnectToURL());
        }
        public void SendMsg(CProtocol ptc)
        {
            try
            {
                if (null != this.m_netProcessor)
                {
                    this.m_netProcessor.Send(ptc);
                }
            }
            catch (Exception ex)
            {
                this.m_log.Fatal(ex.ToString());
            }
        }
        public void Close()
    	{
    		Debug.Log("NetWorkManager::Close");
    		if (null != this.m_network)
    		{
    			this.m_network.Close();
    		}
    		if (null != this.m_wwwRequest)
    		{
    			this.m_wwwRequest.Dispose();
    			this.m_wwwRequest = null;
    		}
    	}
        public void UnInit()
        {
            Debug.Log("NetWorkManager::OnApplicationQuit");
            if (null != this.m_network)
            {
                this.m_network.UnInit();
                this.m_network = null;
            }
            this.m_netProcessor = null;
        }
        /// <summary>
        /// 发送登陆请求
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="serverId"></param>
        public void SendLogin(string username, string password, int serverId)
        {
            CptcC2GReq_Login instance = new CptcC2GReq_Login() 
            {
                m_strUsername = username,
                m_strPassword = password,
                m_dwServerId = serverId
            };
            SendMsg(instance);
        }
    }

    因为客户端和服务器是有协议约定的,所以我们每条消息都必须准守这个协议,这里我在network.dll里面就搞了个协议抽象基类Cptrocol.cs,然后自己在Unity中新建CRegister.cs来管理这些消息。

    CRegister:

    using UnityEngine;
    using System.Collections;
    namespace Game
    {
        internal class CRegister
        {
            /// <summary>
            /// 注册消息协议
            /// </summary>
            public static void RegistProtocol() 
            {
                CProtocol.Register(new CptcG2CNtf_LoginResult());//1001,注册登录消息
            }
        }
    }

    可以有些童鞋还会有报错,这里我们得在写个类:CNetObserver:主要用于接收消息的监控

    using UnityEngine;
    using System.Collections;
    using Utility;
    namespace Game
    {
        public class CNetObserver : INetObserver
        {
            public CNetProcessor oProc = null;
            public void OnConnect(bool bSuccess)
            {
                if (!bSuccess)
                {
                }
                else
                {
                    XLog.Log.Debug("Connect Success!");
                }
            }
            public void OnClosed(NetErrCode nErrCode)
            {
                XLog.Log.Error(string.Format("OnClosed:{0}", nErrCode.ToString()));
            }
            public void OnReceive(int unType, int nLen)
            {
                Singleton<PerformanceAnalyzer>.singleton.OnRecevie((uint)nLen);
            }
            public void OnSend(int dwType, int nLen)
            {
                Singleton<PerformanceAnalyzer>.singleton.OnSend((uint)nLen);
            }
        }
    }

    OK,这样大致的网络通信的单例脚本就写完了。这里我讲下大致的连接步骤流程。

    首先,我们点击确认选择按钮,先判断是否已经连接上网关服务器,如果没有就开始连接。

    那么怎么个连接法,我这里采用的是,先访问web站点,从站点中获取网关服务器ip地址和端口号。这样的好处是能动态的登陆网关服务器,比如网关1崩溃了,我只要改下web站点的ip就可以了,如果在程序内写死的话,这样又得重新更新客户端,麻烦。

    大家可以看到我这边写的是我自己的web站点地址,这里,你们得改成你们自己写的。

    这里我写的ip地址是127.0.0.1,端口号8000,因为我们就在本地测试,所以就写本地ip,如果是腾讯或者阿里云的话,我们就改成他的ip地址即可。

    然后我们回到LoginCtrl里面,添加一个方法LoginStart,传递的参数是选择的服务器id:

        public void LoginStart(int serverId)
        {
            //如果已经连接上,就直接发送登陆消息请求
            if (Singleton<NetworkManager>.singleton.NetState == SocketState.State_Connected)
            {
                SystemConfig.SelectedServerIndex = serverId;//保存上次选择的服务器id
                SystemConfig.LocalSetting.SelectedServer = serverId;//保存到本地配置
                //发送登陆消息请求
                NetworkManager.singleton.SendLogin(this.username,this.password,serverId);
            }
            else 
            {
                Singleton<NetworkManager>.singleton.ConnectToServer();//开始连接服务器
            }
        }

    然后把这个方法添加到LoginWindow的SelectServerButton当做事件监听。

        /// <summary>
        /// 登陆游戏服务器
        /// </summary>
        /// <param name="go"></param>
        public void OnLoginServer(GameObject go)
        {
            if (this.m_selectedServerId >= 0)
            {
                LoginCtrl.singleton.LoginStart(this.m_selectedServerId);
            }
            else 
            {
                Debug.LogError("选择服务器不存在");
            }
        }

    OK,我们回到NetworkManager中,来写SendLogin这方法,主要是发送登陆消息给游戏网关服务器。

        /// <summary>
        /// 发送登陆请求
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="serverId"></param>
        public void SendLogin(string username, string password, int serverId)
        {
            CptcC2GReq_Login instance = new CptcC2GReq_Login() 
            {
                m_strUsername = username,
                m_strPassword = password,
                m_dwServerId = serverId
            };
            SendMsg(instance);
        }

    CptcC2GReq_Login这条就是登陆消息,遵守CProtocol

    using UnityEngine;
    using System.Collections;
    using Game;
    /// <summary>
    /// 客户端发送给服务器的登陆消息请求
    /// </summary>
    public class CptcC2GReq_Login : CProtocol
    {
        public string m_strUsername;//用户名
        public string m_strPassword;//密码
        public int m_dwServerId;//选择的服务器id
        public CptcC2GReq_Login()
            : base(1000)
        {
            this.m_strUsername = "";
            this.m_strPassword = "";
            this.m_dwServerId = 0;
        }
        public override CByteStream DeSerialize(CByteStream bs)
        {
            bs.Read(ref m_strUsername);
            bs.Read(ref m_strPassword);
            bs.Read(ref m_dwServerId);
            return bs;
        }
        public override CByteStream Serialize(CByteStream bs)
        {
            bs.Write(this.m_strUsername);
            bs.Write(this.m_strPassword);
            bs.Write(this.m_dwServerId);
            return bs;
        }
        public override void Process()
        {
            
        }
    }

    可见这条消息包括3个需要传递的变量:

        public string m_strUsername;//用户名
        public string m_strPassword;//密码
        public int m_dwServerId;//选择的服务器i

    有用户名、密码、还有选择的游戏服务器id。

    OK,客户端的通信已经完成,接着我们就得开始搭建服务器,我这里采用的是java的mina框架。

    不懂的童鞋可以去先了解一下,很快就能入门。

    CptcC2GReq_Login
  • 相关阅读:
    poj2104 Kth-Number
    bzoj2120 数颜色
    hdu5145 NPY and girls
    bzoj2734 集合选数
    bzoj3732 NetWork
    bzoj2152 聪聪可可
    hdu2036(多边形面积)
    超大次幂思路
    hdu 2030 统计汉字个数
    Hibernate 配置文件与映射文件 总结
  • 原文地址:https://www.cnblogs.com/CaomaoUnity3d/p/5675356.html
Copyright © 2011-2022 走看看