Unity3D開發入門教程(二)—— Lua入門

五邑隱俠,本名關健昌,12年遊戲生涯。 本教程以 Unity 3D + VS Code + C# + tolua 爲例。

 

如果你還沒有編程基礎,建議你先學習一些編程基礎。本文不是完全菜鳥教程,主要針對有其他語言經驗的開發者,如果想看菜鳥教程,建議看菜鳥教程的 Lua教程

先看一個簡單類的代碼

  1 ---@class BsnsPack @Base class of business pack
  2 local BsnsPack = {
  3     -- class fields
  4     maxSerialNo = 0,
  5 }
  6 
  7 BsnsPack.__index = BsnsPack
  8 
  9 --- new()
 10 ---@return BsnsPack
 11 function BsnsPack:new()
 12     local o = {}
 13 
 14     -- member fields
 15     o.serialNo = 0
 16     o.mod = 0
 17     o.cmd = 0
 18     o.payload = nil
 19     
 20     -- bind BsnsPack methods
 21     setmetatable(o, self)
 22     return o
 23 end
 24 
 25 --- set serial No.
 26 ---@param serialNo number
 27 function BsnsPack:setSerialNo(serialNo)
 28     self.serialNo = serialNo
 29 end
 30 
 31 --- get serial No.
 32 ---@return number
 33 function BsnsPack:getSerialNo()
 34     return self.serialNo
 35 end
 36 
 37 --- set mod
 38 ---@param mod number
 39 function BsnsPack:setMod(mod)
 40     self.mod = mod
 41 end
 42 
 43 --- get mod
 44 ---@return number
 45 function BsnsPack:getMod()
 46     return self.mod
 47 end
 48 
 49 --- set cmd
 50 ---@param cmd number
 51 function BsnsPack:setCmd(cmd)
 52     self.cmd = cmd
 53 end
 54 
 55 --- get cmd
 56 ---@return number
 57 function BsnsPack:getCmd()
 58     return self.cmd
 59 end
 60 
 61 --- set payload
 62 ---@param payload any
 63 function BsnsPack:setPayload(payload)
 64     self.payload = payload
 65 end
 66 
 67 --- get payload
 68 ---@return any
 69 function BsnsPack:getPayload()
 70     return self.payload
 71 end
 72 
 73 function BsnsPack.test()
 74     print(BsnsPack.serialNo)
 75 end
 76 
 77 --- testPrivate
 78 ---@param self BsnsPack
 79 local function testPrivate(self)
 80     print(self.serialNo)
 81 end
 82 
 83 ---@alias Request BsnsPack @Request is BsnsPack
 84 local Request = BsnsPack
 85 
 86 ---@class Response : BsnsPack @Response class
 87 local Response = {}
 88 setmetatable(Response, BsnsPack) -- bind super methods
 89 Response.__index = Response
 90 
 91 --- new()
 92 ---@return Response
 93 function Response:new()
 94     local o = BsnsPack:new()
 95 
 96     -- member fields
 97     o.code = 0
 98 
 99     setmetatable(o, nil) -- remove original metatable
100     setmetatable(o, self) -- bind Response methods
101     return o
102 end
103 
104 --- get result code
105 ---@return number
106 function Response:getCode()
107     return self.code
108 end
109 
110 local bsnsPack = {}
111 bsnsPack.BsnsPack = BsnsPack
112 bsnsPack.Request = Request
113 bsnsPack.Response = Response
114 
115 return bsnsPack

 

 

1、第1行:這是一行註釋

Lua的行註釋以兩個減號 -- 開頭,

class註解:---@class TYPE[: PARENT_TYPE {, PARENT_TYPE}]  [@comment]

這樣有利於 VS Code 代碼提示,例如將鼠標放到代碼中出現 BsnsPack 的地方上,會有小彈窗提示這個table的key,以及key的類型。

用得比較多的註解有 ---@class,---@return,---@param

 

2、第2行,定義一個叫 BsnsPack 的class(通過table模擬)。

Lua裏沒有類,Lua的結構化數據類型叫table,基於key-value結構。相對於其他語言的 map / dictionary。訪問一個table的key對應的值,可以用點 . 來訪問,也可以用中括號[]來訪問

Lua裏沒有單獨的數組結構,也是基於table,數組下標是key,數組元素是value,要注意的是,Lua裏數組下標是從1開始的。

創建table的最簡單方式就是花括號 {},創建一個空table。

Lua是腳本語言,變量不需要指定類型,只需要給它賦值。所以 BsnsPack = {maxSerialNo = 0,} 相當於定義了個變量 BsnsPack,它的值是一個table。

Lua的變量默認是全局的,加載到內存的Lua代碼都可以訪問這個變量。爲了避免命名衝突和意外修改,一般建議變量定義爲局部變量,限制它的作用域。在變量前面加個 local定義局部變量。

這裏定義的 BsnsPack ,作用域只在這個文件裏。其他文件不能訪問。也可以在函數、代碼塊裏用 local 定義局部變量。

 

3、第7行,給 BsnsPack一個叫 __index 的 key 賦值爲 BsnsPack 自己

Lua 的 table 裏,__index 是個特殊的key,這是 Lua實現類和繼承的關鍵。
Lua裏有個概念叫做元表。Lua定義了一些語言默認key,這些key一般跟 +、-、*、/等操作符、key的訪問、值更新關聯,這些key叫做元方法(Lua裏的函數/方法也可以作爲值),這些元方法合起來叫元表。

Lua裏設置元表的方法是代碼第 21 行的 function setmetatable(t, metatable),調用這個方法會讓參數 metatable 成爲 參數 t 的元表。

