当前位置: 首页 > news >正文

Lua语言学习

为什么要用Lua

大部分的手机系统出于安全考虑禁止从网络上下载代码后动态的将这些下载的代码加载到内存中执行

所以,当你更新游戏时,就必须让用户从手机市场下载更新版本的程序,游戏程序通常体积较大,重新下载不仅耗时还耗流量,用户体验非常不好。
其实我们要更新的内容可能是1M都不到的一段程序代码或者一些资源,无需重新下载游戏

解决:绕过C#这一层,通过更上层的Lua脚本进行热更新,Lua脚本它可以作为Lua虚拟机上运行的一种语言被动态加载执行,不受限制

唯一的限制就是当Lua要去调用C#写的那些UnityAPI时,这些API是不能动态加载执行的,但现在市面上已有的Lua绑定工具已经解决了这个问题
方法:不动态加载C#API,而是静态绑定这些API

Lua受欢迎的原因:小巧,整个虚拟机也不过几十K,运行速度快,所有的运算都是基于寄存器的。

Lua中语句后的;号属于可加可不加的情况

Lua语言的学习

1、注释

-- 我是注释--
print("上面是注释");
--[[ 多行注释 --]]

2、变量
在lua中变量已经保存了数据类型,是通过变量来确定数据的类型。
所以在Lua中变量不需要指定变量的数据类型
在Lua中有八种基本类型分别是nil、boolean、
number、string、userdata、function、thread 和 table。

print(type("Hello world"))
print(type(1314520))
print(type(print))
print(type(true))
print(type(nil))

在这里插入图片描述
nil(空)
nil类型表示一种没有任何有效值,它只有一个值-nil
对于全局变量和table,nil还有一个删除的作用,给全局变量或者table表里的变量赋一个nil值,等于删除
nil作比较时应该加上双引号“”

boolean(布尔)
boolean类型只有两个可选值:true(真)和false(假),lua把false和nil看作false,其他的都为true,数字0也是true
number(数字)
默认只有一种number类型-double(双精度)类型(默认类型可以修改luaconf.h里的定义)
string(字符串)
字符串由一对双引号或单引号来表示
function(函数)
由 C 或 lua 编写的函数
thread(线程)
表示执行的独立协程,用于执行协同程序
userdata(自定义类型)
表示任意存储在变量中的C数据结构
table(表)
表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表

学习的步骤:
1、数据类型
2、常量和变量
3、控制结构(if-else、for)
4、函数
5、面向对象(通过表实现)

基本概念
值决定变量类型:值可以是:普通的数字、字符串、也可以是表或者函数

在Lua中,数字不区分是整数类型还是小数类型,统一叫做Number类型

Lua中的表是一个哈希表,键可以有两种类型:1、整数(数组小标);2、字符串(哈希表)

在lua中要进行输出的话,需要使用print函数

Lua的语法与练习

变量

全局变量不需要声明,给一个变量赋值后即可创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil
如果要删除一个全局变量,只需要将变量赋值为nil
注意:Lua中是大小写敏感的

Strings

lua是8位字节,所以字符串可以包含任何数值字符,包括嵌入的0,这就意味着可以存储任意的二进制数据在一个字符串里。
Lua中字符串是不可以修改的,但可以创建一个新的变量存放你要的字符串中

a="one string "
b=string.gsub(a,"one","another")--替换字符
print(a)
print(b)

string和其他对象相同,Lua自动进行内存分配和释放,一个string可以只包含一个字母也可以包含一本书的量。
Lua可以高效的处理长字符串,1M的string在Lua中是很常见的。

运行时,Lua会自动在string和numbers之间自动进行类型转换,当一个字符串使用算术操作符时,string就会被转成数字。

当Lua期望一个string而碰到数字时,会将数字转成string。

..在Lua中是字符串连接符,当在一个数字后写..时,必须加上空格防止被解释出错

