Siki_Unity_3-8_Lua编程(未完)

Posted on 2018-04-16 18:51  FudgeBear  阅读(287)  评论(0编辑  收藏  举报

Unity 3-8 Lua编程

任务1&2&3:前言

课程内容:
  Lua从入门到掌握
  为之后的xLua和其他热更新方案打下基础

任务4:Lua简介

Lua是轻量小巧的脚本语言--无需编译,用标准c语言编写,开源
  目的是为了嵌入应用程序中,为应用程序提供灵活的扩展和定制功能
  可以很方便地与其他程序进行集成(如C++, C#, Java等)
    -- 因为Lua无需编译,对于某些功能,如果不方便重新编写,可以使用Lua进行功能扩展

Lua应用场景:
  游戏开发
  独立应用脚本
  Web应用脚本
  扩展和数据库插件
  安全系统(如入侵检测系统)

Lua和c#的区别:
  Lua可以在几乎所有操作系统/平台运行
    可以很方便的更新代码,代码更新后无需重新安装 -- 后续的热更新方案
    不需要c#那样重新编译打包
  c#只能在特定的操作系统中进行编译成dll文件,然后打包进安装包在其他平台运行
    在移动平台上不能更新替换已有的dll文件,除非重新下载安装包

学习资料:
  Programming in Lua
  www.runoob.com/lua/lua-tutorial.html -- 菜鸟教程
  www.luaer.cn -- Lua中国开发者
  官网:www.lua.org

任务5&6:Lua的安装(SciTE && LuaDist) 和第一个Lua程序

http://www.runoob.com/lua/lua-environment.html
SciTE -- https://github.com/rjpcomputing/luaforwindows/releases,带IDE
LuaDist -- 无IDE

任务7:注释

-- 单行注释

--[[ 多行注释 ]]--

-- 单行注释
--[[
    多行注释
    小技巧:在多行注释开头再加一个-,会使多行注释代码块生效
    原因是第一行和最后一行都变成了单行注释
]]--

---[[
    print("Execute")
--]]

任务8:标识符命名规则和关键字

标识符:字母或_开头,后跟0或多个字母或_或数字

不推荐"_跟大写字母作为标识符",比如"_NAME",这是lua的保留字命名规则(Lua内部使用的变量名)

标识符区分大小写

任务9、21:全局变量和局部变量

默认情况下的变量都是全局的
全局变量不需要声明,给一个变量赋值后就相当于创建了这个全局变量,比如 a = 10
  访问一个没有赋值的全局变量,此时不会报错,但是变量不存在,比如 print(b) 的结果为 nil
  而如果需要删除一个全局变量,赋值为nil即可,如 a = nil,删除后则a不存在

局部变量的声明:local b = 10
  局部变量的生命周期为:从声明开始,到所在语句块结束
  如果是语句块是在最外围,则整个lua脚本文件的语句执行完后才被销毁

function f () 
    a = 1
    local b = 2
    -- a为全局变量,b为局部变量
end

f()      -- 注意,如果这边没有调用函数f(),则上面函数定义中的变量是没有被创建的

print(a)    -- 1
print(b)    -- nil

函数也是可以通过local来修饰为局部函数的

局部变量和全局变量的重名规则:
  有局部变量时局部变量优先

一般而言,访问局部变量的速度会比访问全局变量快,而且局部变量在生命周期结束后会被销毁释放内存空间

任务10~20:数据类型的简介

基本类型:

nil -- 只有一个值nil,表示无效值,条件表达式内相当于false

boolean -- true / false

number -- 双精度实浮点数

string -- "string" 或 'string'

function -- 函数

userdata -- 任意存储在变量中的C数据结构

thread -- 独立执行的线路,协程(非线程)

table -- 表(关联数组),索引为数字或字符串,{}表示一个空表

使用type可以获取变量或者值的类型,如

print(type("hellolua"))    -- string
print(type(10))    number
print(type(1.1)    number
print(type(print))    function
print(type(type))    function
print(type(nil))    nil
print(type(x))    nil

nil:nil类型只有一个值nil,表示无效值,比如当变量未被赋值时,即为nil值

给变量赋值nil后,表示将该变量销毁,释放内存

boolean:nil值视为为false,其他变量值都视为true,比如if(10)是true

string
  表示方法:"" 或 ‘’ 或 [[ ]] (表示多行)
  "string"
  'string'
  [[str
    in g ]]

  字符串的拼接,不能用 + 号,只能用 .. ,如 print("a".."b") --> "ab"     ;     print(2..6) --> 26
  print("2" + "6") --> 8 会自动转换为number类型
  #表示字符串长度(字节数),如  s = "ab呀"; print(#s) --> 4

table:

构造方法及使用

table1 = { }  -- 构造表达式,得到一个空表
print(table1)        -- -> table:00A89600
print(table1.key1)    -- -> nil

table2 = {key1 = 100, key2 = "aaa"}
print(table2.key1)        -- ->100
print(table2["key1"])    -- ->100

table3 = {"111", "222", "hello"}
print(table[2])       -- -> 222 -- Lua中没有数组,用table表示数组。
-- 在Lua中,索引一般是从1开始算的,不是0

-- table的索引
for key,val in pairs(table3) do
    print(key..":"..val);
end
-- -> 1:111 2:222 3:hello 

table的大小不是固定的,可以很灵活的进行增删查改

-- 对于字典形式
tbl = {key1 = "aaa"}
-- 修改
tbl.key1 = 123    -- 类型可以不相同

-- 增加
tbl["key2"] = "value"
tbl.key222 = "value"
tlb[10] = 1000

-- 删除
tbl.key2 = nil    -- 此时进行遍历就不会遍历到key2了

-- 对于数组形式(实质上还是字典,只不过把自动索引作为了key)
tbl = {"1", "a"}
tbl[2] = "b"        -- 修改
tbl[3] = "c"        -- 增加
tbl[2] = nil         -- 删除 -- 注意,这里的数组本质上还是键值对的形式
-- 比如这边把索引为2的删除后,遍历可得到 --> 1:1  3:c (索引不是连续的)
-- 也同样可以添加键值对
tbl["key5"] = "value5"

-- 删除整个表
tbl = nil

function:函数

不需定义返回值类型,但是可以返回任意类型返回值

函数体为 从 function name(argument) 到 end 之间

function f1(n)
    print(n)
    return n
end

可作为一个类型作赋值操作

  f2 = f1 -- 此时f2也为一个function类,函数内容和f1一样

可以作为参数传递

function func1(table fun)
    for key, value in pairs(table)
        fun(key, value)
    end
end

function func2(key, value)
    print(key..":"..value)
end

func1(table, func2) -- 遍历table并输出key,value

匿名函数:只能使用一次,不用定义函数名

-- 以上一段代码为例,调用的时候
func1(table, 
    function (key, value)
        print(key..":"..value)
    end
)
-- 遍历table输出键值对

thread:协同程序

不是线程

拥有自己独立的栈、局部变量和指令指针

可以共享全局变量和其他大部分东西

任意时刻,协程只能运行一个,只有被挂起 suspend 的时候协程才会暂停

userdata:自定义类型

用于表示由应用程序或C/C++创建的类型,可以将C/C++任意数据类型 (通常为struct和指针) 存储到lua变量中进行调用

任务22:多变量赋值

a, b, c = 1, 2, "c"

a, b = b, a  -- 并不是简单的a = b; b = a;,而是很方便的交换了a和b的值 -- 结果为a = 2, b = 1
  因为lua是将右边的值全部计算完后,再统一赋值的

a, b = 10, 20, 30 -- 30会被忽略

a, b, c = 10, 20   -- 未赋值的变量会初始化为默认值,即为nil

函数function也是可以直接返回多个值的
  function test()
    return 1,2
  end
  a, b = test()

任务23、24、26:流程控制

while循环

while (condition) do
  statements
end

condition的括号可加可不加

for循环

数值for循环

for var = start, end, step do
  statements
end

var从start到end (inclusive),每次增加step
step未指定时默认为1

泛型for循环

for key, value in pairs (table) do
  statements
end

repeat unitl循环(和do while流程相同,逻辑相反):

repeat
  statements
until (condition)

直到满足condition后,就跳出循环(而不是do while的继续循环)

if判断

if (condition) then
  statements
end

 

-- 注意,数值0表示true,nil值表示false

if (condition) then
  statements
elseif (condition) then
  statements
else
  statements
end

任务27、28:function详解

[local] function name(arg1, arg2, ..., argn)
  statements
  [return value1, value2, ... valuen]
end

1. function本质为变量类型,因此可以作为数据直接赋值,也可以作为参数传递

2. 可直接返回多个值,可用多变量赋值的方式接收返回值(如果接收返回值的变量数不对,则遵循多变量赋值的规则)
  如:func返回值为4个,调用处代码 a, b = func(),则后两个返回值就没有获取到了

3. 可变参数,传递的参数个数可以是多个

-- 定义时 
function func(...)
    print(arg)  
    -- 这边会将多个参数封装成一个table类型,以便使用
    -- table中保存了所有传入的参数,最后一位保存了参数的总个数
    local arg = {...}
    -- 此时args里保存的为所有传入的参数,不带最后一个总个数了
end

-- 调用时
func(a)
func(a, b)

任务29:运算符

数学运算符:

^  -- 求幂

% -- 求余(可对小数进行求余)

关系运算符:

~=  -- 不等

逻辑运算符:

and  -- 与

or  -- 或

not  -- 非

其他运算符:

..  -- 字符串连接

#  -- 取得字符串长度

任务32、33:字符串常见操作

newstring = string.upper(oldstring)
newstring  = string.lower(oldstring)
newstring  = string.gsub(oldstring, "o", "r", num)  -- 将str1中的 o 替换成 r, num 表示替换的最大次数,可以不传
index = string.find(str, "substr", num)  -- 从num开始,搜索str是否有子字符串substr,返回子字符串开始的索引
newstring = string.reverse(oldstring)
str = string.format("%02d", month)
str = string.char(97, 98, 99)  -- 对应ASCII码。abc
integer = string.byte("ABCD", num)  -- 返回num个字符对应的值。不写num时默认为第一个字符
newstring  = string.rep(oldstring, count)  -- oldstring重复count次
string.gmatch()/ string.match() -- 正则表达式

任务35:多维数组

array = { {"a", "b"}, { "1", "2"}, {"xx", "yy"} }
print(array[1][1])  -- 输出a

任务36、37:迭代器函数

pairs 遍历 table,表中所有的key和value
ipairs 按索引遍历,从1开始,递增遍历,遇到nil值就停止

自定义迭代函数:

.......

......

......

......

......

.......

......

......

......

......

.......

......

......

......

......

.......

......

......

......

......

任务50~54:协同程序

Lua中协程和线程的区别有:线程是由CPU进行任务调度的,可以视为同一时间可以同时运行多个线程
  而协程是在同一个线程中的,同一时刻只能在运行一个协程,需要代码控制多协程的协作,对协程进行挂起或继续等操作

基本用法:

-- 定义方法1
co = coroutine.create (
    function (a,b)    -- 这里是一个匿名函数
        print(a)
    end
)

-- 对应的调用
coroutine.resume(co, 20, 30)

-- 定义方法2
co = coroutine.wrap (
    function (a,b)    -- 区别只有调用的函数不同,这边是wrap
        print(a)
    end
)
-- wrap对应的调用,直接启动即可,不需通过resume()
co(20,30)

-- 挂起:在上面定义的匿名函数中,使用coroutine.yield()语句即可挂起协程
co = coroutine.wrap (
    function (a,b)    -- 区别只有调用的函数不同,这边是wrap
        print(a)
        coroutine.yield()
        print(b)
    end
)

-- 恢复运行协程 -- 注意:当一个协程执行完毕return后,再进行resume并传参进去,并不能再次开始执行协程
coroutine.resume(co)

-- 返回值
-- 假设上面的匿名函数的最后一句为
return a+b, a-b
-- 调用/继续的时候接收返回值(如果没有执行到return语句,只会有第一个true/false的返回值表示该次调用/继续是否成功)
res1, res2, res3 = coroutine.resume(co, 20,30)
print(res1, res2, res3)
-- 结果为true, 50, -10,其中第一个返回值表示resume成功与否
-- 如果想在挂起的时候有返回值的话,在yield中传递参数
coroutine.yield(a*b)
-- 调用/继续resume的时候会得到额外的返回值
res1, res2 = coroutine.resume(co, 20, 30)
-- res1 = true, res2 = 600

其他函数:

coroutine.status(co)
  有三个值:
    running:协程正在执行过程中
    suspended:协程未开始或暂停挂起后
    dead: 协程执行完毕后

coroutine.running()
  当前正在运行的协程,返回值为协程的地址
  如果当前没有正在运行的协程(包括未开始、挂起都不算正在运行),则输出nil

加大难度:协同程序内部和外部的数据交流

https://www.runoob.com/lua/lua-coroutine.html

function foo (a)
    print("foo 函数输出", a)
    return coroutine.yield(2 * a) -- 返回  2*a 的值
end
 
co = coroutine.create(function (a , b)
    print("第一次协同程序执行输出", a, b) -- co-body 1 10
    local r = foo(a + 1)
     
    print("第二次协同程序执行输出", r)
    local r, s = coroutine.yield(a + b, a - b)  -- a,b的值为第一次调用协同程序时传入
     
    print("第三次协同程序执行输出", r, s)
    return b, "结束协同程序"                   -- b的值为第二次调用协同程序时传入
end)
       
print("main", coroutine.resume(co, 1, 10)) -- true, 4
print("--分割线----")
print("main", coroutine.resume(co, "r")) -- true 11 -9  -- 此处resume的参数中,除了第一个参数,剩下的参数将作为yield的参数
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- true 10 end
print("---分割线---")
print("main", coroutine.resume(co, "x", "y")) -- cannot resume dead coroutine
print("---分割线---")

--[[
第一次协同程序执行输出    1    10
foo 函数输出    2
main    true    4
--分割线----
第二次协同程序执行输出    r
main    true    11    -9
---分割线---
第三次协同程序执行输出    x    y
main    true    10    结束协同程序
---分割线---
main    false    cannot resume dead coroutine
---分割线---
]]--

任务55~58:文件操作

I/O的简单模式:

file = io.open(filename [, mode])
  mode:
  r -- 只读,文件必须存在
  w -- 只写,若文件已存在则清空内容后开始写入,若文件不存在则创建新文件开始写入
  a -- 附加方式打开只写文件,若文件不存在则创建,若文件存在则从文件尾(保留EOF符)开始写入
  r+ / w+ / a+:文件是可读写文件
  b -- 针对二进制文件,进行二进制模式的打开

io.flush() -- 向文件中写入缓冲中的所有数据

-- 读取
file = io.open("name.txt", "r")
io.input(file)
io.read()    -- 读取文件流中的一行数据
io.read("*l")     --默认值,和io.read()一样,读取下一行,在文件尾EOF处返回nil
io.read("*n")    -- 读取一个数字
io.read("*a")    -- 读取文件中的所有内容
io.read(9)    -- 读取之后的9个字符
io.close(file)

-- 写入
file = io.open(..., "a")
io.output(file)
io.write("new string")
io.close(file)

I/O的完全模式:需要同一时间处理多个文件时

以file:的方式进行调用(注意是 : ),而不是io.的方式

file = io.open("xx.txt","a")
file:read()
file:write("xxxxx")
file:close()

file:seek(optional whence, offset)  -- 把光标移到对应位置后开始操作

任务59:垃圾回收机制

Lua采用了自动内存管理机制