谁是大魔王 - 框架分析

在Login场景当中,运行起来后,有个按钮叫“登陆游戏”,点击下去后会连接服务器失败。

在TcpSocketClient的第58行会报一个错,尝试连接会连接失败,最好的办法就是不让他们进行连接,现在就是要找到在哪进行连接的,里面有个connect方法也被封装为Wrap文件了,也就是说肯定是Lua层在调用,报错的告诉我们,在NetWork.lua的71行

在这里的时候输出了一下,IP地址与端口号,但是,任然没有解决根本的问题,那就是任然不知道是哪里进行调用的,所以还得继续找

在Network的login方法中的237行调用了一下这个方法,现在最主要的就是再找到谁在调用这个login方法那基本上就找到了,反正就是依次往上去找,我们在“从文件中查找”找到了是谁在调用它

一个是LoginView.lua,另一个是它自己NetWork.lua,显然不是它自己,我们就去LoginView去看看

 

到了LoginView.lua,就看见了这句话,但是具体是哪个东西进行与服务器连接的呢?

分析一下,打开Login场景之后,没有立马进行服务器连接,当我们点击了“登陆游戏”之后,才会尝试与服务器进行连接,噢!由此一来,就知道了,这个与服务器连接是点下按钮之后出发的,那肯定是这个按钮身上有个事件,那我们得看一下Panel层级关系

这里有个LoginButton那肯定是在它身上添加了事件与监听器

果然,在LoginView.lua中发现了这一串代码,点击按钮之后就会用网络管理器进行一个与服务器的连接,IP和端口号都是出自这的所以说,直接把这里改成跳转场景的话,那肯定就不会进行连接服务器会直接进行跳转了吧,更改完的代码如下图

但是!我们知道是这么写代码进行跳转场景了,可是我们还得知道这句代码的根据是从哪里来的?

那么,我们就去找一个东西叫“场景管理器”

它直接将原生的“SceneManager”进行了封装,可以直接用Lua代码进行调用以及跳转场景,

我们通过文件查找字段“client.scene”,在LoginScene查到了一句话

 

这一句可以直接进行跳转场景,

 

刚刚我们尝试去找一个东西叫“场景管理器”但是它没有【误?】,它可能就存在于公共的地方,我们在Main.lua中找到一个表结构

 

 

 

就找到一个client ={} 的这个,没有其他数据结构来表示,所以我们就拿表来表示跟client重要相关的组件扔到client中,里面都加了什么东西

这张截图中,要额外说一个东西,看第56行,UnityEngine.GameObject();的话,它会生成一个空的游戏物体;

这个程序一旦运行,就会立马生成出一个游戏物体,然后这个游戏物体会client.globalObject给持有,同时也会被赋予名字“GlobalObject”和运行后不会被销毁

 

 

当需要加载资源的时候,就可以进行加载资源了。

这个Main是全局的,那么它底下的client表也是全局的,我们拿到了client,就能拿到client底下的go,也就能拿到它持有的ResourceManager

它也持有了很多东西【对象池】,【全局计数器】,【网络管理器】

这个scene代表了,当前在什么场景中。

这种语法就是底层实现了__call方法

有个Lua文件叫IScene.lua,要注意一下这个文件,它封装了很多很多方法

加载商城面板

首先得了解的是Form.lua里面的两个方法,一个是show另一个是setVisible,

这个show方法,里面传的是bool【布尔值】是true的话就创建出来,是false的话这个界面就自动销毁。

Form.cs

setVisible方法的话,true也是自动创建出面板,false则隐藏不销毁

我们可以这么理解,我当前这个LoginScene这个表,它实现了一个call函数,这个call函数指向了一个方法,就是我这样调的话,就相当于我在调这个方法,这个方法现在从结构上来看,它会返回一个对象,由这个scene来持有,因为它指向它返回的东西去了,赋=号就是在指向,所以有client.scene来指向LoginScene返回的对象

最主要的话,还是class这个 方法

下面,会介绍到class这个方法是怎么来的。

所以说,最后我们找到IScene,在IScene中就看见了

这个跳转的方法,也知道是怎么进行跳转的,因此在之前说过的

这个问题就回答出来了

至于它是什么场景都不重要,因为都能调到。。。。

 

Main.lua是整个游戏的入口,它是用tolua实现的只不过没有FrameWork这个框架,它的核心业务逻辑全是由这个Main函数进行接的

一个核心函数是Main ,另一个是OnLevelWasLoaded,

Main函数被C#给调用了

在这里读了整个文件,然后在将它们加载出来

这里十分之复杂,就不做过多的解释了,非常复杂!

在LuaClient中,

在这个方法中,直接执行一个文件

这个StartMain函数调用的也是非常神奇,经过很多层调用,才调用完毕,

 

 

 

第四层调用是在OnLoadFinished方法当中

第三层调用是LoadLuaFiles里面调用了这个方法

第二层调用在Init方法中调用了它

最初的调用是在生命周期函数Awake中调用的。