設置元表後,在代碼裏訪問參數 t 的一個key時會發生以下情況:

1)Lua先在 t 裏找,看有沒有這個key,如果有,就返回這個key的值。否則,執行2)

2)Lua看參數 metatable 有沒有 __index 這個key,如果有,就在 __index 對應的值裏查找(__index的值如果是表,在這個表裏查找;如果是函數,執行這個函數)

BsnsPack.__index = BsnsPack,如果其他的table設置 BsnsPack 爲元表,就可以訪問 BsnsPack 所有的key。 這些key的值如果是方法,就可以調用這些方法。
所以 BsnsPack 可以定義方法的模板,這就跟class類似。BsnsPack定義的數據key,相當於靜態字段class field。BsnsPack定義的方法key,可以是成員方法member method,也可以是class method,後面介紹
 

4、第11~23行,定義BsnsPack的一個叫new的key,它的值是一個方法

Lua裏一個代碼塊不用花括號{},通過end作爲結束標記。

定義方法用 function 開頭,用點.或者冒號:分割talbe名和方法名,

用冒號:定義的方法,會有個默認參數self,相當於調用者,如果外部用 BsnsPack:new() 這樣調用,則self == BsnsPack,如果用a:new()這樣調用,self==a。

用點.定義的方法,沒有默認參數,只能顯式寫參數self。

一般地,用冒號:定義構造對象的方法(例如第11行的 function BsnsPack:new()),以及成員方法member method(例如第23行的 function BsnsPack:setSerialNo(serialNo) )

1)當在其他地方調用 BsnsPack:new()時,方法BsnsPack:new()的定義裏的self是 BsnsPack,setmetatable(o, self)相當於設置對象 o 的類爲 BsnsPack

2)調用 o:setSerialNo(serialNo) 時,這裏的self是對象o,只改對象o的serialNo,其他對象的serialNo不受影響。

用點.定義靜態方法class method(例如第69行的 function BsnsPack.test() )

1)在其他地方通過 BsnsPack.test()調用

建議,方法定義的時候是冒號:,調用的時候用冒號:,定義的時候是點.,調用的時候也用點.

構造方法不一定叫new,只是爲了統一,建議都用new,與其他語言一致。

方法new裏面的 local o = {},定義的key都是成員字段member field(每次調用方法 BsnsPack:new() 都創建一個新的 table並對其字段初始化後返回)

Lua沒有私有的成員方法,可以定義文件的 local 方法,把對象作爲參數self顯式的傳進去(例如第79行 local function testPrivate(self)

 

5、第84行,給類BsnsPack定義一個別名 Request,其實就是把一個table賦值給另外一個變量

 

6、第87~102行,定義一個類Response,繼承基類BsnsPack

第87行,定義子類Response

第88行,設置元表,使得Response可以訪問BsnsPack所有的key(繼承BsnsPack所有的方法)

第89行,所有對象可以訪問Response所有的key(通過遞歸,包括BsnsPack所有的key)

第94行,通過調用BsnsPack構造方法初始化o,o是BsnsPack的對象,擁有基類所有的member field

第97行,定義Response相對於基類新增的字段code

第99、100行,修改o的元表爲Response,這樣o可以訪問Response所有的成員方法 member method(通過遞歸,包括BsnsPack所有的成員方法)

Response可以重寫BsnsPack的方法

1 function Response:getPayload()
2     return self.payload
3 end

由於Lua裏訪問key,先在table本身查找,如果沒有才在元表查找,所以會調用重寫的方法,實現多態。

 

7、第110~115行,導出本文件定義的類。

由於class的定義都是以 local 方式定義的局部變量,外部不能訪問,所以要把它當作文件的返回值 return

如果該文件只導出一個class,直接return這個局部變量就可以,(例如,如果只導出BsnsPack,return BsnsPack 就可以)

由於本文件需要導出3個class,這裏把他們放到一個table裏導出

其他文件需要訪問這個文件的class,需要調用require方法導入進來,該方法的參數是路徑(不含後綴.lua),返回值是這個lua文件的return值

1 local bsnsPack = require("Assets.Lua.bsns.bsns_pack")
2 local Request = bsnsPack.Request
3 local Response = bsnsPack.Response

 

8、如果想模塊化管理,把一個模塊所有的類通過一個文件暴露給其他模塊,可以在這個文件定義一個局部變量,把這個模塊所有的文件的類,都通過require導入進來,並賦值給跟類同名的key,最後返回這個局部變量

文件bsns.lua

 1 local bsns = {}
 2 
 3 local bsnsPack = require("Assets.Lua.bsns.bsns_pack")
 4 bsns.Request = bsnsPack.Request
 5 bsns.Response = bsnsPack.Response
 6 
 7 bsns.BsnsMod = require("Assets.Lua.bsns.bsns_mod")
 8 bsns.BsnsCenter = require("Assets.Lua.bsns.bsns_center")
 9 
10 return bsns

 

5、簡單語法介紹

1)Lua裏的語句不需要分號;結尾

2)Lua常用的基本數據類型有nil、boolean、number、string、function、table

3)獲取數組長度用 #數組變量,例如

1 local array = {"Lua", "Tutorial"}
2 local len = #array
3 print(len)

4)遍歷數組用 

1 for i, v in ipairs(table) do
2 
3 end

5)遍歷對象用

1 for k, v in pairs(obj) do
2 
3 end

6)其他語法請查閱菜鳥教程

 

6、Lua代碼規範可參考 https://lua.ren/topic/172/

 

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