Unity热更新——AB包和Lua

AB包:

1.是什么?

是特定平台的资产压缩包,包括(模型,贴图,预制体,音效,材质球)

Resources:核心必需资源 + 常驻内存 + 不热更,主打 "随用随取、不用折腾";

AB 包:非必需资源 + 动态加载 / 卸载(省内存) + 支持热更,主打 "灵活可控、适配手游场景"。

2.有什么用?

打包层面,相较于resources的资源打包,可以指定,位置,压缩方式(减小包大小)还可以动态更新(热更新)

3.生成打包AB包

Asset Bundle Browser资源包,unity已经2020后取消了

然后就可以选择要打包的资源了。

然后在Windows工具栏里面就有了,完事呢(中间有一个打包页签):

4.使用解包AB包

同一个ab包不能重复加载包

加载ab包和里面的资源:

同步:

bash 复制代码
        //加载AB包
        AssetBundle ab= AssetBundle.LoadFromFile(Application.streamingAssetsPath+"/"+"f");//包名
        //加载AB包里面的资源
        GameObject obj=ab.LoadAsset<GameObject>("Cube");
        GameObject obj2=ab.LoadAsset("Cube",typeof(GameObject)) as GameObject;
        Instantiate(obj);

异步:

bash 复制代码
        StartCoroutine(LoadABRes("f","Cube"));
    }
    //异步不知到什么时候成功,所有要携程,本质不依赖携程的分时执行,只是要携程的顺序执行
    IEnumerator LoadABRes(string ABname,string RESname)
    {
        AssetBundleCreateRequest AB=AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + "/" + ABname);
        yield return AB;
        AssetBundleRequest ab= AB.assetBundle.LoadAssetAsync(RESname,typeof(GameObject));
        yield return ab;
        GameObject jg= ab.asset as GameObject;
        Instantiate(jg);
    }

卸载ab包以及是否影响已经使用的资源:

bash 复制代码
			//false不影响,true影响
			ab.unLoad(true);//单个
        AssetBundle.UnloadAllAssetBundles(false);//全部

5.AB包依赖:

某个包里面的资源的组成部分都会也必须在ab包里面。

当需要的组成部分和资源不在一个包:1,放一个包,2,加载资源们所在包(解压缩到内存占据容器)3,使用主包找某个包的依赖包(主包名字在biudin里面可以看)

bash 复制代码
        //加载主包StandaloneWindows
        AssetBundle zhu = AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "StandaloneWindows");
        //加载主包固定文件夹:
        AssetBundleManifest abManifest = zhu.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        //从固定文件夹得到依赖信息
        string[] strs = abManifest.GetAllDependencies("one");
        for (int i = 0; i < strs.Length; i++)
        {
            Debug.Log(strs[i]);
            AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + strs[i]);
        }
        AssetBundle one= AssetBundle.LoadFromFile(Application.streamingAssetsPath + "/" + "one");
        GameObject obj=one.LoadAsset("Cube",typeof(GameObject)) as GameObject;
        Instantiate(obj);

右键我们的主包就是默认包可以看到里面每个自定义包的依赖包:

6.AB包资源加载管理器

用到了之前封装好的这个mono单例模块,毕竟是个管理器

bash 复制代码
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using UnityEngine;
using UnityEngine.Events;

public class ABMsg : SingletonAutoMono<ABMsg>
{
    private Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();
    private AssetBundle mainAB = null;
    private AssetBundleManifest mainfest = null;

    /// <summary>
    /// 获取AB包的路径
    /// </summary>
    private string PathUrl
    {
        get
        {
            return Application.streamingAssetsPath;
        }
    }