只要LuaClient加载到Hierarchy层上,就会执行Awake,Init,LoadLuaFiles,OnLoadFinished,最后执行到StartMain方法,就会DoFile(“Main.cs”);就会获取OnLevelWasLoaded这个函数,跳转场景的时候就会走这个方法

 

在这个 谁是大魔王 项目中,它们所加载的界面全部都是生成出来的,我们知道这个界面是怎么生成的,然后直接调用一个函数,这个界面就生成出来了。

 

 

在Main.lua中,有个全局的guiMgr= nil 的这个实例,

然后在第96行的时候就进行了一个对象的指向,肯定有class方法的帮助和__call。

 

在这里介绍了LuaFunction变量类型的 levelLoaded ,这个是来自 LuaClient.cs这个类的东西,说了gc 和非gc的使用方法

ulua:http://bbs.ulua.org/article/ulua/toluadeexamples02scriptsfromfile20160320204728.html

 

在C#这边注册了之后,那么Lua就能通知的到,OnLevelWasLoaded就会被调用到也会执行

 

第119行它会执行guimgr的onLevelLoaded方法,它也有一个通知

首先他去找canvas对象,assert函数就类似于C#的try catch ,有异常就会报

 

Lua遇到不期望的情况时就会抛出错误,比如:两个非数字进行相加;调用一个非函数的变量;访问表中不存在的值等。你也可以通过调用error函数显示的抛出错误,error的参数是要抛出的错误信息。

assert(a,b) a是要检查是否有错误的一个参数,ba错误时抛出的信息。第二个参数b是可选的。

 

第50行的canvas是我们从层级面板上找到的canvas对象,就是那个画布

在52行的时候获取了一个它自己身上的canvas组件

52行到56行都在获取组件,设置一些信息,58行到60行都是在创建一层一层的面板

至于前面为什么全有layer-这个前缀,那就要去看createLayer的方法

所以说,那一层一层,都是加载上去的,而不是一开始就在上面的

并且尺寸的话,在一开始就已经设定好了,是1334*750这么一个大小

 

这里准备跳的是LoginScene,判断一下是不是开始跳了

 

 

看见LoginScene里面,其实是在执行IScene的start方法,把自己启动

 

这里就有一系列的方法,至于什么方法,那肯定是创建这个窗体的方法啦,就不做过多的解释了

这个21行,设定的是你准备要加载的面板是什么面板,那我们可以验证一下

这里就看出来了LoginScene要加载出gui/Login/LoginView这个面板

又跑到了createUIView这个方法,然后。。又是一大堆判断逻辑,特别麻烦,不解释

 

这21行就说明了,即将要加载到层级上的面板就是它

想创建出一个面板,必须要知道Form.lua,也就是他们的父级

在Form.lua中,这个layout默认是空的,什么都不加载,毕竟是所有面板都是继承自Form

 

子类要创建的话,就得去这么初始化出来

这是商店

创建出一个商城的话,那么首先得仿照MainView.lua去写出它的格式

FormUI界面基类

它里面有特别重要的几个方法,也是非常绕的一个方法

为什么在每个控制器类有ctor和onCreate函数呢?

因为ctor是为了先让控制器知道有这么一个东西

好比这张图,这里这么多nil,毫无意义啊,

最主要的还是在onCreate中去获取它们相应的组件,例如按钮,图片等组件

想要啥获取啥

这个项目,目前最绕最绕的地方来了,那就是这个class

在里面创建了一个表 cls

它的__cname代表的是这个表的名字

它的__printName代表的是输出类名

cls.New = _new

这里太饶了,,,,,额,,无法进行解释。。。。。。。。。

吧啦吧啦吧啦,到了64行,它就会调用每一个调用它的ctor 【类似于构造函数】

所以,我们要打开一个面板的话,就必须写它的构造函数

然后到了functions.lua的class方法的82行,这个New就返回一个当前打开面板的对象进去,被cls.New给持有了。

到class方法的下面

创建了一个表mt,设置了一堆,,,元方法,呵呵,结果也是无法解释,也没弄清楚

反正加上__call,这个表就可以加上小括号

就是说 Network是个类型,如果想构造出它的对象的话,就直接加上小括号就行

定义一个构造函数ctor出来

还有个问题,就是那个onCreate方法是从哪里来的呢?

在这个createView中会加载资源,把当前调用的layout加载出来,

然后再实例化那个加载过后的资源,再在它身上添加LuaBehaviour组件,再绑定脚本它自身

最后调用onCreate方法。

因此,这个onCreate方法,就必须要写进去,否则就调用不到

onCreate方法就是来做初始化的,因为只要在这个面板初始化加载完毕之后,才能在onCreate中找到,反之,在ctor方法的时候,是一个东西都找不着的!

再所以说,ctor,为什么全是nil,到了onCreate中,才会来初始化

大概,这就是最基础的一个面板,这就是创建出一个空面板所需要写的一个lua

连入服务器

按下“登陆按钮”之后,会执行network.lua的login方法,也就是说