注意如果需要显式将string转成数字可以使用函数tonumber(),如果string不是正确的数字该函数将返回nil。

表达式

Lua中的表达式包括数字常量、字符串常量、变量、一元和二元运算符、函数调用。还可以是非传统的函数定义和表构造

算术运算符
二元运算符:+ - * / ^ (加减乘除幂)
一元运算符:- (负值)
这些运算符的操作数都是实数。关系运算符
< > <= >= == ~=
这些操作符返回结果为 false 或者 true==~=比较两个值,如果两个值类型不同,
Lua 认为两者不同;nil 只和自己相等。Lua 通过引用比较 tables、userdata、functions。
也就是说当且仅当两者表示同一个对象时相等逻辑运算符
and or not
逻辑运算符认为 falsenil 是假(false),其他为真,0 也是 true.
andor 的运算结果不是 truefalse,而是和它的两个操作数相关
--a and b -- 如果 a 为 false,则返回 a,否则返回 b
--a or b -- 如果 a 为 true,则返回 a,否则返回 b
--print(4 and 5) --> 5
--print(nil and 13) --> nil
--print(false and 13) --> false
--print(4 or 5) --> 4
--print(false or 5) --> 5--(a and b) or c--not 的结果一直返回 false 或者 true
--[[
print(not nil) --> true
print(not false) --> true
print(not 0) --> false
print(not not nil) --> false
]]--连接运算符.. 字符串连接,如果操作数为数字,Lua将数字转成字符串
--print("Hello".."Lua事件")
--print(0 .. 1)优先级如下:
^
not - (unary)
* /
+ -
..
< > <= >= ~= ==
and
or除了^..外所有的二元运算符都是左连接的
a+i < b/2+1 <--> (a+i) < ((b/2)+1)
5+x^2*8 <--> 5+((x^2)*8)
a < y and y <= z <--> (a < y) and (y <= z)
-x^2 <--> -(x^2)
x^y^z <--> x^(y^z)

表结构

构造器是创建和初始化的表达式,表是Lua特有的功能强大的东西。最简单的构造函数为{},用来创建一个空表。可以直接初始化数组

