Lua语言学习

article/2025/8/25 20:57:48

为什么要用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.hkcw.cn/article/scLjnVtHqa.shtml

相关文章

Maven 仓库类型与镜像策略

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

蓝牙和wifi相关的杂项内容总结

蓝牙的传输速率演进 蓝牙技术的传输速率随着版本的演进不断提升&#xff0c;不同版本和模式&#xff08;经典蓝牙 BR/EDR 和低功耗蓝牙 BLE&#xff09;的速率差异显著。以下是蓝牙传输速率的完整发展历程和技术细节&#xff1a; 经典蓝牙&#xff08;BR/EDR&#xff09;的速…

AAA稳态LED太阳光模拟器的特点剖析

AAA稳态LED太阳光模拟器作为光伏测试领域的重要设备&#xff0c;其技术特点直接关系到太阳能电池研发与质量控制的精度。以下从光谱匹配性、辐照均匀性、稳定性、能效比及智能化设计五个维度展开深度剖析&#xff1a; 一、光谱匹配性的突破性进展 传统氙灯光源在AM1.5G标准光谱…

cadence PCB 精度设置成小数点4位方法

1. allegro 在进行PCB设计时&#xff0c;单位一般默认为Mils&#xff0c;会遇到&#xff0c;精度只能选择2位&#xff0c;不能增加到4位&#xff0c; 精度的范围只能设置为0-2&#xff0c;不能设置为3或4 2. Setup -> User preference&#xff0c;进行设置&#xff0c…

VirtualBox安装 Rocky

这不是 CentOS要完蛋了吗&#xff0c;找了Rock Linux 。下载了一个差不多需要10G&#xff0c;艹。 然后在virtual BOX中安装&#xff0c;安装成功了 安装和Centos一样&#xff1a; 《VirtualBox安装以及安装CentOS7》 有几点需要注意就行了&#xff1a; 准备工作 确保主机的…

【MySQL】C语言连接

要使用C语言连接mysql&#xff0c;需要使用mysql官网提供的库&#xff0c;大家可以去官网下载 我们使用C接口库来进行连接 要正确使用&#xff0c;我们需要做一些准备工作: 保证mysql服务有效在官网上下载合适自己平台的mysql connect库&#xff0c;以备后用 下载开发库 s…

SpringBoot 日志

今天我们来学习日志&#xff0c;日志是啥玩意呢&#xff0c;其实我们之前使用过超简易版的日志&#xff0c;就是打印&#xff0c;我感觉大家应该都一样&#xff0c;使用打印来检查代码是不是执行到这里了&#xff0c;通过控制台打印的日志来发现问题&#xff0c;排查问题&#…

C语言——深入理解指针(1)

一、内存和地址 1.1 内存 在讲内存之前&#xff0c;我们先看一个生活中的案例&#xff1a; 假设有一栋宿舍楼&#xff0c;把你放在楼里&#xff0c;楼上有100个房间&#xff0c;但是房间没有编号&#xff0c;你的一个朋友来找你玩&#xff0c;如果想找到你&#xff0c;就得挨…

计算机操作系统(十四)互斥锁,信号量机制与整型信号量

计算机操作系统&#xff08;十四&#xff09;互斥锁&#xff0c;信号量机制与整型信号量 前言一、进程互斥与互斥锁1. 什么是进程互斥&#xff1f;2. 互斥锁是什么&#xff1f; 二、信号量机制&#xff08;解决互斥的更通用方案&#xff09;1. 为什么需要信号量&#xff1f;2. …

铸铁平台的优势和应用

铸铁平台是一种具有广泛应用的工业设备&#xff0c;其在各个领域均发挥着重要作用。本文将探讨铸铁平台的优势和应用&#xff0c;以帮助读者更好地了解这一重要设备。 一、铸铁平台的优势 强度高&#xff1a;铸铁平台采用高强度铸铁材料制成&#xff0c;具有优异的强度和耐磨性…

EfficMultiCoreMemoryPool项目

