Unity:lua热更新(一)——AB包AssetBundle、Lua语法
写在前面:
写本系列(自用)的目的是回顾已经学过的知识、记录新学习的知识或是记录心得理解,方便自己以后快速复习,减少遗忘。主要是C#代码部分。
一、AB包AssetBundle
1、创建AB包
导入AssetBundle资源后,选中资源区中的预设体,在右侧inspector窗口下方AssetBundle处点击下拉列表选择new,创建一个新AB包并为其取名即可。这里AB包命名为model。

创建完成后,点击Window,并选择AssetBundle Browser即可查看当前工程中的AB包。

点击Build页签,黄色框中框选的即为主包路径,可以将名字修改一下,例如修改为PC(不改也行,改短了方便使用)。

如果想要一键将多个物体打包在同一个AB包里,按下shift或者ctrl进行选择即可。
2、加载AB包中的资源
(1)同步加载
想要加载AB包中的资源,需要先加载AB包。
加载AB包使用的api是AssetBundle.LoadFromFile(),括号内传入AB包路径。AB包会被保存在streamingAssets文件夹中,因此路径为Application.streamingAssetsPath + "/" + "你为AB包取的名字"
加载AB包后就可以加载AB包的资源。可以直接调用api用资源的名字加载,如我想要加载上一步中打包在model包中的Cube:ab.LoadAsset("Cube");
void Start()
{AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");ab.LoadAsset("Cube");
}
但只用名字加载时,如果出现同名不同类型的资源,会分不清。因此,建议泛型加载或是Type指定类型。泛型加载为GameObject obj = ab.LoadAsset<GameObject>("Cube");
类型加载为GameObject obj = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject;
void Start()
{AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");GameObject obj = ab.LoadAsset<GameObject>("Cube");//GameObject obj = ab.LoadAsset("Cube", typeof(GameObject)) as GameObject;Instantiate(obj);
}
(2)异步加载
除了同步加载外,还可以使用异步加载,防止玩家游玩时游戏卡顿。异步加载涉及到协程的知识。可以启动协程,配合异步加载apiAssetBundle.LoadFromFileAsync()来进行加载,括号内传入的是需要加载资源的路径。
加载得到的AB包类型为AssetBundleCreateRequest,记住即可。加载完AB包后,加载AB包中的资源,对应的API为.assetBundle.LoadAssetAsync(),括号内传入两个参数,资源名字与资源类型。返回类型为AssetBundleRequest。
得到资源后,可以.asset得到加载的资源。由于该类型默认为object,还需要将资源类型转化为你想要加载的资源的类型。
public Image image;void Start()
{StartCoroutine(LoadABRes("head", "23_11100001"));
}IEnumerator LoadABRes(string ABName, string resName)
{AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABName);yield return abcr;AssetBundleRequest abq = abcr.assetBundle.LoadAssetAsync(resName, typeof(Sprite));yield return abq;image.sprite = abq.asset as Sprite;
}
3、AB包的卸载
AB包是不能重复加载的,如果重复加载了,哪怕和原来加载的ab包名字不同也会报错,比如如下的代码是会报错的:
void Start()
{AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");AssetBundle ab2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");
}
卸载AB包也有专门的API。
AssetBundle.UnloadAllAssetBundles(),可以直接卸载所有加载的AB包,括号内传入bool参数,如果是true,会将场景上所有从ab包内加载的资源删除,例如前面已经实例化了的cube。因此一般填false。
也可以卸载单个AB包:ab.Unload(),括号内参数同理。
void Start()
{//加载AB包AssetBundle ab = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "model");AssetBundle.UnloadAllAssetBundles(false);ab.Unload(false);
}
4、AB包依赖
AB包依赖说的是,一个资源身上用到了别的AB包中的资源,这个时候如果只加载自己的AB包,会出现资源丢失的情况这。种时候,需要把依赖包一起加载了。
依赖包的关键知识点是——利用主包 获取依赖信息。主要有如下几个步骤:
1、加载主包:AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "PC");这里的“PC”是我们一开始为主包改的名。
2、加载主包中的固定文件: AssetBundleManifest abMainfest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");这句代码基本是固定的,固定文件的类型、名字等都是Unity内部固定的,记住即可。
3、从固定文件中 得到依赖信息。使用string[] strs = abMainfest.GetAllDependencies("model");,就可以得到model的所有依赖信息了,之后遍历这些依赖信息,把所有相关的AB包加载了即可。
void Start()
{AssetBundle abMain = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "PC");AssetBundleManifest abMainfest = abMain.LoadAsset<AssetBundleManifest>("AssetBundleManifest");string[] strs = abMainfest.GetAllDependencies("model");for(int i = 0; i < strs.Length; i++){AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + strs[i]);}
}
二、Lua语法
1、lua注释及第一个lua程序
lua可以单行注释、多行注释。多行注释有三种方式。
(1)单行注释
使用--双横杠即可
--单行注释
(2)多行注释
如下,有三种方式,都大同小异。
--[[
多行注释
]]
--[[
第二种
]]--
--[[
第三种
--]]
(3)第一个lua程序
lua使用print打印函数,与C#不同,lua语句可以省略分号:
print("hello world")

2、变量
(1)简单变量类型
有nil number string boolean
需要说明的是:lua里面的所有变量声明都不需要声明变量类型,他会自动判断类型。也就是说lua中的一个变量,可以随便赋值。a = 1, a = 1.4, a = true。
通过type函数可以得到变量类型,返回值是string。
①nil
nil 类似于C#里的null,lua中使用没有声明过的变量 不会报错 默认值是nil。
print("************变量************")print(b)
![]()
print("************nil************")
a = nil
print(a)
print(type(a))
print(type(type(a)))