days={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
print(days[1])Lua将"Sunday"初始化days[1](第一个元素索引为1),用"Monday"初始化days[2]...
print(days[4])构造函数可以使用任何表达式初始化:
tab={sin(1),sin(2),sin(3),sin(4),sin(5),sin(6),sin(7),sin(8)}如果想初始化一个表作为record使用可以这样使用:
a={x=0,y=0}
print(a.x)
print(a.y)不管用何种方式创建table,我们都可以向表中添加或者删除任何类型的域,构造函数仅仅影响表的初始化
function sin(n)return n+1
endw={x=0,y=0,label="console"}
x={sin(0).sin(1).sin(2)}
w[1]="another field"
x.f=W
print(w["x"])
print(w[1])
print(x.f[1])
w.x=nil每次调用构造函数,Lua都会创建一个新的table,可以使用table构造一个list:
list=nil
for line in io.lines() dolist={next=list,value=line}
end--在构造函数中域分隔符逗号(',')可以用分号(';')替代,通常我们使用分号用来分割不同类型的表元素
tab={x=10,y=45;"one","two","three"}
print(tab[1])--注意:不推荐数组下标从0开始,否则很多标准库不能使用
--在构造函数的最后的","是可选的,可以方便以后的扩展
a={[1]="red",[2]="green",[3]="blue",}
print(a[1])--如果真的想要数组下标从0开始
days={[0]="Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
print(days[1])

语句

Lua像C和PASCAL几乎支持所有的传统语句:赋值语句、控制结构语句、函数调用等,同时也支持非传统的多变量赋值、局部变量声明

--1、赋值语句
a="hello" .. "world"
print(a)--Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边
a,b,c=1,2,3;
print(a,b,c)a,b=a+1,b+1,b+2
print(a,b)--注意:如果要对多个变量赋值必须依次对每个变量赋值
a,b,c=0
print(a,b,c)--多值赋值经常用来交换变量,或将函数调用返回给变量:
--a,b=f()
--f()返回两个值,第一个赋给a ,第二个赋给b--2、局部变量与代码块(block)
--使用local创建一个局部变量,与全局变量不同,局部变量只在被声明的那个代码块内有效。
--代码块:指一个控制结构内,一个函数体,或者一个chunkx=10
local i=1while i<=x dolocal x=i*2print(x)i=i+1
end
if i>20 thenlocal xx=20print(x+2)
elseprint(x)
endprint(x)尽可能的使用局部变量,有两个好处
1、避免命名冲突
2、访问局部变量的速度比全局变量更快
因此给block划定一个明确的界限:do...end内的部分。当想要更好的控制局部变量的作用范围的时候是很有用的
dolocal a2=2*a2local d=sqrt(b^2-4*a*c)x1=(-b+d)/a2x2=(-b-d)/a2
endprint(x1,x2)控制结构语句
控制结构的条件表达式结果可以是任何值,Lua认为falsenil为假 ,其他值为真
if语句,有三种形式if conditions thenthen-part
end;if conditions thenthen-part
elseelse-part
end;if conditions thenthen-part
elseif conditions thenelseif-part
.. --多个elseif
elseelse-part
end;]]--while语句
while conditions dostatements;
end;repeat-until 语句
repeatstatements;
until conditions;for语句有两大类:
1、数值for循环
for var=exp1,exp2,exp3 doloop-part
end
for将用exp3作为step从exp1(初始值)exp2(终止值),执行loop-part。其中exp3可以省略,默认step=1注意:
1、三个表达式只会被计算一次,并且是在循环开始前。
for i=1,f(x) doprint(i)
endfor i=10,1, -1 doprint(i)
end2、控制变量var是局部变量自动被声明,并且只在循环内有效
for i=1,10 doprint(i)
end
max=i
print(max)如果需求保留控制变量的值,需要再循环中将其保存
local found=nil
for i=1,a.n doif a[i]==value thenfound=ibreakend
end
print(found)3、循环过程中不要改变控制变量的值,那样做的结果是不可预知的。如果要退出循环,使用break语句第二,泛型for循环
for i,v ipairs(a) do print(v) end
范型for遍历迭代子函数返回的每一个值for k in pairs(t) do print(k) end范型for和数值for有两点相同:
1、控制变量是局部变量
2、不要修改控制变量的值
days = {"Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"}revDays = {["Sunday"] = 1, ["Monday"] = 2,["Tuesday"] = 3, ["Wednesday"] = 4,["Thursday"] = 5, ["Friday"] = 6,["Saturday"] = 7}x = "Tuesday"
print(revDays[x])revDays={}
for i,v in ipairs(days) dorevDays[v]=i
end
x = "Tuesday"
print(revDays[x])breakreturn语句
break语句用来退出当前循环(for,repet,while)。在循环外部不可以使用。
return 用来从函数返回结果,当一个函数自然结束结尾会有一个默认的return
Lua语法要求breakreturn只能出现在block的结尾一句

函数

函数的两种用途:
1、完成指定的任务,这种情况下函数作为调用语句使用
2、计算并返回值,这种情况下函数作为赋值语句的表达式使用

function func_name(arguments-list)statements-list
end

调用函数的时候,如果参数列表为空,必须使用()表明是函数调用
当函数只有一个参数并且这个参数是字符串或者表结构的时候,()为可选的。

Lua使用的函数可以是Lua编写也可以是其他语言编写的,对于Lua来说,无论用什么语言实现的函数使用起来都是一样的。
Lua函数实参与形参的匹配与赋值语句类似,多余部分被忽略,缺少部分用nil补足