目录 1. 定长内存池介绍 2. tcmalloc整体框架了解 2.1 ThreadCache设计 各区间内存浪费分析 2.1.1 ThreadCache申请内存 2.1.2 ThreadCache释放内存 2.1.3 TLS线程局部变量 2.2 CentralCache设计 2.2.1 CentralCache申请内存 ThreadCache --> CentralCache的申请内…

【Doris基础】Apache Doris vs 传统数据仓库:架构与性能的全面对比

目录 1 引言 1.1 传统数据仓库的发展 1.2 现代分析型数据库的崛起 2 核心架构对比 2.1 传统数据仓库的架构 2.2 Doris的架构设计 3 关键技术差异 3.1 存储引擎对比 3.2 查询执行对比 3.3 数据摄入方式对比 4 性能与扩展性对比 4.1 性能基准对比 4.2 扩展性对比 5…

字符串转换函数-leetCode-008

问题描述&#xff1a; 实现一个将字符串转换为整数的函数时&#xff0c;需要考虑各种边界情况和细节&#xff0c;例如空格的处理、符号的判断、数字的读取以及整数溢出的处理等。以下是详细的解题过程以及代码实现。 问题分析 空格处理&#xff1a;需要丢弃字符串开头的空格字…

深度学习目标检测实战——YOLOv8从入门到部署

本文将手把手带你用 YOLOv8&#xff08;目前非常先进的目标检测框架&#xff09;实现从数据标注到模型部署的全流程实战&#xff0c;并达到工业级检测精度&#xff01; [yolo视频教程&#xff0c;戳蓝字即可学习] 这才是科研人该学的【YOLO算法全系列】&#xff0c;一口气学完…

C#对象集合去重的一种方式

前言 现在AI越来越强大了&#xff0c;有很多问题其实不需要在去各个网站上查了&#xff0c;直接问AI就好了&#xff0c;但是呢&#xff0c;AI给的代码可能能用&#xff0c;也可能需要调整&#xff0c;但是自己肯定是要会的&#xff0c;所以还是总结一下吧。 问题 如果有一个…

[ctfshow web入门] web124

信息收集 error_reporting(0); //听说你很喜欢数学&#xff0c;不知道你是否爱它胜过爱flag if(!isset($_GET[c])){show_source(__FILE__); }else{//例子 c20-1$content $_GET[c];// 长度不允许超过80个字符if (strlen($content) > 80) {die("太长了不会算");}/…

AI生成思维导图全技术解析

一、AI生成思维导图的底层技术逻辑 (一)知识结构化的核心流程 AI生成思维导图的本质是非结构化文本到结构化知识图谱的转化,其技术流程可拆解为五大核心环节: 1. 语义解析与实体抽取 多模态输入处理:支持文本(Markdown/Word/PDF)、语音(会议录音)、手写笔记(图片O…

秋招Day12 - 计算机网络 - 网络综合

从浏览器地址栏输入URL到显示网页的过程了解吗&#xff1f; 从在浏览器地址栏输入 URL 到显示网页的完整过程&#xff0c;并不是一个单一的数据包从头到尾、一次性地完成七层封装再七层解析的过程。 而是涉及到多次、针对不同目的、与不同服务器进行的、独立的网络通信交互&a…

SpringBoot集成第三方jar的完整指南

原文地址&#xff1a;https://blog.csdn.net/weixin_43826336/article/details/141640152?ops_request_misc%257B%2522request%255Fid%2522%253A%25227d4118ef2d572ba4428caf83f1d2bb28%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id7d4118…

官宣开源!通过 AI 驱动的前后端 Java Vue 脚手架(AI 后台管理)正式发布!

&#xff08;在本帖评论点赞关注的小伙伴&#xff0c;今日可得好运相随&#xff0c;会有好事发生&#xff0c;不信试试看&#xff5e;&#xff09; &#x1f525; 知路后台管理 - 通过 AI 驱动的新一代前后端脚手架 知路是一个原生集成 AI 并通过 AI 驱动的重新构思、重新设计…