unity3d游戏运行时lua热重载
上个项目体验到了关掉界面修改lua代码就会生效的便捷,这两天趁没什么紧急事项就在现在项目也加上了这个支持。
核心思想
核心思想就是
package.loaded[fileName]=nil;
require(fileName)
过程
当前项目lua文件引用有好几种:
第一种是路径作为参数传入一个叫luawindom或luaitem的cs文件,通过slua的luastate的start方法引用,也就是dofile.
第二种是传统的写在固定文件里,登录的时候统一require,
第三种是分布在各个业务系统里自己require的一些文件,也就是没按照规则放在统一加载地方的文件。
首先重载第一种方式载入的lua文件,由于当前项目目前关闭界面只是隐藏界面,并不是销毁,不好跟之前项目一样搞成按关闭界面就重载,那个载入lua文件的start函数也只会执行一次,由于每次都是隐藏,所以不会正常程序是不会再执行的,那个luaitem也是这样。我选择的方案是加一个重载按钮,点了重载就加一个标志位,如果这个界面是标志过的那就直接destroy,由于它destroy也会遍历本身包含的luaitem,所以这两个就一并解决了,非常简单。
接着,如果不是作为路径传给luaWindow或luaitem的lua文件所有人都是按照规则把require写在固定文件夹那就好办了,直接通过重载按钮传参重新require指定的文件就行,但固定文件里只有部分文件,只require那部分得不到想要的效果,所以我把第二种和第三种情况合并处理。
我观察项目目录,发现那些不按规则require的文件基本都是在对应功能的目录或者子目录下,例如活动界面require了一个item1,item2,他们目录通常都是act/item1或者act/sub/item2。所以当重载界面时,选择重载这个界面所在目录的所有lua文件是一个不错的方案,能保证跟这个界面相关的文件都重载(如果有奇葩把其他功能模块的lua文件在不对应的界面require这种情况就不理了)。
接下来就很简单了,在重载按钮里调用lua重载就可以了,slua是这么写:
LuaTable luaData = luastate.start("你的目录/hotRequire.lua");
LuaFunction f = (LuaFunction)luaData["RefreshLua"];
if (f == null)
{
return;
}
/*path为当前文件所在目录,得根据自己项目目录做处理,要保证它是当前模块的父目录,
例如lua/activity,保证当前活动相关的文件都在这下面*/
f.call(path);
接着是全文的重点,这次遇到的坑。
坑
1.这个项目用不了lfs,require “lfs”会报字符串无法转换,查了下说是.netframework版本问题或编码问题,我试了下设置package.cpath和重新换一个lfs库都不行,我不知道怎么改dll编码,也不能因为我这个东西而修改项目.netframewrok设置,所以只能不用lfs来遍历文件夹,能用的情况就不会有下面的坑。
2.在不能用lfs的情况,你去查资料会查到可以用os.execute或者io.popen函数来执行系统命令进行遍历,这里有个特别坑的地方,就是windows下的路径“/”要替换成“||”,不然执行遍历目录的命令根本不会生效,我是在cmd运行命令才发现这一点的,我查到所有资料都没说,我真不怀疑他们是不是能真的运行,这卡了我一整天时间,这个问题解决后还要找到正确的系统指令,网上搜的系统指令乱七八糟的,经过一番搜索和尝试才找到了正确的指令,至此就能遍历整个lua文件夹里的lua文件,然后利用核心思想重载他们。
代码:
hotReqire = {}
function main()
return hotReqire
end
hotRequire.RefreshLua = function(path)
hotRequire:FindPath(path)
end
function hotRequire:FindPath(path)
--只输出文件
local cmd1 = "dir"..string.gsub(path,"/","\\").." /b /a-d"
--只输出目录
local cmd2 = "dir"..string.gsub(path,"/","\\").." /b /ad"
local handle1 = io.popen(cmd1)
local handle2 = io.popen(cmd2)
if handle then
for line in handle:lines() do
if string.find(line,"lua") and not string.find(line,"meta") then
package.loaded[line]=nil;
require(line)
end
end
end
if handle2 then
for line in handle2:lines() do
local subPath = string.gsub(path,"/","\\").."\\"..line
hotRequire:FindPath(subPath)
end
end
handle1:close()
handle2:close()
end
这就是所有代码,按实际项目做路径拼接对应修改应该就能生效。
我踩的坑是用不了lfs,然后不用lfs遍历在lua遍历文件夹,这个东西并不复杂,主要还是要了解自己当前项目是怎么载入lua的,根据项目情况来进行处理。