    /// <summary>
    /// 主包名,随着平台改变
    /// </summary>
    private string MainABName
    {
        get
        {
#if UNITY_IOS
            return "IOS";
#elif UNITY_ANDROID
            return "Android";
#else 
            return "StandaloneWindows";
#endif
        }
    }
    /// <summary>
    /// 代码复用,实现多形式同步加载资源
    /// </summary>
    /// <param name="abName"></param>
    public void LoadAB(string abName)
    {
        //加载主包
        if (mainAB == null)
        {
            string mainABPath = System.IO.Path.Combine(PathUrl, MainABName);
            mainAB = AssetBundle.LoadFromFile(mainABPath);
            mainfest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        //获取并加载依赖包
        string[] dependencies = mainfest.GetAllDependencies(abName);
        for (int i = 0; i < dependencies.Length; i++)
        {
            if (!abDic.ContainsKey(dependencies[i]))
            {
                string depPath = System.IO.Path.Combine(PathUrl, dependencies[i]);
                AssetBundle depAB = AssetBundle.LoadFromFile(depPath);
                if (depAB != null)
                {
                    abDic.Add(dependencies[i], depAB);
                }
                else
                {
                    Debug.LogError("加载依赖AB包失败: " + depPath);
                }
            }
        }

        //加载目标包
        if (!abDic.ContainsKey(abName))
        {
            string abPath = System.IO.Path.Combine(PathUrl, abName);
            if (!System.IO.File.Exists(abPath))
            {
                Debug.LogError("目标AB包不存在: " + abPath);
                return;
            }

            AssetBundle targetAB = AssetBundle.LoadFromFile(abPath);
            if (targetAB != null)
            {
                abDic.Add(abName, targetAB);
            }
            else
            {
                Debug.LogError("加载目标AB包失败: " + abPath);
                return;
            }
        }

    }
    //同步加载资源(普通)
    public Object LoadRes(string abName, string resName)
    {
        LoadAB(abName);
        //加载并返回资源
        Object res = abDic[abName].LoadAsset(resName);
        if (res == null)
        {
            Debug.LogError($"在AB包 {abName} 中找不到资源: {resName}");
        }
        return res;
    }
    //同步加载资源(泛型)
    public Object LoadRes<T>(string abName, string resName) where T:Object
    {
        LoadAB(abName);
        //加载并返回资源
        T res = abDic[abName].LoadAsset<T>(resName);
        if (res == null)
        {
            Debug.LogError($"在AB包 {abName} 中找不到资源: {resName}");
        }
        return res;
    }
    //同步加载资源(类型)
    public Object LoadRes(string abName,string resName,System.Type type)
    {
        LoadAB(abName);
        //加载并返回资源
        Object res = abDic[abName].LoadAsset(resName,type);
        if (res == null)
        {
            Debug.LogError($"在AB包 {abName} 中找不到资源: {resName}");
        }
        return res;
    }
    //异步加载(协程是为了管理异步阶段,比较后台不阻塞需要一个携程得到当异步准备好了后触发回调委托)
    public void LoadResAsync(string abName,string resName,UnityAction<Object> callback)
    {
        StartCoroutine(ReallyLoadResAsync(abName,resName,callback));
    }
    private IEnumerator ReallyLoadResAsync(string abName, string resName, UnityAction<Object> callback)
    {
        LoadAB(abName);
        //加载并返回资源
        AssetBundleRequest rab = abDic[abName].LoadAssetAsync(resName);
        yield return rab;
        if (rab.asset == null)
        {
            Debug.LogError($"在AB包 {abName} 中找不到资源: {resName}");
        }
        else
        {
            callback(rab.asset);
        }
    }

    //异步加载(泛型)
    public void LoadResAsync<T>(string abName, string resName, UnityAction<Object> callback)
    {
        StartCoroutine(ReallyLoadResAsync<T>(abName, resName, callback));
    }
    private IEnumerator ReallyLoadResAsync<T>(string abName, string resName, UnityAction<Object> callback)
    {
        LoadAB(abName);
        //加载并返回资源
        AssetBundleRequest rab = abDic[abName].LoadAssetAsync<T>(resName);
        yield return rab;
        if (rab.asset == null)
        {
            Debug.LogError($"在AB包 {abName} 中找不到资源: {resName}");
        }
        else
        {
            callback(rab.asset);
        }
    }
    //异步加载(类型)
    public void LoadResAsync(string abName, string resName,System.Type type, UnityAction<Object> callback)
    {
        StartCoroutine(ReallyLoadResAsync(abName, resName,type, callback));
    }
    private IEnumerator ReallyLoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callback)
    {
        LoadAB(abName);
        //加载并返回资源
        AssetBundleRequest rab = abDic[abName].LoadAssetAsync(resName,type);
        yield return rab;
        if (rab.asset == null)
        {
            Debug.LogError($"在AB包 {abName} 中找不到资源: {resName}");
        }
        else
        {
            callback(rab.asset);
        }
    }



