仿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

仿LOL项目开发第八天的更多相关文章

  1. 仿LOL项目开发第九天

    仿LOL项目开发第九天 by 草帽 OK,今天我们完全换了一种风格,抛弃了Unity3d的c#语法,我们来写写java的项目. 说到java服务器,当然有些人可能鄙视java的服务器速度太慢,但是相对 ...

  2. 仿LOL项目开发第七天

    仿LOL项目开发第七天 by 草帽 不知不觉已经写到了第七篇这种类型的博客,但是回过头看看之前写的,发现都只能我自己能看懂. 我相信在看的童鞋云里雾里的,因为我基本上没怎么详细讲一个脚本怎么用?但是你 ...

  3. 仿LOL项目开发第六天

    仿LOL项目开发第六天 by草帽 OK,因为更新模块已经处理好了,接着开始登陆的编写.那么我们就需要状态机的管理. 所谓状态机就是在哪个状态执行那个状态的代码逻辑: 那么我们开始编写GameState ...

  4. 仿LOL项目开发第五天

    仿LOL项目开发第五天 by草帽 今天呢,我们看下能开发什么内容,首先上节我们已经讲了UI框架的搭建,上节还遗留下很多问题,比如说消息的字符是代码里面自己赋值的. 那么就比较死板,按照正常的逻辑,那些 ...

  5. 仿LOL项目开发第四天

    ---恢复内容开始--- 仿LOL项目开发第四天 by草帽 上节讲了几乎所有的更新版本的逻辑,那么这节课我们来补充界面框架的搭建的讲解. 我们知道游戏中的每个界面都有自己的一个类型:比如登陆界面,创建 ...

  6. 仿LOL项目开发第三天

    仿LOL项目开发第二天 by草帽 昨个我们已经实现了下载功能,但是发现没有,下载的包是压缩的,没有解压开,那么Unity是识别不了的. 所以今个我们来讲讲如何实现解压文件. 还记得吗,我们在Downl ...

  7. 仿LOL项目开发第二天

    仿LOL项目开发第二天 by草帽 接着上节来讲,上节更新还没开始写代码逻辑,今天我们补充完整. 我们找到VersionManager脚本里面的CheckVersion方法: 首先我们想到检测版本,需要 ...

  8. 仿LOL项目开发第一天

    ---恢复内容开始--- 仿LOL项目开发第一天 by---草帽 项目源码研究群:539117825 最近看了一个类似LOL的源码,颇有心得,所以今天呢,我们就来自己开发一个类似于LOL的游戏demo ...

  9. IT第八天 - 类的应用、debug、项目开发模式优化

    IT第八天 上午 类的应用 1.对象在实例化时是非常耗费系统资源的,因此要尽量减少new字段的使用 2.类的初始值是null,在使用未实例化的类时,很容易导致报错:NullExceptionPoint ...

随机推荐

  1. CCF CSP 201609-4 交通规划

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201609-4 交通规划 问题描述 G国国王来中国参观后,被中国的高速铁路深深的震撼,决定为自 ...

  2. 安装配置SVN

    官网下载地址,下载完之后如果想看到中文,可以下载语言包进行安装,安装之后TortoiseSVN -> Settings -> General -> Language选项中选择:中文( ...

  3. C语言:写一函数,将两个字符串中的元音字母复制到另一个字符串,然后输出

    题目描述 写一函数,将两个字符串中的元音字母复制到另一个字符串,然后输出. 输入 一行字符串 输出 顺序输出其中的元音字母(aeiuo) 样例输入 abcde 样例输出 ae 编码: #include ...

  4. WepE

    1. 计算机管理 第一个主:1024MB FAT32 活动,主分区 第二个主: * exFAT 主分区 都分配盘符2. WePE安装包 安装到移动硬盘 双启导 指定指一个主分区

  5. TypeScript学习笔记(四) - 类和接口

    本篇将介绍TypeScript里的类和接口. 与其他强类型语言类似,TypeScript遵循ECMAScript 2015标准,支持class类型,同时也增加支持interface类型. 一.类(cl ...

  6. BC-NFS安装依赖

    [root@BC-NFS01 glusterFS_installer]# sh install_local.sh 18-09-19 22:43:28 [install_local.sh] INFO : ...

  7. 【Java】返回长度为零的数组或者集合,而不是null

    今天在牛客网上做一个编程题时,在提交代码后老是抛出NullPointerException异常,大概的代码如下: public ArrayList<Integer> foo(TreeNod ...

  8. TI科学家谈浮点DSP未来发展

        自十多年前浮点数字信号处理器(DSP)诞生以来,便为实时信号处理提供了算术上更为先进的备选方案.不过,定点器件至今仍是业界的主流.当然低成本是主要原因.定点DSP每器件产品的价格很低,这对大规 ...

  9. Python 递归删除非空目录(包括子目录以及文件)

    Python的OS模块自带rmdir和removedirs函数用于删除目录,但是两者都不能删除非空目录,以下代码定义了一个函数 remove_dir 用于删除非空目录. #作者官网 http://ww ...

  10. Django 模板中使用css, javascript

    Django 模板中使用css, javascript (r'^css/(?Ppath.*)$', 'django.views.static.serve', {'document_root': '/v ...