本文分享Unity中使用Lua时的动态刷新方案
相信Lua的使用大家已经不陌生了, 对于那些大部分逻辑使用lua的项目来说, 我们可以使用一个小技巧来提高日常的开发效率.
Lua文件的加载
Lua本身是解释执行的语言, 和C那些编译执行的语言不一样, 存在形式只是一个文本(当然也可以使用luajit预先编译).
一个Lua文件被称之为Chunk, 可以理解为是一个模块, 代码块.
使用require加载Lua文件之后, 会返回一张lua表或者返回true和false, 当然, 这取决于Lua文件的最后一个语句的写法.
------------------------------------
-- test1.lua
local t = {}
return t
------------------------------------
-- test2.lua
local t = {}
------------------------------------
-- main.lua
local t1 = require("test1")
local t2 = require("test2")
------------------------------------
print(t1)-- table: 000002AF947DDDE0
print(package.loaded.test1) -- table: 000002AF947DDDE0
print(t2) -- true
print(package.loaded.test2) -- true
如果Lua文件有返回值, require则返回该值, 并将该值存储在package.loaded表中, 表的key为lua文件名字(不带后缀).
如果Lua文件没有返回值, 且在require时没有出错, 则返回true, 并将true存储在package.loaded表中, 表的key为lua文件名字(不带后缀).
当我们再次使用require加载一个已经加载过的模块时, 会在package.loaded中查找, 如果存在则返回已经存储的值.
所以实际上只有第一次使用require时才真正的加载了模块, 后续的加载只是返回之前存储的值而已.
如果我们需要真正的进行重新加载, 需要先将package.loaded表中的值删除才行.
Lua文件的动态刷新
如果项目的大部分逻辑是由Lua编写, 我们不需要每次在修改之后进行: “停止Editor”=>“启动Editor”=>“经过一系列进入流程”=>“验证修改”. 只需要"刷新Lua"=>“验证修改”. 省去了前面的一系列流程, 从而提高效率.
这里的"刷新Lua"操作本身其实很简单, 就是将Lua模块从package.loaded中移除, 然后重新require即可.
我们可以将这个过程做成一个editor扩展, 使用快捷键一键搞定.
当然, 我们需要一个刷新方法, 一个刷新模块文件和一个编辑器扩展方法:
[MenuItem("Lua动态刷新 %#R")]
private static void RefreshLua()
{
if (!Application.isPlaying) return;
XLuaStart.Instance.DoString(string.Format("if RefreshModule then RefreshModule() end"));
}
function RemoveRequireCache(moduleName)
package.loaded[moduleName] = nil
end
function RefreshModule()
RemoveRequireCache("RefreshModuleFile")
require("RefreshModuleFile")
end
------------------------------------
-- RefreshModuleFile.lua
RemoveRequireCache("test1")
local t = require("test1")
然后使用快捷键"Shift+Ctrl+R"就能进行刷新, 可以在RefreshModuleFile写测试代码来验证结果.
要注意的问题
在使用动态刷新的时候, 要注意需要刷新的模块是否存在全局引用, 比如:
- 大部分情况下, 我们一般是将Lua脚本绑定到对应的预制, 然后在Lua中操作预制, Lua和预制存在引用
- require之后的赋值给了一个全局的变量, 然后在业务代码中使用该变量
这些情况都需要先将引用移除, 不然使用的依然是之前存储的值.
说在最后
动态刷新的应用是很灵活的, 很多服务器也是通过这一特性来达到不重启的前提下更新内容.
本文分享的方案只是起到一个抛砖引玉的作用, 希望大家能够结合自己的需求来使用, 提高效率.
整个方案其实非常简单, 没什么更多的可以讨论的内容.
本来作者觉得这个方案应该很多项目都在使用, 并没有单独分享的必要.
作者进入当前的项目组后发现大家并不了解这方面的内容, 很多小伙伴之前可能没有研究过脚本语言的特性, 依然按照C#开发的模式来使用Lua, 没用充分将Lua的优势发挥出来.
而且我们的项目比较大, 每次重启, 进入目标点起码要花费2分钟以上, 日积月累下来浪费的时间是很恐怖的.
所以在和项目组的小伙伴们分享后, 也决定发布到网上, 希望对大家有所帮助.