一.lua安裝和程式設計環境搭建
lua語言可以在官網:http://luadist.org/下載安裝包安裝,程式設計IDE之前學習使用的是SciTE(https://www.cnblogs.com/movin2333/p/14348703.html),這個IDE安裝時會一併安裝lua,當然,vscode、idea、luaStudio等IDE也可以編寫lua,這次使用SublimeText編寫lua,SublimeText的內地官網:http://www.sublimetext.cn/。
二.lua語法:變數
1.簡介:lua中的變數類似於c#種var,使用時不需要宣告變數型別,賦值時確定變數型別。沒有宣告的變數可以直接使用,預設值為nil。
2.變數分類
1)簡單變數型別 nil number string boolean
2)複雜資料型別 function table userdata thread
3.簡單變數型別
1)nil:空值
2)number:數值,所有的數值型別(不管是整型還是浮點型)都是number型別
3)string:字串,宣告字串使用單引號或者雙引號都可以
4)boolean:布林值
5)和型別相關的函式:type(xxx)--獲取變數xxx的型別
4.string型別的相關函式
1)使用#獲取字串長度:
str = "雙引號字串" str2 = '單引號字串' --獲取字串的長度 print(#str)
注意:一個漢字佔用3個位元組(utf-8編碼)
2)字串多行列印
--字串多行列印 --lua中支援轉義字元 print("123\n123") --定義多行字串 s = [[你好 我的寶貝 歡迎來到 lua]] print(s)
3)字串拼接
str = "雙引號字串" str2 = '單引號字串' --字串拼接,通過..拼接 print(str..str2) s1 = "123" s2 = 1232 s3 = true --print(s1..s2..s3) 報錯,boolean值不能拼接 print(s1..s2)
注意:boolean值拼接時報錯。
--字串拼接,通過format函式拼接 print(string.format("你好,我今年%d歲,尚未婚配",18)) --%d:配對數字 --%a:配對字母 --%s:配對字元
4)其他型別轉字串
--非字串型別轉字串 print(tostring(true)) --使用tostring函式,直接列印預設也會呼叫tostring函式(和C#其他型別轉字串方式是一致的) print(true)
5)字串的其他函式
--其他字串函式 s = "addDEIN" --小寫轉大寫 print(string.upper(s)) --大寫轉小寫 print(string.lower(s)) --反轉字串 print(string.reverse(s)) --字串索引查詢 print(string.find(s,"ddD")) --擷取字串 print(string.sub(s,3,5)) --字串重複 print(string.rep(s,3)) --字串修改 print(string.gsub(s,"dd","**")) --字元轉ASCII碼 a = string.byte("lua",2) print(a) --ASCII碼轉字元 print(string.char(a))
注意:lua中有多返回值,如查詢函式中返回值為“ddD”字串在s中從第2個字元到第4個字元,替換字串中第二個返回值1代表替換了1次。lua中字串索引是從1開始的,而不是0(和C#不同)。
三.lua語法--運算子
1.算數運算子:支援+、-、*、/、%,不支援自增++自減--複合運算子+=、-=、*=、/=、%=。注意:在lua中+號沒有拼接字串功能,使用..進行字串拼接,將數字字串和數字使用+號連線時系統會自動將字串轉為number型別進行加法運算。
lua中支援c#中沒有的冪運算,使用^符號(如3的4次冪記為3^4),但是這個符號在c#中是冪運算子。
2.條件運算子:支援>、<、>=、<=、==、~=(不等於),注意:不等於符號和c#不同。
3.邏輯運算子:and(與)、or(或)、not(非),注意:lua中邏輯運算子和c#符號不同,但是功能幾乎相同,都支援短路。
4.lua中不支援位運算和三目運算
四.lua語法--條件分支語句
1.if語句
--條件語句 a = 2 --單分支 if a < 3 then print("a<3") end --雙分支 if a < 4 then print("a<4") else print("a>=4") end --多分支 if a < 5 then print("a<5") elseif a == 5 then print("a=5") else print("a>5") end
2.lua中沒有switch語法和三目運算子。
五.lua語法--迴圈語句
--迴圈語句 --while迴圈 num = 0 while num < 5 do print(num) num = num + 1 end print("***********************") --do while迴圈 --注意:until後面是結束迴圈的條件,而c#中do while語句while後是繼續迴圈的條件,這一點不同 num = 0 repeat print(num) num = num + 1 until num > 5 print("************************") --for迴圈 --預設自增1,i從1自增到大於5結束 for i = 1,5 do print(i) end --指定i自增2,i從1自增到大於5結束 for i = 1,5,2 do print(i) end
六.lua語法--函式
1.無參無返回值
--函式 --無參無返回值 --函式定義方式一 function F1() print("F1函式") end --函式定義方式二,類似C#中的委託 F2 = function() print("F2函式") end F1() F2()
注意:lua中程式碼從上向下執行,如果函式呼叫時還未定義,而呼叫完函式後定義函式,會報錯。這一點和C#不同,因為C#會預先編譯,因此函式先呼叫再定義是沒有問題的,但是lua不會編譯,不能先呼叫再定義。
2.有引數,不用指定引數型別
--函式 --有引數 F1 = function(a) print(a) end F1(345)
注意:如果函式定義了引數,但是不給定引數或者少給了多給了引數,都不會報錯,lua會自動補空nil或者丟棄
3.有返回值
--函式 --有返回值 --函式可以任意返回多個返回值,使用逗號隔開 F1 = function() return 1,2,3,"true" end print(F1()) --使用多個變數接多返回值,變數個數和返回值個數不統一時也會補空或者丟棄返回值 a,b,c,d = F1() print(a..b..c..d)
4.lua中函式的型別是複雜資料型別function。lua不支援函式的過載,預設呼叫最後宣告的函式(宣告過載函式相當於改變了函式賦值)
5.變長引數
--函式 --變長引數 F1 = function(...) arg = {...} for i = 1,#arg do print(arg[i]) end end F1(1,2,3,4,5,6,7,1,2,12,1)
6.函式巢狀
--函式 --函式巢狀 F1 = function() return function() print(1234) end end --返回函式時可以使用一個變數來接收返回函式再呼叫 F2 = F1() F2() --也可以直接使用兩個括號呼叫,第一個括號呼叫F1,第二個括號呼叫F1()返回的函式 F1()() --閉包 F3 = function(x) --x作為引數本來是一個臨時變數,但是在返回函式中被呼叫,改變了x的生命週期,稱為閉包 return function(y) return x + y end end print(F3(2)(3))
七.lua語法--表
1.概述:所有的複雜型別都是基於table(表)實現的。
2.陣列及陣列遍歷
--陣列 --定義陣列,任意型別的資料都可以放到同一個表中 a = {1,2,3,"字串",nil,true,nil} --lua中陣列索引從1開始,所以索引0值為nil print(a[0]) print(a[1]) print(a[4]) --#是通用的獲取長度的關鍵字,注意:計算長度時,從第一個nil開始的所有資料會被忽略,因此這裡計算出來的陣列長度為4,取陣列長度時true和nil都沒有計算在內 print(#a) --遍歷 --通過#遍歷陣列是一種不可靠的遍歷,這裡就沒有遍歷到true for i = 1,#a do print(a[i]) end
3.二維陣列及其遍歷
--二維陣列 a = {{1,2,3},{4,5,6}} --取值 print(a[1][2]) --遍歷 for i = 1,#a do for j = 1,#a[i] do print(a[i][j]) end end
4.自定義索引
--自定義索引 a = {[0] = 1,2,3,[-1] = 4,5} --取值 print(a[0]) print(a[-1]) --獲取長度時實際上從下標1開始獲取元素個數,所以這裡得到的長度是3 print(#a) --自定義索引跳過其中某些索引出現的異常情況 m = {[1] = 1,[2] = 2,[4] = 4,[6] = 6} print(#m) --實際有4個元素,索引值3和5對應的值是nil,但是實際得到的元素個數是6 n = {[1] = 1,[2] = 2,[5] = 5,[6] = 6,[9] = 9,[10] = 10} print(#n) --實際有6個元素,有兩處跳過了連續兩個索引3、4和7、8,但是實際得到的元素個數是2
5.迭代器遍歷
--迭代器遍歷 --#得到的表的長度不準確,使用#遍歷不推薦使用,一般使用迭代器遍歷 a = {[0] = 1,2,[-1] = 3,4,5} --ipairs遍歷,仍然是從1開始遍歷,也只能找到連續索引值,索引值斷開會出現遍歷不準確的問題 for i,k in ipairs(a) do print(i..":"..k) end print("******************") --pairs遍歷,能遍歷出所有值,推薦使用的遍歷方法 for i,v in pairs(a) do print(i..":"..v) end print("******************") --pairs遍歷,只遍歷鍵的方法 for i in pairs(a) do print(i) end
6.使用table實現字典功能
--字典 --使用自定義索引的方式就可以自定義表 a = {["name"] = "movin",["age"] = 14,["sex"] = true,["1"] = 2} --使用類似於C#索引器的方式訪問字典 print(a["name"]) print(a["age"]) print(a["sex"]) print(a["1"]) print("******************") --使用類似於成員變數的方式訪問 print(a.name) print(a.age) --print(a.1)會報錯,使用這種方式訪問時索引不能是數字 print("******************") --修改和新增,直接賦值即可,有這個索引就是修改,沒有這個索引就是新增 a["sex"] = false print(a["sex"]) print(a.sex) a["mm"] = 14 print(a["mm"]) print(a.mm) print("******************") --刪除,置空即可 a["age"] = nil print(a["age"]) print("******************") for k,v in pairs(a) do print(k,v) end
7.利用表模仿類的實現
1)基本宣告
--類 --lua中預設沒有物件導向,但是我們可以使用表來表現類 Student = { --宣告變數age age = 1, --宣告變數sex sex = false, --成員函式Up Up = function() print("我成長了") end, --成員函式Study Study = function() print("我在學習") end, } --lua中類的屬性和方法更類似於靜態的屬性和方法 print(Student.age) Student.Up() Student.Study()
2)表內的函式使用表內的其他元素
--類 Student = { age = 1, sex = false, Up = function(s) --在表中使用表內的變數和方法,不能直接呼叫如print(age),這樣訪問不到表中的變數age --可以通過表名點出呼叫的變數 print(Student.age) --可以將自己作為引數傳遞,然後呼叫 print(s.name) end, Study = function() print("我在學習") end, } --在表的外部為表新增屬性或方法 Student.name = "movin" print(Student.name) --表的外部呼叫函式Up,將表自身作為引數傳遞 Student.Up(Student) --使用冒號可以簡寫,冒號可以將自身作為第一個引數傳遞。 Student:Up() --注意:定義函式時也可以使用冒號進行定義,使用self作為自身 function Student:Up2() print(self.sex) end Student:Up2()
8.表的一些公共操作
t1 = {{age = 1,name = "123"},{age = 2,name = "345"}} t2 = {name = "movin",sex = true} --表的插入 print(#t1,#t2) table.insert(t1,t2) --將t2插入t1最後 print(#t1,#t2) --刪除指定元素 --預設remove函式移除表中最後一個值 print(t1[1]) print(t1[2]) print(t1[3]) table.remove(t1) print(t1[1]) print(t1[2]) print(t1[3]) --指定移除表中某個元素 table.remove(t1,1) print(t1[1]) print(t1[2]) print(t1[3]) --表的排序 t3 = {1,3,8,-1,0,5} --預設升序排序 table.sort(t3) for _,v in pairs(t3) do print(v) end --自定義排序規則,類似於C#中list的sort函式的使用,這裡實現降序排序 table.sort(t3,function(a,b) if a>b then return true --這個返回值可以理解為a和b是否交換位置 end end) for _,v in pairs(t3) do print(v) end --表的拼接 t4 = {"123","456","789","111"} --將表中元素使用第二個引數的符號連線成一個字串 print(table.concat(t4,","))
八.lua語法--多lua指令碼執行
1.全域性變數和區域性變數
不適用local宣告的變數稱為全域性變數,在整個指令碼中都存在(無論在哪裡宣告);使用local宣告的變數是區域性變數,只在當前語句塊(宣告變數的語句塊)中起作用。
--區域性變數和全域性變數 --全域性變數 if true then a = "全域性變數" end print(a) --區域性變數 if true then local b = "區域性變數" end print(b)
2.多指令碼執行
如果想在一個lua指令碼中執行另一個指令碼或者呼叫另一個指令碼中定義的函式等,需要使用require關鍵字引入其他指令碼
--被引用的指令碼 A = "全域性變數A" local B = "區域性變數B"
--引用其他指令碼 --同資料夾下直接引用指令碼檔名即可,注意:同一個指令碼require引入兩次及以上,並不會執行兩次,也就是說require已經引用的指令碼是無效的 require("BeenRequired") print(A) print(B) --B是區域性變數,這個指令碼中看不到 --使用load方法獲取一個boolean值,代表指令碼是否執行過 print(package.loaded["BeenRequired"]) --解除安裝指令碼,直接把loaded的值置為nil即可 package.loaded["BeenRequired"] = nil print(package.loaded["BeenRequired"])
3.大G表
大G表是一個總表,將我們宣告的所有全域性的變數都儲存在裡面(包括大G表自身)。大G表的表名是_G。使用local宣告的本地變數沒有儲存在大G表中。只要執行過的全域性變數都儲存在大G表中,因此即使指令碼被解除安裝了,仍然可以訪問指令碼中宣告過的全域性變數。
注意:指令碼也可以當成是一個函式,在指令碼最後可以返回外部希望獲取的內容,在外部使用require引入後可以接收這個返回值。
九.lua語法--特殊用法
1.多變數賦值
和函式的多返回值類似,多變數賦值會自動補空或者丟棄。
--多變數賦值 a,b,c = 1,2 d,e,f = 3,4,5,6 print(a) print(b) print(c) print(d) print(e) print(f)
2.函式的多返回值:函式部分已經有介紹
3.邏輯與和邏輯或
and和or不僅可以連線boolean,任何東西都可以連線,當連線的不是boolean值時,將這些值當成boolean值處理(在lua中,非boolean值當作boolean值處理的規則是nil當作false,其他都當作true處理)。
注意:lua中不支援三目運算子,但是可以使用and和or模擬三目運算子。
--使用and和or模擬三目運算子的實現 local x = 3 local y = 2 --執行邏輯:利用and和or的短路運算特性 --如果x>y運算的結果為true,則根據and的短路規則會接著判斷x的值是真還是假,顯然x不為nil當作true處理,因此(x>y) and x的結果是true,根據or的短路特性,不會繼續判斷y,因此最後返回x --如果x>y運算的結果為false,則根據and的短路特性,不會判斷x,(x>y) and x的結果為false,根據or的短路特性,會繼續判斷y,顯然y不為nil當作true處理,因此false or y的最終返回結果為y local result = (x>y) and x or y print(result)
十.lua語法--協程
1.協程的建立
--建立協程 --首先宣告一個函式作為協程函式 function fun() print(123) end --建立方法一:使用coroutine表中的create函式建立協程 co = coroutine.create(fun) print(co) print(type(co)) --建立方法二:使用coroutine表中的wrap函式建立協程 co2 = coroutine.wrap(fun) print(co2) print(type(co2)) --兩種方法建立的協程返回值不同,開啟方式也不同 print("*********************") --通過create函式建立的協程的執行方法 coroutine.resume(co) --通過wrap方法建立的協程的執行方法 co2() --wrap方法建立協程返回值為函式型別,直接呼叫函式即可
2.協程的掛起
--掛起協程 function fun() local i = 1 while true do print(i) i = i + 1 --使用coroutine表中的yield函式掛起協程,create函式建立的協程yield的返回值第一個為啟動協程是否成功的boolean值,yield函式傳入的引數是協程這一次掛起的其他返回值,wrap方式建立的協程沒有第一個預設的boolean返回值 coroutine.yield() end end co = coroutine.create(fun) --啟動協程 coroutine.resume(co) --列印1 --再次啟動協程,繼續執行這個協程 coroutine.resume(co) --列印2
3.協程的執行狀態
--協程的狀態 function fun() local i = 1 while i<2 do print(i) i = i + 1 print(coroutine.status(co)) --running執行狀態 --獲取正在執行的協程的執行緒號 print(coroutine.running()) coroutine.yield() end end co = coroutine.create(fun) --使用coroutine表中的status方法獲取協程的執行狀態 print(coroutine.status(co)) --suspended暫停狀態 coroutine.resume(co) print(coroutine.status(co)) coroutine.resume(co) print(coroutine.status(co)) --dead協程結束狀態
十一.lua語法--元表
1.元表的概念和設定
1)任何表變數都可以作為另一個表變數的元表
2)任何表變數都可以由自己的元表(可以理解為父表)
3)當表進行一些特定操作時,會執行元表中的內容
--元表 meta = {} myTable = {} --設定元表 setmetatable(myTable,meta) --將meta表設定為myTable的元表
2.特定操作
1)__tostring
--__tostring meta = { __tostring = function(t) return t.name end, } myTable = { name = "movinToString" } setmetatable(myTable,meta) --當子表被當成字串使用時,會呼叫_tostring方法,這個方法可以設定一個引數,系統會預設將子表自身傳入 print(myTable)
2)__call
--__call meta = { __call = function(c) print("movinCall",c.name) end } myTable = { name = "movin" } setmetatable(myTable,meta) --當子表被當作函式使用時,會呼叫元表中的__call函式,同樣的,__call的第一個引數預設為子表自身,不需要傳遞 myTable()
3)運算子過載
--運算子過載 meta = { __add = function(t1,t2) return t1.number + t2.number end } myTable = { number = 1 } myTable2 = { number = 2 } setmetatable(myTable,meta) setmetatable(myTable2,meta) --當兩個子表進行運算子操作時,如果這兩個子表有同一個元表且元表中實現了響應的運算子過載,會呼叫這個運算子過載,預設將這兩個子表作為兩個引數傳遞(運算子過載一定是兩個引數) print(myTable2+myTable) --其他運算子過載 --__sub(減),__mul(乘),__div(除),__mod(取餘),__pow(冪),__eq(相等),__lt(小於),__le(小於等於),__concat(連線符號..) --注意:沒有大於和大於等於的運算子過載
4)__index和__newIndex
--__index和__newindex meta = { age = 2 } meta.__index = meta myTable = {} setmetatable(myTable,meta) --當呼叫表中沒有的元素時,會自動在元表的__index指向的表中尋找 print(myTable.age) meta.__newindex = meta --當設定表中沒有的屬性時,會自動設定屬性到元表的__newindex指向的表中 myTable.name = "movin" print(myTable.name) print("****************") for k,v in pairs(myTable) do print(k,v) end print("***************") for k,v in pairs(meta) do print(k,v) end
注意:為元表中的__index和__newindex賦值時最好在表的外部設定,因為__index在表的內部設定指向元表自身時這個設定是無效的。
3.元表的一些方法
--元表的相關方法 meta = {} meta.age = 1 meta.__index = meta meta.__newindex = meta mytable = {} setmetatable(mytable,meta) --使用getmetatable獲取表的元表 print(getmetatable(mytable)) --使用rawget方法在獲取變數時忽略元表(不去__index對應的表中找,相當於忽略__index這個變數) print(rawget(mytable,"age")) --使用rawset方法在設定變數時忽略元表(不去__newindex對應的表中設定,相當於忽略__newindex這個變數) rawset(mytable,"age",2) print(mytable.age) print(meta.age)
十二.lua實現物件導向程式設計
1.封裝
--封裝 --嘗試封裝萬物之父object Object = {} Object.id = 1 --new方法,新建一個物件 function Object:new() --宣告一個空表 local object = {} --設定元表 setmetatable(object,self) --元表的__index指向自身,呼叫這個表都會呼叫元表的方法 self.__index = self return object end local obj = Object:new() print(obj) print(obj.id)
2.繼承
--繼承 Object = {} function Object:new() local object = {} setmetatable(object,self) self.__index = self return object end --提供一個繼承的方法 function Object:subClass(className) --使用_G總表,可以建立指定名稱的表 _G[className] = {} --使用元表模擬繼承 --取出新建立的表 local obj = _G[className] --設定元表 setmetatable(obj,self) self.__index = self end --建立新表Person Object:subClass("Person")
3.多型
--繼承 Object = {} function Object:new() local object = {} setmetatable(object,self) self.__index = self return object end function Object:subClass(className) _G[className] = {} local obj = _G[className] setmetatable(obj,self) self.__index = self --建立base屬性,實現繼承後子類呼叫父類方法 self.base = self end --Object中提供move方法 function Object:Move() print("move") end --建立新表Person Object:subClass("Person") --重寫父類Object的Move方法 function Person:Move() --保留父類邏輯,必須使用點呼叫,不能使用冒號呼叫,然後將呼叫者自身作為引數傳入 self.base.Move(self) print("PersonMove") end --建立物件 p = Person:new() --呼叫Move方法 p:Move()
十三.lua自帶庫
1.時間相關
--常用自帶庫 --時間相關 --系統時間,毫秒值 print(os.time()) --自己傳入引數得到時間 print(os.time({year = 2021,month = 4,day = 1})) --得到現在的時刻,將現在的年月日時分秒等儲存在一個表中 local nowtime = os.date("*t") for k,v in pairs(nowtime) do print(k,v) end
2.數學運算
--常用自帶庫 --數學運算 --絕對值 print(math.abs(-11)) --弧度轉角度 print(math.deg(math.pi)) --三角函式,引數是弧度值 print(math.sin(math.pi)) --向下或向上取整 print(math.floor(4.3)) print(math.ceil(2.3)) --最值 print(math.max(2,3)) print(math.min(4,2)) --將數字的小數部分和整數部分分離 print(math.modf(3.4)) --冪運算 print(math.pow(3,2)) --隨機數,必須先設定隨機數種子 math.randomseed(os.time()) print(math.random(100)) print(math.random(100)) --開方 print(math.sqrt(121))
3.路徑
--常用自帶庫 --路徑 --lua指令碼載入路徑 print(package.path)
PS:可以自己檢視_G表中的內容,自帶庫都儲存在_G表中,可以遍歷響應的表格自學。
十四.lua的垃圾回收機制
lua中存在自動垃圾回收機制,但是在unity中使用lua時儘量手動回收垃圾,減少效能開銷。
--垃圾回收 --得到當前lua佔用記憶體數,單位為千位元組 print(collectgarbage("count")) --進行垃圾回收的命令 collectgarbage("collect") --再次檢視lua佔用記憶體數,可以看到佔用變小print(collectgarbage("count")) print(collectgarbage("count"))