|
XLua在安卓端我遇到的那些事
- 在安卓中读写,我起初是用的c#的方式(File.WriteAllText与File.ReadAllText),死活写入不了,于是我想到我为啥要舍近求远呢,Lua不一样也能读写吗? 于是请看代码
- 需要注意在使用Application.persistentDataPath路径用lua写入时会遇到一个错误"LuaException: xxx:36: cannot open file '......./xxx.json' (No such file or directory)" 原因我也不是很清楚,这个问题我用c#解决的(也就是判断一下平台,如果时pc就用c#的file读写,如果时安卓就用lua的读写)
--写 写完记得关闭
local filePath = Application.persistentDataPath .. "/Archive/xxxx.json"
local file2=io.output(filePath ) --不需要文件存在
io.write(jsonData)
io.flush()
io.close()
--读
local file1=io.input(filePlayerDataPath) --不需要文件存在
local str=io.read("*a") -- *a 意思是读取全部
local playerDataByJson = Json.decode(str) --用json工具类转成表
-------Application.persistentDataPath在pc用lua读写报错解决方案
if Application.platform == CS.UnityEngine.RuntimePlatform.Android then
local file2=io.output(filePlayerDataPath)
io.write(jsonData)
io.flush()
io.close()
else
File.WriteAllText(filePlayerDataPath,jsonData)
end
1
Lua基础知识
- 数据类型
- 简单的复杂类型 (使用没有声明过的变量,不会报错,默认值为nil)
- nil (空)
- number (数值类型)
- string (lua中没有字符与字符串之分)
- # 获取字符串的长度,英文占1个长度,汉字占3个长度
- 多行打印
str = [[ 我
是
小
小
酷 ]]
- 字符串拼接 .. (只有用了都是string拼接)
- boolean
- 复杂的数据类型
- 函数 function
- 表 table
- 数据结构 userdata
- 协同程序 thread(线程)
- 运算符
- 算术运算符 + - * / % ^(幂运算)
- 没有自增自减 ++ --
- 没有符合运算符 += -= *= /= %=
print("123"+1) 打印124 为什么呢 lua会将字符串转成数值类型然后相加 还有个原因lua中拼接符号为..
而c#中 会打印1231 它会认为这个+号是字符串拼接 而非算术运算符
- 条件运算符 > < >= <= == ~=(不等于)
- 逻辑运算符 and(与) or(或) not(非) 支持短路
- 它们不仅能连接boolean表达式,还能连接任意东西(如:1 and 2)。当用and连接非boolean表达式时返回and关键词右边的表达式,用or连接则返回or左边的表达式(这里潜藏一个&#34;短路&#34;的知识,结合下面那句,and:判断左边如果不等于nil与false,则继续判断右边,如果2边都为真返回右边的值)
- 在lua中 只有nil 和 false 才认为时假
- 位运算符 不支持
- 三目运算符 不支持但能这么写
a= 3
b= 2
--如果a大于b(逻辑与有假则假),那么它会用a or b(结合在连接非boolean表达式) 所以它会返回a
--否则返回b
c= (a>b) and a or b
print(c)
a=9
if a>3 then
print(&#34;3&#34;)
elseif a>5 then
print(&#34;5&#34;)
else
print(&#34;0&#34;)
end
for i=1,5 do
print(i) --1-5
end
for i=1,5,2 do
print(i) --1,3,5
end
num = 0
while num < 5 do
print(num)
num = num + 1
end
- do while (until 是结束条件而非进入条件 也就是说满足条件我就跳出循环)
repeat
print(num)
num = num + 1
until num > 5
function F1()
print(&#34;函数F1&#34;)
end
F1()
- 有参数无返回值 (不支持默认值也就是给参数赋值)
- 当传入参数不匹配时,不会报错 只会补空nil 或者 丢弃 (传入参数多于需要参数)
function F2(a)
print(&#34;有参数的函数F2:&#34;.. a)
end
F2(2)
- 有参数有返回值 (支持多返回值)
- 如果接收变量不够或接收变量过多,会直接丢弃多于的返回值 或 会给个nil值,不会报错
function F3(a)
return a
end
temp = F3(1)
--多返回值
function F3(a)
return a,&#34;test&#34;,true
end
temp,temp1,temp2 = F3(1)
- 函数的类型 就是 function
- 函数的重载 不支持重载 默认调用最后声明的函数
- 变长参数(...) 第一行的参数列表与第二行的表是固定写法
function F4( ... )
arg = {...}
for i=1,#arg do
print(arg)
end
end
F4(1,3,2,32)
function F5()
return function()
print(&#34;333&#34;)
end
end
f5 = F5()
f5()
function F5(x)
return function(y)
return x+y
end
end
f5 = F5(10)
print(f5(5))
- table(表) 索引从1开始
- 数组 a = {1,3,4,1,&#34;Test&#34;,true,nil}
- 奇怪的现象
- 当表的末尾 为nil时,其他任意位置为nil会影响到#获取长度(1与2位置不会影响)。栗子: a={1,2,3,nil,5,6,7,nil} #a 输出3 | a={1,2,nil,4,5,6,nil} #a 会输出2
- 打印长度时空(nil)会被忽略
- 二维数组与二维数组的遍历
a = {{1,2,3},{4,5,6}}
for i=1,#a do
b=a
for j=1,#b do
print(a[j])
end
end
- 自定义索引
- 获取长度时,会优先计算非自定义索引,如果自定义索引能续接非自定义索引,长度继续计算,续接不能超过1 ,也就是说索引只要按照顺序1增长都视为有效长度。如果当前表的索引按顺序进行排列(包括自定义索引),中间索引可以断隔1个长度,还能被计算(1,2,4,6 这里的长度为6 它中间会给你填个nil值)
aa={[1]=1,2,3,[-1]=4,5} --长度为3
aa={[4]=1,2,3,[-1]=4,[5]=5,[3]=&#34;21&#34;} --长度为5
aa={[4]=1,2,3,[-1]=4,[5]=5} --长度为2
aa = {[1] = 1,[2]=2,[4] = 4,[6] = 6} --长度为6
print(#aa)
- ipairs 迭代器遍历
- 从1开始的顺序遍历,只要顺序没有断开都会进行遍历(不管索引的先后顺序)
a={[0]=1,2,[-1]=3,4,5,[5]=6,[4]=3}
for i,v in ipairs(a) do
print(i .. &#34;_&#34; ..v)
end
-[[
输出如下
1_2
2_4
3_5
4_3
5_6
]]-
a={[0]=1,2,[-1]=3,4,5,[5]=6,[4]=3}
for k,v in pairs(a) do
print(k .. &#34;_&#34; .. v)
end
-[[
输出
1_2
2_4
3_5
0_1
4_3
-1_3
5_6
]]-
- 字典
- 字典由key value构成
- 访问值
- 通过中括号 如:print(a[&#34;name&#34;])
- 通过 . 成员变量的形式,但不能是数值 如:print(a.name)
- 声明
a={[&#34;name&#34;]=&#34;小小酷&#34;,[&#34;age&#34;]=18,[&#34;1&#34;]=3}
print(a[&#34;name&#34;]) --输出 :小小酷
print(a.name) --输出 :小小酷
print(a.1) --会报错
--增
a.test=&#34;test&#34; -- 还可以用 a[&#34;test&#34;] = &#34;test&#34;
print(a.test)
--改
a.name = &#34;xxk&#34;
print(a[&#34;name&#34;])
--删
a.test = nil
print(a.test) --打印 nil
Student={
age=1,
sex=true,
Up = function()
print(age) --跟Student表中age没有关系,它是一个全局变量
Student.age = Student.age+1
print(Student.age)
end
}
--调用 有点类似于c#中的静态成员方法和静态成员变量
Student.Up()
--添加跟字典类似
Student.xxx = &#34;1&#34;
Student.Speak = function() print(&#34;我说话了&#34;) end
function Student.Speak() print(&#34;我又说话了&#34;) end
function Student:Speak()
print(self.age .. &#34;岁!&#34;)
end
Student:Speak()
- &#34;.&#34; 调用有参数必须传参数与 &#34;:&#34; 默认会把调用者传给第一个参数
- self 表示第一个传入参数 如:function Student:Speak() print(self.age .. &#34;岁!&#34;) end
- 表的公共操作
- table.insert(t1,t2) 将t2表插入到t1表的最后
- table.remove(t1) t1为操作表,第二个参数传入指定位置,否则删除最后一个
- table.sort
aa = {5,2,9,7,6,3}
table.sort( aa ) --升序排序
for _,v in pairs(aa) do
print(v)
end
table.sort( aa,function(a,b) --降序排序
if(a>b) then --可以理解为交换位置
return true;
end
end )
for _,v in pairs(aa) do
print(v)
end
- table.concat(拼接) 要传入操作表,第二个参数为连接字符(123:456 中间的&#34;:&#34;就叫连接字符)
- Require
- 多脚本执行 关键字require(&#34;lua脚本名&#34;) 单引号与双引号都行,互相require会报错
- 全局变量和本地变量 不加local的为全局变量,加local的为本地变量
- 可以在脚本里面返回一个任意类型,然后接收使用,如果不使用返回默认会返回一个true
for i=1,2 do
local a = &#34;xxk&#34;
print(a) -- 输出xxk
end
print(a) --输出nil
-- ////////////////////////////
print(&#34;Test&#34;)
return &#34;TestLocal&#34;
--上下是2个不同的脚本
print( require(&#34;Test&#34;))
- 脚本卸载
- package.loaded[&#34;脚本名&#34;] 该脚本是否被执行 。 将它赋值nil就是卸载脚本(package.loaded[&#34;Test&#34;]=nil)
- 大G表 写法:_G 固定写法
- 它是一个总表,它将我们声明的所有全局的变量都存储在其中
- 多变量赋值 a,b,c=1,2 当后的值不够会自动补空,多了会自动省略
- 函数多返回值 return 10,20,30 用几个,就用几个变量接,返回值多了会自动省略,少了会自动赋值nil
- 协同程序(协程) 协程的本质是一个线程,类型是:thread
- 创建
- 使用coroutine.create创建后返回一个线程
fun = function()
print(123)
end
co= coroutine.create(fun)
print(co)
- 使用coroutine.wrap创建后返回一个函数
fun = function()
print(123)
end
co= coroutine.wrap(fun)
print(co)
- 运行
- 使用coroutine.create创建后用coroutine.resume运行
coroutine.resume(co)
- 使用coroutine.wrap创建后可以像调用函数一样调用
- 挂起
- coroutine.yield 它可以有返回值,第一个返回值为resume的返回值(也就是是否运行成功),第二个才是yield的返回值
fun = function()
local i = 1
while true do
print(i)
i = i + 1
coroutine.yield(i)
end
end
co= coroutine.create(fun)
isOK,tempI = coroutine.resume(co)
print(isOK,tempI)
- wrap 没有是否运行成功的返回值,也是可以有返回值的 跟函数的返回值获取方式一样
- 状态 coroutine.status获取当前协程的状态
- dead(结束)
- suspended(暂停)
- running(进行中)
- coroutine.running 获取线程号
- 元表
- 元表的概念
- 任何表变量都可以作为另一个表变量的元表
- 任何表变量都可以有自己的元表
- 当我们子表中进行一些特定操作时,会执行原表中的内容
- 设置元表 setmetatable(子表,元表(父表))
- 特定操作
- __tostring 当子表被当做字符串使用时,默认调用元表中tostring方法。想获取子表中的东西可以加参数,设置原表时会默认把子表传递进来
meta = {
__tostring = function()
return &#34;小小酷&#34;
end
}
myTable = {}
setmetatable(myTable,meta)
print(myTable) --打印:小小酷
--*****************
meta = {
__tostring = function(t)
return t.name
end
}
myTable = {
name = &#34;xxk&#34;
}
setmetatable(myTable,meta)
print(myTable) --打印:xxk
- __call 当子表被当做一个函数来使用时,默认调用call方法。第一个参数是子表本身,第二个开始的参数才是传入参数
meta = {
__tostring = function(t)
return t.name
end,
__call = function()
print(&#34;小小酷&#34;)
end
}
myTable = {
name = &#34;xxk&#34;
}
setmetatable(myTable,meta)
myTable() --打印:小小酷
--***************
meta = {
__tostring = function(t)
return t.name
end,
__call = function(t,a)
print(t) --打印:xxk
print(a) --打印:1
print(&#34;小小酷&#34;)
end
}
myTable = {
name = &#34;xxk&#34;
}
setmetatable(myTable,meta)
myTable(1) --打印:小小酷
- 运算符重载
- __add 当子表使用+运算符时,默认调用_add方法。
- 其他运算符关键词:
- __sub(-)
- __mul(*)
- __div(/)
- __mod(%)
- __pow(^)
- __eq(==) --条件运算符需要2个子表的元表一致 否则为false
- __lt(<) --没有> >= ~= 可以用取反(not)
- __le(<=)
- __concat(..)
meta = {
__tostring = function(t)
return t.name
end,
__call = function(t,a)
print(t) --打印:xxk
print(a) --打印:1
print(&#34;小小酷&#34;)
end,
__add = function(t1,t2)
return t1.name .. t2.name
end
}
myTable = {
name = &#34;xxk&#34;
}
setmetatable(myTable,meta)
myTable2 = { name = &#34;张三&#34;}
print(myTable + myTable2)
- __index和__newindex
- __index 当子表中找不到某一个属性时,会去元表中_index指定的表去找属性,会一层一层的往上找,如果找不到返回nil。建议写在外面。用于实现继承关系
meta = {
__index = { age=1}
}
myTable = {
}
setmetatable(myTable,meta)
print(myTable.age) --打印1
--*********************
meta = {
age = 1
}
meta.__index = meta
myTable = {
}
setmetatable(myTable,meta)
print(myTable.age) --打印1
- __newindex 当赋值时,如果赋值一个不存在的索引,那么会把这个值赋值到newindex所指的表中,不会修改自己
- 获取元表 getmetatable(传入子表)
- rawget(表,属性名) 在自身上找有没有这个变量,即使设置了index也不会去元表找
- rawset(表,属性名,值) 只会设置自身的变量,即使设置了newindex也不会去设置元表
- 面向对象三大特性实现
--元表相关的知识点
Object = {}
Object.id = 1
-- 冒号 自动将调用者作为第一个参数传入
function Object:new()
--新建一个表用于返回值
local obj = {}
--将传入的表设置index元表特性,如果当前表找不到,就会向上元表中找
self.__index = self
--设置元表
setmetatable(obj,self)
--返回子表
return obj
end
local myObj = Object:new()
print(myObj) --打印表的地址
print(myObj.id) --打印:1
function Object:SubClass(className)
--利用大g表创建全局表
_G[className] = {}
--获取从大g表中获取表
local obj = _G[className]
--设置index
self.__index = self
obj.base = self --写这句是为了在重写方法后保留父类的方法(能在子类调用父类的方法)
--设置元表,继承关系
setmetatable(obj,self)
end
Object:SubClass(&#34;myTable&#34;)
print(myTable.id)
Object:SubClass(&#34;GameObject&#34;)
GameObject.posX = 0;
GameObject.posY = 0;
function GameObject:Move()
self.posX = self.posX+1
self.posY = self.posY+1
print(self.posX)
print(self.posY)
end
GameObject:SubClass(&#34;Player&#34;)
function Player:Move( )
self.base:Move()
end
local p1 = Player:new()
p1:Move()
--*************************
GameObject:SubClass(&#34;Player&#34;)
function Player:Move()
--为什么为引用一个对象呢? 答案就在下面这句代码中
--首先p1与p2的Move中posX和posY实际使用的是GameObject
--所以这里得手动把自己传进去,这才符合面向对象下编程
self.base.Move(self)
end
local p1 = Player:new()
p1:Move() --打印:11
local p2 = Player:new()
p2:Move() --打印:22
- 自带库
- 时间 year(年) month(月) day(日) hour(时) min(分) sec(秒)
- os.time() 获取当前系统时间戳 或者 传入一张表转换成时间戳
print(os.time()) --打印当前系统的时间戳
print(os.time({year = 2021,month = 3,day = 1})) --输出:1614571200
- os.date(&#34;*t&#34;) 返回当前时间表
- 数学运算
- math.abs(-11) 绝对值
- math.deg(math.pi) 弧度转角度
- math.cos(math.pi) 三角函数 传弧度
- math.floor(2.6) 向下取整
- math.ceil(5.2) 向上取整
- math.max(1,2) 返回最大值
- math.min(4,5) 返回最小值
- math.modf(1.2) 小数分离 分成整数部分和小数部分 --打印:1 0.2
- math.pow(2,5) 幂运算 打印:32
- math.randomseed(os.time()) 设置随机数种子
- math.random(100) 获取随机数 先要设置随机数种子
- math.sqrt(4) 开方
- 路径
- package.path 获取路径列表
- package.path = package.path .. &#34;;c:\\&#34; 添加路径
- 垃圾回收 collectgarbage() lua中有自动定时进行gc的方法,在unity中热更新开发中尽量不要用自动垃圾回收
- collectgarbage(&#34;count&#34;) 获取当前lua占用内存数 k字节 用 返回值*1024 就可以得到具体的内存占用字节数
- collectgarbage(&#34;collect&#34;) 立即进行一次垃圾回收
- 函数(实用方法)
- type (获取类型) 返回值是string
- string.format(拼接字符串) 栗子:string.format(&#34;我是小小酷,今年%d岁了&#34;,18);
- %d : 与数字拼接
- %a :与任何字符拼接
- %s : 与字符配对
- tostring (转换成string)
- string.upper (将字符串转换成大写,不会改变原字符串)
- string.lower(将字符串转换成小写,不会改变原字符串)
- string.reverse (将字符串翻转,不会改变原字符串)
- string.find (查找字符索引,索引从1开始的)
- string.sub (截取字符串)
- string.rep (将字符串重复n次)
- string.gsub (字符串替换 会返回替换次数)
- string.byte (将字符转ASCII码)
- string.char (将ASCII码转成字符)
<hr/>AssetBundle基础
- AB包是什么
- 特定于 平台的资产压缩包,有点类似于压缩文件
- ab包包含:模型、贴图、预设体、音效、材质球等等
- AB包有什么作用
Resources与AB包的区别
热更新基本规则
- 生成AB包资源文件
- 自定义打包工具
- 官方提供:Asset Bundle Browser 可以去Github下载:
- AssetBundleBrowser参数相关
- 压缩方式
- LZMA 它压缩后包体最小 但缺点是用一个就得全部解压
- LZ4 它压缩后包体会比LZMA大一点点 用到什么资源解压什么资源
- AB包生成的文件(AssetBundleBrowser生成的)
- AB包文件 资源文件
- manifest文件
- AB包文件信息
- 当加载时,提供了关键信息
- 资源信息,依赖关系,版本信息等等
- 关键AB包(和目录名一样的包)
- AB包资源加载 同一名字的ab包不能加载多次
- 通过文件
- AssetBundle.LoadFromFile(ab包完整路径)
- AssetBundle.LoadFromFileAsync(ab包完整路径) 异步加载ab包 需要用协程加载
private IEnumerator Start()
{
//通过异步加载ab包 返回一个异步请求
var abCreateRequest = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath + &#34;/model&#34;);
//等待加载完成
yield return abCreateRequest;
//通过异步加载资源 返回一个异步请求
var abRequest = abCreateRequest.assetBundle.LoadAssetAsync(&#34;Cube&#34;,typeof(GameObject));
//等待加载完成
yield return abRequest;
//根据资源生成一个GameObject
Instantiate(abRequest.asset);
}
- AB的卸载
- abCreateRequest.assetBundle.Unload(false) 卸载ab包 参数是否卸载场景中用ab加载的资源
- AssetBundle.UnloadAllAssetBundles(false) 卸载所有ab包 参数是否卸载场景中用ab加载的资源
- AB包的依赖
- 关于AB包的依赖:一个资源用到了别的AB包的资源,如果不加载依赖包,就会引用丢失,如果加载就正常了,只要使用前一起加载了就没有问题。当然如果项目体积过大这样加载人都傻了
- 为了解决这个问题,可以用个ab包保存这些依赖关系(也就是利用主包,获取依赖信息)
- 在AssetBundleBrowser
//加载主包
var mainAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath + &#34;/PC&#34;);
//从主包中加载Manifest
var manifest = mainAB.LoadAsset<AssetBundleManifest>(&#34;AssetBundleManifest&#34;);
//获取所有的依赖并加载
manifest.GetAllDependencies(&#34;model&#34;).ToList()
.ForEach(_ => AssetBundle.LoadFromFile(Application.streamingAssetsPath + &#34;/&#34; + _));
<hr/>xLua热更新
- xLua配置与AB包相关准备
- 下载xlua可以去github
- 需要导入Assets下的Plugins和XLua文件夹
- 先点击一次XLua->clear generated code->generate code
- 在 Project Settings->Scripting Define Symbols 写入:HOTFIX_ENABLE
- 把下载的包里面的Tools 剪切到 项目根目录
- 点击hotfix inject in editor
- 测试环境可以 点击 clear generated code 在把Xlua目录中的Example文件夹删除
- 每次更新C#代码 都必须重新生成与注入一下代码 否则会报错
- 报try to dispose a LuaEnv with C# callback! 请把xlua.hotfix(CS.命名空间.脚本名,&#39;Update&#39;,nil)
- C# 调用 Lua
- Lua解析器
- DoString(执行lua代码) 参数 1执行lua代码 2当报错是抛出报错文件名
- Tick(清楚没有手动释放的对象 垃圾回收) 不要每帧执行 垃圾回收很好性能
- Lua文件加载重定向
- AddLoader() xlua提供的自定义加载lua文件的规则 给个加载委托,如下
...
var xluaEnv = new LuaEnv();
xluaEnv.AddLoader(Loader);
...
//fileName 是require执行的文件名
private byte[] Loader(ref string fileName)
{
var absPath = Application.dataPath + &#34;/Scripts/Lua/&#34; + fileName + &#34;.lua.txt&#34;;
return File.ReadAllBytes(absPath);
}
public class XLuaEnvMgr : Singleton<XLuaEnvMgr>
{
private XLuaEnvMgr()
{
}
private LuaEnv mXLuaEnv = null;
/// <summary>
/// 获取Lua环境
/// </summary>
public LuaEnv XLuaEnv
{
get
{
mXLuaEnv ??= new LuaEnv();
mXLuaEnv.AddLoader(Loader);
return mXLuaEnv;
}
}
/// <summary>
/// 得到Lua中的_G
/// </summary>
public LuaTable Global => XLuaEnv.Global;
private byte[] Loader(ref string fileName)
{
var absPath = Application.dataPath + &#34;/Scripts/Lua/&#34; + fileName + &#34;.lua.txt&#34;;
if (File.Exists(absPath)) return File.ReadAllBytes(absPath);
else throw new Exception($&#34;当前路径{absPath}不存在:{fileName}&#34;);
}
}
XLuaEnvMgr.Instance.Global.Get<int>(&#34;testNumber&#34;).Log();
XLuaEnvMgr.Instance.Global.Set(&#34;testNumber&#34;,33);
- 注意:直接使用require xlua会自动使用加载器去加载
-- Main
print(&#34;hello world&#34;)
require(&#34;Test2&#34;)
--Test2
print(&#34;Test2.lua&#34;)
testNumber =1
testString =&#34;xxk&#34;
testBool = true
--打印 hello world与Test2.lua
--lua代码
--无参无返
test1 = function()
print(&#34;无参无返&#34;)
end
--有参有反
test2=function(a)
print(&#34;有参有反&#34;)
return a+1
end
--多反
test3=function()
print(&#34;多反&#34;)
return 1,&#34;2&#34;,1.3,true
end
--变长参数
test4 = function(...)
print(&#34;变长参数&#34;)
local arg = {...}
for i, v in pairs(arg) do
print(k,v)
end
end
- 获取与修改都可以用下面的方法,当类型不确定是可以用object来装
XLuaEnvMgr.Instance.Global.Get<Action>(&#34;test1&#34;);
LuaFunction lf = XLuaEnvMgr.Instance.Global.Get<LuaFunction>(&#34;test1&#34;);
- 映射到List和Dictionary
- 映射到类
- 跟litjson使用差不多 变量名一样 私有和保护无法赋值
- 不需要全部定义 没定义的就不获取
- 映射到接口 接口中不能写字段 所以用属性实现
- 映射到LuaTable
- Lua 调用 C#
- 类
- 枚举
- 数组、List、Dictionary
- 函数(扩展方法)
- 函数(ref和out)
- 函数(重载)
- 委托和事件
- 特殊问题(二维数组遍历)
- 特殊问题(null和nil比较)
- 特殊问题(让系统类型能被Lua访问)
- 协程
- 泛型函数
- xlua 热补丁
- 第一个热补丁
- 多函数替换
- 协程函数替换
- 索引器和属性替换
- 事件替换
- 泛型类替换
- 代码的更新
[XLua.Hotfix]
public class Cube : UnityEngine.MonoBehaviour
{
private UnityEngine.Rigidbody mRB;
private void Start()
{
mRB = GetComponent<UnityEngine.Rigidbody>();
XLuaEnvMgr.Instance.XLuaEnv.DoString(&#34;require &#39;Test&#39;&#34;);
}
[XLua.LuaCallCSharp]
private void Update()
{
if (UnityEngine.Input.GetKeyDown(UnityEngine.KeyCode.W))
{
mRB.AddForce(UnityEngine.Vector3.up * 300);
}
}
}
//其中的XLuaEnvMgr是一个单例 XLuaEnv是一个属性 我在这个属性get中创建了LuaEnv 并 实现了Loader
private LuaEnv mXLuaEnv = null;
/// <summary>
/// 获取Lua环境
/// </summary>
public LuaEnv XLuaEnv
{
get
{
//如果为空则赋值
mXLuaEnv ??= new LuaEnv();
mXLuaEnv.AddLoader(Loader);
return mXLuaEnv;
}
}
//ref
private byte[] Loader(ref string fileName)
{
//Loader的路径
var absPath = Application.dataPath + &#34;/Scripts/Lua/&#34; + fileName + &#34;.lua.txt&#34;;
return File.ReadAllBytes(absPath);
}
private void OnDisable()
{
XLuaEnv.DoString(&#34;require &#39;LuaDispose&#39;&#34;);
}
private void OnDestroy()
{
mXLuaEnv?.Dispose();
}
xlua.hotfix(CS.命名空间.脚本名,&#39;Update&#39;,function(self)
if CS.UnityEngine.Input.GetKeyDown(CS.UnityEngine.KeyCode.D) then
self.mRB:AddForce(CS.UnityEngine.Vector3.up * 300)
end
end)
- 注册事件 带参数时需要加个静态的类型列表并使用特性CSharpCallLua 需要放到Editor目录下,如下
public static class CSharpCallLua
{
[XLua.CSharpCallLua]
public static List<Type> csharpCallLuaList = new List<Type>()
{
typeof(UnityEngine.Events.UnityAction<bool>)
};
}
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|