到了Network的login函数中,调用了本身的startTcpService方法

startTcpService这个方法当中,第78行和第79行,这两个是回调函数,被定义在了BaseSocketClient类中,

这个onReceiveMessage在parseData中被调用到了,parseData在receiveData方法中被调用了

 

 

receiveData在dispatchMessage中被调用了

最后dispatchMessage被声明周期函数Update调用了

反正就是这么一个特别复杂的调用过程。。。好几个方法一直调用

接下来就是这个self.onMessage方法,它是个回调函数

就到这个,它是处理网络通信的回调函数

把传过来的东西,存入了args表当中

那么这里就有个问题了,我们该去哪里找对应的处理事件的函数呢?

在Network.lua的上面,require了两个表,一个是【逻辑下载上传】表,另一个是【测试下载上传】表,这个测试表,它里面什么都没有,是个空表,返回的也是个空表,那么,我们得去看看逻辑表

这个表的最后,返回了一大堆东西,所以说,它处理函数都是在这里进行处理的。

我们模拟的是客户端与服务器收发数据,并且协议号是104,对应的就是这个函数

data是服务器发来的消息,经过了一个proto文件里面的一个类封装,就是

logic.GameOnlineLoginTCPServerProBuf,就成为了proto这个东西,在进行一个解析方法,Parse

就能获取到底下的相对应的属性/字段

这个原生protobuf文件,客户端这边是知道了这个文件的存在,但是服务器并不知道客户端发过来的是什么,所以,我们需要用的ServerFrameWork它自带的服务器,把这个Protobuf文件变成一个CS文件

这个就是变成的CS文件【部分】,我们在这里用到的是服务器端的,所以呢,只放上Server端这边的代码

所以说!onCmdLogin当中的logic.GameOnlineLoginTCPServerProtobuf();是这么来的。

 

登陆测试

 

回到Main.lua这个文件,它执行完之后,会跳场景,跳到LoginScene里面去,LoginScene有个LoginView,这个里面有登入游戏的按钮,它会向服务器发送连接请求,

这个连接请求,它所对应的是common.pb里面的东西。

也就是说是这里处理了用户名和密码

又回到了Network:login

 

然后又回到了它,到了第81行tcpService:connect也就是说,要回到客户端的TcpSocketClient的connect方法,

到这的时候,它要连接服务器了,那我们就要去ServerFrameWork那边去看,如何接受消息,如何解析消息和 如何处理消息

启动服务器后,让客户端直接入服务器的话,的确可以连入服务器了

暂时注释掉第262行代码

到上张图,输出结果是false,就说明连接服务器没有问题

 

262行是做了一个,只要点击“登陆游戏”按钮,立马发送登陆服务器请求,但是,这个发送的内容,服务器无法进行解析,看接下来的解释

logic_up.lua

这张图是将1000和50发往服务器的过程,具体是用到了protobuf和这个protobuf转换成C#的这个类,看第8行,这个就是GameOnlineLoginTCPClientProBuf类,由protobuf进行转化而来的一个类

 

看【上两张图】的第11行,sendProto方法,在Network.lua下,

10200是它的协议号,也就是说服务器那边也要有一个10200的这个协议号

 

再跳转到protocol.lua里面的client_send_proto_message,这里会解析一大堆东西

 

 

 

 

看见这个10201协议号了吧,它是服务器发往客户端的协议号,

也就是说Protocal.cs中,要手动添加两个这样的协议号,

Login是客户端发往服务器端的协议号10200,在cmdLogin中可以看出来,因为发送到服务器的协议号为10200,因此Login必须是10200,可为什么必须是Login呢?

原因是我们可以直接拿Login.cs拿来用,进行微调一下基本上就可以用了

这个10201是客户端的接受协议号,如果客户端想接受到消息的话,必须发送到10201这个协议号上,因此resLogin10201是这么来的

 

 

总而言之,一收一发,协议号必须是成对出现!

而且必须相互照应

 

到现在为止,客户端与服务器端能够进行交互了,就是能收发数据了。

现在有个需求,那就是客户端请求商城购买,服务器返回是否成功,所以说这是我们的最新需求。

 

那么首先就得知道10200和10201的方法是从哪里来的,之前也说到了,它是原生python文件转化而成的cs文件,所以说,必须找到原生python文件

这就是它的路径,network文件夹之下

这就是登录的原生python文件,10200是客户端发往服务器的这么一个协议号

10201是服务器这边的一个事件处理的协议号,

 

所以说上面说的东西,也就是我们需要写一个额外的两个方法,用于处理买和卖的操作

 

这两个方法就是买和卖的两个操作方法,现在将它转化成lua文件,这就即可了

 

这是出售的方法,它只会返回是否成功的这么一个bool值,而且是服务器端发往客户端的应答

 

 

 

这是购买的方法,它会发送两个值,一个是物品ID,另一个是数量,是客户端发往服务器的请求

 

 

【 完结 , 2018-9-5 16:14:40 】

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章