誰是大魔王 - 框架分析

在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 】

 

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