②number
在lua中,所有的数值都是number,不分int float之类的。
print("************number************")
a = 1
print(a)
print(type(a))
a = 1.2
print(a)
print(type(a))

③string
string是字符串类型,可以用单引号或者双引号包裹,因为lua里没有char,无论如何都是string。
print("************string************")
a = "2345"
print(a)
a = '123'
print(a)
print(type(a))

④boolean
相当于C#的bool类型
print("************boolean************")
a = true
print(a)
a = false
print(a)
print(type(a))

(2)复杂数据类型
复杂数据类型主要包括:函数 function、表 table、数据结构 userdata、协同程序 thread(线程)。会在后续介绍。
3、string
无论是单引号还是双引号,都是字符串。
print("************字符串************")
str = "双引号字符串"
str2 = '单引号字符串'
(1)获取字符串长度
可以使用#来获取,例如想要获取字符串s的长度,打印#s即可。
需要注意的是,一个汉字占三个长度,一个英文字符占一个长度,如下
print("************字符串长度************")s = "aBcdEfg"
print(#s)s = "字符串"
print(#s)

(2)字符串多行打印
lua也支持转义字符,所以可以在字符串中插入\n来进行多行打印。
也可以使用两个中括号进行多行打印,如下
print("************字符串多行打印************")
print("123\n123")s = [[南瓜
牛乳小丸子
好好吃
]]
print(s)

(3)字符串拼接
可以通过两个点拼接 “123” .. "456"
也可以通过string.format(),该用法与C语言类似。%d表示数字,%a表示字母,%s表示字符。
print("************字符串拼接************")print("123" .. "456")
s1 = "1234"
s2 = 111
print(s1 .. s2)print(string.format("我今年%d岁了", 18))

(4)别的类型转字符串
使用lua提供的tostring()函数即可,括号内传入希望转为字符串的变量
print("************别的类型转字符串************")
a = true
print(tostring(a))
![]()
(5)字符串提供的公共方法
1、小写转大写与大写转小写
需要注意的是,这一系列公共方法,会得到新字符串不会改原来的字符串。
小写转大写的方法:string.upper()
大写转小写:string.lower()
print("************字符串提供的公共方法************")
str = "abCdefg"print(string.upper(str))
print(str)print(string.lower(str))

2、翻转字符串
使用api:string.reverse()
print(string.reverse(str))
![]()
3、字符串索引查找
使用string.find(str, "Cde")),第一个参数是要进行查找的字符串,第二个参数是要查找的内容
该函数,会返回开始位置和结束位置。值得注意的是,lua的字符串索引是从1开始的
print(string.find(str, "Cde"))
![]()
4、截取字符串
使用string.sub(),第一个参数传入的是需要进行截取的字符串。接下来,
如果只传一个数字:从该数字到结尾都截取返回
如果传两个数字:那么就会返回这两个数字之间的字符
print(string.sub(str, 3))print(string.sub(str, 3, 4))
![]()
5、字符串重复与字符串修改
字符串重复使用string.rep(),参数一是重复的字符串,参数二是重复的次数
字符串修改string.gsub(),参数一传入字符串,参数二是需要修改的字符,参数三是需要修改为什么。例如string.gsub(str, "Cd", "**")指的是,把字符串str中的Cd都改为*。返回值会多出一个数字,得到的数字指的是修改了几次,例如如果str中有两个Cd,就会返回2。
print(string.rep(str, 2))print(string.gsub(str, "Cd", "**"))
![]()
6、字符 ASCII码 互转
string.byte(),第一个参数是字符串,第二个参数是需要转换的索引。例如string.byte("Lua", 1)指的是把Lua字符串的第一个字符L转为ASCII码。
string.char(),ASCII码转字符串
a = string.byte("Lua", 1)
print(a)print(string.char(a))
![]()
4、运算符
(1)算数运算符
主要包括+ - * / % ^,没有自增自减 ++ --(使用会报错),没有复合运算符 += -= *= /=(使用会报错)。
+ - * / % ^分别是加、减、乘、除、取余、幂。
非常特殊的一点是,只要字符串能转为数字,都可以直接和数字进行加减乘除运算。如下:
print("123.4" + 1)也是能正常运算的
print("************运算符************")
print("************算数运算符************")print("加法运算" .. 1 + 2)
a = 1
b = 2
print(a + b)print("123.4" + 1)

其他的运算也同理:
print("减法运算" .. 1 - 2)
print("123.4" - 1)print("乘法运算" .. 1 * 2)
print("123.4" * 1)print("除法运算" .. 1 / 2)
print("123.4" / 1)print("取余运算" .. 1 % 2)
print("123.4" % 1)--lua中是幂运算,c#是位运算
print("幂运算" .. 1 ^ 2)
print("123.4" ^ 1)

(2)条件运算符
> < >= <= == ~=:大于,小于,大于等于,小于等于,等于等于,不等于。
注意这里不等于使用的是~=,C#里使用的是!=
print("************条件运算符************")
print(3>1)
print(3<1)
print(3>=1)
print(3<=1)
print(3==1)
print(3~=1)

(3)逻辑运算符
与或非,使用的是and、or、not 。也遵循“短路”原则。
短路说的是print(false and print("123")),这句代码,由于and的左侧是false,那么这个条件就一定是false的,and不会再执行右侧的语句。
反之print(true and print("123"))就会有打印输出。
print("************逻辑运算符************")print(true and false)
print(true and true)
print(true or false)
print(false or false)
print(not true)print(false and print("123"))
--print(true and print("123"))

(4)位运算符与三目运算符
lua不支持位运算符,需要我们自己实现;lua也不支持三目运算符。