    //卸载单个AB包
    public void UnloadAB(string abName, bool unloadAllLoadedObjects = false)
    {
        if (abDic.ContainsKey(abName))
        {
            abDic[abName].Unload(unloadAllLoadedObjects);
            abDic.Remove(abName);
        }
    }

    //卸载全部AB包
    public void UnloadAll()
    {
    AssetBundle.UnloadAllAssetBundles(false);
        abDic.Clear();
        mainAB = null;
        mainfest = null;
    }
}

使用:

bash 复制代码
    private void Start()
    {
        Object x=ABMsg.GetInstance().LoadRes("one", "Cube");
        Object y=ABMsg.GetInstance().LoadRes<GameObject>("one", "Cube");
        ABMsg.GetInstance().LoadResAsync("one","Cube",typeof(GameObject),cc);
        ABMsg.GetInstance().LoadResAsync<GameObject>("one","Cube",cc);
        Instantiate(x);
        Instantiate(y);
    }
    public void cc(object a)
    {
        Instantiate(a as GameObject);
        print("你好");
    }
    private void Update()
    {
        
    }

Lua热更新:

用vscode可以写,但是我们目前用这个sublime text超强文本编辑器

1.快捷键:

ctrl+B运行

选择open 文件夹=打开文件夹做这个目录吧

2.lua语法:

第一个程序

bash 复制代码
print("*********第一个程序*********")
--单行注释
print("你好世界")--可以省略分好
print("你好世界")
--[[
第一种多行注释
]]
--[[
第二种多行注释
]]--
--[[
第三种多行注释
--]]

变量

bash 复制代码
print("**********变量********")
--lua当中的简单变量类型
--nil number string boolean
--lua中的变量申明不需要变量类型,会自动判断
print("**********nil********")
print(b)--没有声明过的变量默认为nil空
a = nil
print(type(a))--可以使用这个函数得到类型,这个函数的返回值是string
print(type(type(a)))
print(a)
print("**********number********")
a=1
print(a)
a=1.99
print(a)
print("**********string********")
a="123"
print(a)
a='123'
print(a)
print("**********boolean********")
a=true
print(a)
a=false
print(a)
--lua当中的复杂变量类型
--function函数 table表 userdata数据结构 thread协同程序

字符串操作

