一.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"))