可变参数
Lua函数可以接受可变数目的参数。Lua将函数的参数放在一个叫arg的表中,除了参数以外,arg表中还有一个域n表示参数的个数

命名参数
Lua的函数参数是和位置相关的,调用时实参会按照顺序依次传入形参。有时候用名字指定参数是很有用的。

Lua中的函数是带有词法定界的第一类值

1、闭包:当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界。

2、非全局函数:Lua中函数可以作为全局变量也可以作为局部变量

3、Lua中函数的另一个特征是可以正确的处理尾调用,尾调用时一种类似在函数结尾的goto调用,当函数最后一个动作是调用另外一个函数时,我们称这种调用尾调用。

迭代器与泛型

1、 迭代器与闭包
迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素

范性for的语义
每次调用都需要创建一个闭包,大多数情况下,这种做法都没什么问题,但在allwords迭代器中创建一个闭包的代价比起读整个文件来说微不足道,然而
在有些情况下创建闭包的代价是不能忍受的。在这些情况下我们可以使用范性for本身来保存迭代的状态

无状态的迭代器
无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价
每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。
这种无状态迭代器的典型的简单的例子是ipairs,他遍历数组的每一个元素。

多状态的迭代器
迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包

真正的迭代器
有一种方式创建一个在内部完成迭代的迭代器

协同程序

协同程序(coroutine)与多线程情况下的线程比较类似:有自己的堆栈
自己的局部变量,有自己的指令指针,但是和其他协同程序共享全局变量等很多信息

Lua资源如何热更新
1、编写Lua脚本
2、打包.bytes结尾的二进制资源
3、按普通AB包的方式上传,下载、载入Unity
4、利用Unity的Lua脚本解析插件 解析Lua资源包中的脚本资源 并加载执行。
其中Unity脚本在收到一些消息(Start、Updata 、OnClick 时,要将这些消息转发给Lua脚本,一般我们会开发一个Unity和Lua之间的桥梁脚本,用于消息的传递)

http://www.xdnf.cn/news/720217.html

相关文章:

  • 编译原理OJ平台练习题题解
  • 用 Python 模拟下雨效果
  • 输入输出相关问题 day4
  • CSS--background-repeat详解
  • 数据中台是什么?数据中台解决方案怎么做?
  • Java基于SpringBoot的医院挂号系统,附源码+文档说明
  • Animate CC CreateJS 技术50道测试题目
  • python面向对象
  • NodeJS 基于 Koa, 开发一个读取文件,并返回给客户端文件下载,以及读取文件形成列表和文件删除的代码演示
  • 64、【OS】【Nuttx】任务休眠与唤醒:clock_nanosleep
  • Java类中各部分内容的加载执行顺序
  • 【JS进阶】JavaScript 中 this 值的确定规则
  • 软考-系统架构设计师-第六章 系统工程基础知识
  • 软考-系统架构设计师-第二章 嵌入式基础知识
  • 《Map 到底适合用哪个?HashMap、TreeMap、LinkedHashMap 对比实战》
  • 位图--Bitset【0基础详细版】
  • AI和大数据:是工具,还是操控人心的“隐形之手”?
  • Vue模板语法
  • 【大模型学习网络互联】Memory-Mapped I/O MMIO语义与MEM语义
  • 【Elasticsearch】exists` 查询用于判断文档中是否存在某个指定字段。它检查字段是否存在于文档中,并且字段的值不为 `null`
  • 【数据库】数据库的完整性
  • 2024 吉林 CCPC
  • 【25-cv-05855】Keith律所代理Paula Alejandra Navarro 版权图
  • RAG技术:私有大模型知识更新的最佳实践
  • 简述如果要存储用户的密码散列,应该使用什么字段进行存储?
  • 数据的类型——认识你的数据
  • SpringBoot使用MQTT协议简述
  • database disk image is malformed 的解决方法
  • C++ —(详述c++特性)
  • 行锁与表锁详解:原理、区别与面试要点