bash 复制代码
print("*********字符串操作*********")
str="nihao你好"--英文占一个字符,中文占三个
print("*********字符串长度*********")
print(#str)
print("*********字符串换行*********")
print("123\n123")
s=[[第一行
第二行
第三行
]]
print(s)
print("*********字符串拼接*********")
print("123"..111.9)
print(string.format("我是%s,我今年%d","xzc",19))
print("*********转字符串*********")
print(tostring(true))
print("*********公共方法*********")
print(string.upper(str))--大写
print(string.lower(str))--小写
print(string.reverse(str))--翻转
print(string.find(str,"好"))--索引从一开始,并且返回的是字节第一个和最后一个
print(string.sub(str,3,4))--截取>=3<=4
print(string.rep(str,4))--重复
print(string.gsub(str,"你好","你坏"))--修改(修改了多少次)
a=string.byte("Lua",1,3)--转ascll妈>=1<=3区间
print(a)
print(string.char(a))--ascll码转字符串

运算符

bash 复制代码
print("*********运算符*********")
print("*********算数运算符*********")
-- + - * / %
-- 没有自增自减++ --
--没有复合运算符 += -= /= *= %=
-- 字符串在运算符里面自动转换成number如果可以
print((1+2).."|"..(1-2).."|"..("12"*2).."|"..(9/3).."|"..(9%2))
print("幂运算"..2^3)
print("*********条件运算符*********")
--> < >= <= ~=
print(1~=9)
print("*********逻辑运算符*********")
-- && ||  ! and or not"遵循前面满足后面不执行,短路"
print(true and false)
print(true or false)
print(not (true and false))
print("*********位运算符*********")--不支持

print("*********三目运算符*********")--不支持

条件分支

bash 复制代码
print("*********条件分支*********")
a=5
if a<5 then
	print("1")
elseif a>5 then
	print("2")
else
	print("3")
end

循环语句

bash 复制代码
print("*********循环语句*********")
print("*********while语句*********")
num=0
while num<5 do
	print(num)
	num=num+1
end
print("*********do while语句*********")
	num=0
	repeat
print(num)
num=num+1
	until num>5--结束条件
print("*********for语句*********")
for i=1,10,2 do--i默认从初+第三个参数(默认1)
	print(i)
end

函数

bash 复制代码
print("*********函数*********")
print("*********无参无返回值*********")
function F(  )
print("s")
end

F(  )

F1=function()
print("b")
end
F1()
print("*********有参数*********")
function F2(A)
	print(A)
end

F2()
F2("1","2")
F2('1')
F2(12.9)
print("*********有返回值*********")
function F3(a)
	print(a)
	return a,"123",true--多返回值和接取
end
x,y,z,s=F3(11.3)
print(x,y,z,s)

print("*********函数类型*********")
F4=function (  )
	-- body
end
print(type(F4))
print("*********函数重载*********")
--函数名相同,函数参数类型和个数不同,但是天然就是支持不同类型和不同参数
--不支持
print("*********变长参数*********")
function F5( ... )
	--要先用一个表存起来
	arg={...}
	for i=1,#arg do
		print(arg[i])
	end
end
F5("1",2,true)
print("*********函数嵌套*********")
function F6()
	F7=function()
		print(123)
		end
return F7
end
F8=F6()
F8()

表table

bash 复制代码
print("*********复杂数据类型表talbe*********")
--所有数据类型的根基
print("*********数组*********")
a={1,2,"s",true,nil}
print(a[0])--表的索引第一位是1
print(#a)--中间为空就会断
print("*********数组遍历*********")
for i=1,#a do
	print(i)
end
print("*********二维数组*********")
a={{1,2,3},{4,5,6}}
print(a[1][1])
print("*********二维数组遍历*********")
for i=1,#a do
	b=a[i]
	for j=1,#b do
		print(b[j],a[i][j])
	end
end
print("*********自定义索引*********")
aa={[0]=1,2,3,[-8]=4,5}--跳过的索引为nil
for i=-8,#aa do
	print(aa[i])
end

i.pairs迭代器遍历

bash 复制代码
print("*********ipairs迭代器遍历(拉)*********")
--迭代器遍历 主要是用来遍历表的
--#得到的数组长度,受nil中断,自定义索引也会有坑
a={[0]=1,2,[-1]=3,4,5,[9]=100}
for i,k in ipairs(a) do
	print(i..k)
	--ipairs遍历是从1开始遍历,小于等于0的值得不到,
	--中间断了也不成

end
print("*********pairs迭代器遍历(ok)*********")
for i,k in pairs(a) do--两个接着,table内容本质就是键值对
	print("pairs"..i..k)
end

表table2

bash 复制代码
print("*********复杂数据类型表talbe-2*********")
print("*********字典*********")
print("*********字典的申明*********")
a={["name"]="xzc",["age"]=14,["1"]=5}--声明(table可以自定义键)
a.name="dy"--修改
a["2"]=100--增加
a["2"]=nil--删除,因为不加默认是nilb
print(a["name"],a.age,a["1"],a["2"])--可以直接点,但是不能是数字
print("*********字典的遍历*********")
for k,v in pairs(a)do
	print(k,v)
end


print("*********类和结构体*********")
--默认没有面向对象,需要自己实现
student={
age=1,
sex=true,
up=function(x)
	--print(age)--成员之间不能直接调用
	print(student.age)
	print("成长函数",x)
end
}
student.name="t"
--lua的table中.和冒号区别
student.up()
student:up()--冒号调用会默认吧冒号者当第一个参数传入。self表示该函数的第一个参数
for i,j in pairs(student)do
	print(i,j)
end

print("*********表的公共操作*********")
--表中table的公共操作
t1={{age=1,name="123"},{age=1,name="345"}}
t2={name="xzc",sex=true}
print("*********插入*********")
print(#t1)
table.insert(t1,t2)--插入
t2={name="xzc",sex=true}
print(#t1)
print("*********删除*********")
table.remove(t1,2)--删除指定,默认为1
print(t1[2].name)
print("*********排序*********")
t2={5,8,2,5}
table.sort( t2)
for i,j in pairs(t2) do
	print(j)
end
table.sort( t2,function(a,b)
	if a>b then
		return true
	end
end)
for i,j in pairs(t2) do
	print(j)
end
print("*********拼接*********")
t2={20,1,"nihao"}--数字,字符串
str=table.concat( t2, ",",1,2 )
print(str)

多脚本执行

bash 复制代码
print("*********多脚本执行*********")
print("*********全局本地变量*********")
for i=1,2 do
	c="xzc"--全局变量
	local d = 1--局部变量
end
print(c)
print(d)
print("*********多脚本执行*********")
--关键字require("脚本名")
print(aa)
require("first")--执行过了就可以使用另外一个脚本的访问权限满足的东西
require("first")--不能多次执行
print(require("first"),"返回值")--还可以得到另外脚本返回值
print(aa)
print(bb)
print("*********脚本卸载*********")
--关键字package.loaded["脚本名"]可以得到返回值判断是否被加载
print(package.loaded["first"])
package.loaded["first"]=nil
print(package.loaded["first"])
print("*********大G表*********")
--大G表(_G)是一个总表table,会把所有全局变量都存储
--for k,v in pairs(_G)do
--	print(k,v)
--end
bash 复制代码
aa="123"
local bb = 1
print("first")
return 'ok'

Lua的特殊用法

bash 复制代码
print("*********特殊用法*********")
print("*********多变量赋值*********")
a,b,c,d=1,true,"你好"--多变量为空,少变量取前
print(a,b,c,d)
print("*********多返回值*********")
function z()
	return 1,2,3
end
a,b,c,d=z()
print(a,b,c,d)--多变量为空,少变量取前
print("*********and or*********")
--不止链接bool,只有nil,false是假,反正会程、返回代表真假的东西而不是true,false
print(nil and 1)
print(true and 1)
print(nil or 1)
print(nil or false)
print(false or nil)
--模拟三目运算符
xx=(1>3)and"ok"or"no"
print(xx)
xx=(1<3)and"ok"or"no"
print(xx)

协程

bash 复制代码
print("*********协程*********")
print("*********协程创建*********")
function a()
	print(123)
end
--coroutine.create(),返回线程类型
co = coroutine.create(a)
print(co,type(co))
--coroutine.wrap(),返回函数类型
co2 = coroutine.wrap(a)
print(co2,type(co2))

print("*********协程运行*********")
--coroutine.resume()
coroutine.resume(co)
--直接调用函数
co2()
print("*********协程挂起*********")
function b(  )
	local i=1
	while true do
		print(i)
		i=i+1
		print(coroutine.status(co3))
		coroutine.yield(i)
	end
end
--方式一:
co3=coroutine.create(b)
isok,re=coroutine.resume(co3)--第一个返回值是携程是否成功,第二个是返回值
print(isok,re)
isok,re=coroutine.resume(co3)
print(isok,re)
--方式二:
co4=coroutine.wrap(b)
co4()
re=co4()
print("返回值"..re)--第一个返回值就是写的返回值
print("*********协程状态*********")
--coroutine.status()
--dead结束
--suspended暂停
--running运行中
print(coroutine.status(co))
print(coroutine.status(co3))
--还有函数可以得到正在运行协程的线程号
print(coroutine.running())

元表

bash 复制代码
print("*********元表*********")
print("*********元表概念*********")
--任何表变量都可以作为另一个表变量的元表
--任何表变量都能拥有自己的元表(父)
--当我们子表中进行特定操作时,会执行元表内容
print("*********设置元表*********")
meta={}
myTable={}
--设置元表函数
--第一个参数(子表)。第二个参数(元表)(父)
setmetatable(myTable,meta)
print("*********特定操作*********")
print("*********特定操作__tostring*********")
mate2={
	--当子表要被当string使用时,会默认调用原表的__tostring()方法
	__tostring=function(t )--默认将自己作为第一个参数赋值
		return t.name
	end
}
myTable2={
	name="xzc"
}
setmetatable(myTable2,mate2)
print(myTable2)
print("*********特定操作__call*********")
mate3={
	--当子表要被当string使用时,会默认调用原表的__tostring()方法
	__tostring=function(t)--默认将自己作为第一个参数赋值
		return t.name
	end,
	--当子表要被当函数  使用时,会默认调用原表的__call()方法
	__call=function(a,b)--默认将自己作为第一个参数赋值
		print(a)
		print("oi"..b)
	end
}
myTable3={
	name="xzc"
}
setmetatable(myTable3,mate3)
myTable3("你好")
print("*********特定操作__运算符重载*********")
mate4={
	__add=function(a,b)
		return a.age+b.age
	end,
	__sub=function(a,b)
		return a.age-b.age
	end,
	__mul=function(a,b)
		return a.age*b.age
	end,
	__div=function(a,b)
		return a.age/b.age
	end,
	__mod=function(a,b)
		return a.age%b.age
	end,
	__pow=function(a,b)
		return a.age^b.age
	end,
	__eq=function(a,b)--比较
		return a.age==b.age
	end,
	__lt=function(a,b)
		return a.age<b.age
	end,
	__le=function(a,b)
		return a.age<=b.age
	end,
	__concat=function(a,b)--..
		return "a.age..b.age"
	end,


}
MyTable4={

	age=10
}
setmetatable(MyTable4,mate4)
print(MyTable4+MyTable4)
--条件运算符的重载要求两个表的元表一致
print("*********特定操作------index和newIndex*********")
---当表找不到属性会去找元表中__index指定的表的属性(得)
mate6 ={
	age = 1
	--__index={age = 1}
}
--下面这个代码要是写内部,会有坑,因为声明的同时不能立马用
mate6.__index=mate6
myTable6={

}
setmetatable(myTable6,mate6)
print(mate6.age)
--当赋值时,如果赋值一个不存在的索引,那么会把责怪值赋值到newubdex所指表中,不改自己(改)
mate7={}
mate7.__newindex={age=1}
MyTable7={

}
setmetatable(MyTable7,mate7)
MyTable7.age=11
print(mate7.__newindex.age)
print("*********其他操作*********")
--获取元表
print(getmetatable(MyTable7))--对标settmetatable
--忽视__index设置,只能自己表内找
print(rawget(MyTable7,"age"))
--忽视__newindex设置,如果表没有直接给表加上没有的
rawset(MyTable7,"age",99)
print(MyTable7.age)

面向对象

bash 复制代码
print("*********面向对象*********")
print("*********封装*********")
--调用时候用":",self赋值。声明时候":"提供默认self防止没人:调用,但是要用self.
Object={}
Object.id=1--添加成员变量
--实现封装一个类,可以被new
function Object:new()--添加成员函数
	local obj = {}
	--元表
	self.__index=self
	--self.__newindex=self//只允许得父类,不允许改父类,改的话就自己私有的了
	setmetatable(obj,self)
	return obj
end
function Object:hs()
print(self.id)
end
local myObj=Object:new()
myObj.id=10
myObj:hs()
print(Object.id)
print("*********继承*********")
--写一个继承方法.创建一个表并且继承:调用者
function Object:subClass(className)
	_G[className]={}--声明一张全局表
	local obj=_G[className]
	self.__index=self
	obj.base=self--自己定义base保留父类
	setmetatable(obj,self)
end
Object:subClass("Person")--我们对于冒号声明的函数都要注意预留第一个参数,除非也是冒号调用
x=Person:new()
print(x.id)
print("*********多态*********")
--相同方法不同执行逻辑
Object:subClass("GameObject")
GameObject.posX=0;
GameObject.posY=0;
GameObject.posZ=0;
function GameObject:Speak()
	print(self.posX,self.posY,self.posZ)
end
GameObject:subClass("Player")
function Player:Speak()
--self.base:Speak()--相当于传递的Gameobject的,改同一个父类
self.base.Speak(self)
print("重写")
end
yy=Player:new()
yy:Speak()
yx=Player:new()
yx:Speak()

封装面向对象

bash 复制代码
--面向对象实现
--基类Object
Object={}
--new()方法->体现封装
function Object:new( )
	--声明空表
	local obj={}
	--关联self表为元表,设置元表的__index属性
	self.__index=self
	setmetatable(obj,self)
	--返回链接好元表的空表
	return obj
end
--继承方法->体现继承
function Object:subClass( className )
	--使用大G表声明一个全局的表,创建一个继承某类的类
	_G[className]={}
	local obj=_G[className]
	--关联self表为元表,设置元表的__index属性
	self.__index=self
	setmetatable(obj,self)
	--为新诞生类添加一个成员属性,存储父类,以便实现多态
	obj.base=self
end

Object:subClass("GameObject")
GameObject.x=0
GameObject.y=0
function GameObject:SP(  )
	self.x=1
	self.y=9
end
go=GameObject:new()
print(go.x,go.y)
go:SP()
print(go.x,go.y)
GameObject:subClass("Player")
function Player:SP()
	print("重载SP:")
	self.base.SP(self)--得吧self传第一个参数,而不是base,子类使用父类方法,而不是直接拿父类方法
	self.x=self.x+1
	self.y=self.y+1
end
p=Player:new()
print(p.x,p.y)
p:SP()
print(p.x,p.y)

自带库

bash 复制代码
print("*********自带库*********")
print("*********时间*********")
-- 1. 时间戳
print("当前时间戳:", os.time())
print("指定时间戳(2024-08-14):", os.time({year=2024, month=8, day=14}))

-- 2. 格式化获取当前时间
local currentDate = os.date("%Y-%m-%d") -- 年-月-日
local currentTime = os.date("%H:%M:%S") -- 时:分:秒
local fullTime = os.date("%Y-%m-%d %H:%M:%S") -- 完整时间(年-月-日 时:分:秒)

print("\n当前日期:", currentDate)
print("当前时间:", currentTime)
print("完整时间:", fullTime)

-- 3. 单独获取每个时间字段(按需提取)
local year = os.date("%Y")
local month = os.date("%m")
local day = os.date("%d")
local hour = os.date("%H")
local min = os.date("%M")
local sec = os.date("%S")
local week = os.date("%w") -- 星期(0=周日,1=周一,...,6=周六)

print("\n单独字段:")
print("年:", year)
print("月:", month)
print("日:", day)
print("时:", hour)
print("分:", min)
print("秒:", sec)
print("星期(0=周日):", week)

print("\n*********数学*********")
-- 数学库常用示例
--先设置随机数种子
math.randomseed(os.time())
print("随机数(0-1):", math.random())
print("随机数(1-100):", math.random(1, 100))
print("绝对值:", math.abs(-456))
print("平方根:", math.sqrt(25))
print("最大值(3,7,2):", math.max(3, 7, 2))
print("最小值(3,7,2):", math.min(3, 7, 2))
print("圆周率:", math.pi)
print("正弦值(π/2):", math.sin(math.pi/2)) -- 结果接近 1
print("弧度转角度",math.deg(math.pi))
print("向上取整",math.floor(2.6))
print("向下取整",math.ceil(2.6))
print("小数分离",math.modf(1.2))
print("\n*********路径*********")
--lua脚本加载路径
print(package.path)
package.path=package.path.."C:\\"
print(package.path)

垃圾回收

bash 复制代码
print("*********垃圾回收*********")
--关键字collectgarbage
--获取当前lua占用内存数kb,返回值*1024=字节
print(collectgarbage("count"))
test={id=20,name="你好"}
print(collectgarbage("count"))
--垃圾回收(对标GC)
test=nil--解除羁绊就是变垃圾
collectgarbage("collect")
print(collectgarbage("count"))
--lua有自动计时进行GC方法

3.简单闭包:定义+意义

1. 定义

闭包 = 内部函数 + 捕获的外部函数局部变量

核心特点:内部函数被返回后,依然能访问/修改外部函数的局部变量(这些变量不会随外部函数执行结束而消失)。

lua 复制代码
function 外部函数()
    local 局部变量 = 0  -- 外部函数的局部变量
    return function()  -- 返回内部函数(闭包)
        局部变量 = 局部变量 + 1
        print(局部变量)
    end
end

local 闭包实例 = 外部函数()
闭包实例()  -- 输出1(记住了局部变量)
闭包实例()  -- 输出2(继续修改局部变量)

2. 意义

核心意义:让函数带"记忆",同时不污染全局变量

用大白话讲2个关键作用:

  1. 不用全局变量,也能让函数记住之前的状态(比如计数器、累加器);
  2. 保护变量不被随意修改(外部只能通过闭包接口操作,不能直接改)。

3.对比:不用闭包的麻烦

如果不用闭包,想实现计数器只能用全局变量:

lua 复制代码
local 全局变量 = 0  -- 容易被其他代码误改
function 计数()
    全局变量 = 全局变量 + 1
    print(全局变量)
end

而闭包能让"状态"(局部变量)和"操作"(内部函数)绑在一起,既安全又干净。

相关推荐
为你写首诗ge5 小时前
【Unity知识分享】Unity中获取Pico设备的序列号(SN码)
unity
B0URNE7 小时前
什么是虚拟现实(VR)?
unity·ue5·vr
B0URNE16 小时前
【Unity基础详解】Unity3D全程学习路线
学习·unity·游戏引擎
一步一个foot-print20 小时前
[Unity Shader Base] RayMarching in Cloud Rendering
unity·游戏引擎
ithinking11020 小时前
kotlin 集成 unity
unity·android studio
立刀人1 天前
关于Unity 轴心点 Pivot、锚点 Anchor和控制轴
unity·游戏引擎
Hody912 天前
【XR开发系列】Unity第一印象:编辑器界面功能布局介绍(六大功能区域介绍)
unity·编辑器·xr
FAREWELL000752 天前
Lua学习记录(3) --- Lua中的复杂数据类型_table
开发语言·学习·lua
lrh30252 天前
Custom SRP - 14 Multiple Cameras
unity·渲染管线·srp