【Lua运行时热重载①】检测Lua文件发生变化
这几天由于项目组需要一直在琢磨一个功能,就是如何在 unity编辑器下不需要重启游戏就能让lua文件改动后立刻生效 。如果能够实现这个功能,那会大幅提高开发效率。查了一圈,网上的结果都不太满意,要么只有理论没有源码,要么有源码但是考虑的情况过于简单。所以自己打算写博客告诉大家,我是怎么实现的,并且提供完整源码。 github工程地址
使用的unity2019.3.0 + xlua。改成其他lua也是可以用,只要在传入luaEnv的时候做相应改动就可以了。
这个功能大体分为两大步:
- 检测哪些lua文件发生变化
- 重新加载lua模块,保留数据,替换函数 (因为我们做了函数、数据分离,所以我这个工程目前只考虑替换函数)
其中重载lua模块还要考虑以下问题:
- 其它模块缓存了旧模块的函数的处理
- upvalue值的处理
- 需要更新的模块的元表的处理
- 对于正在运行的函数的处理,比如update
内容有点多,一篇文章应该塞不下,所以这第一篇先讲第一步, 怎么检测哪些lua文件发生变化。 涉及到工程里两个类:
- DirectoryWatcher ,检测文件变化
- LuaFileWatcher ,处理检测文件发生变化后该做什么事情
DirectoryWatcher
public class DirectoryWatcher
public DirectoryWatcher(string dirPath, FileSystemEventHandler handler)
Debug.Log("create directory watcher");
CreateWatch(dirPath, handler);
void CreateWatch(string dirPath, FileSystemEventHandler handler)
if (!Directory.Exists(dirPath)) return;
var watcher = new FileSystemWatcher();
watcher.IncludeSubdirectories = true; //includeSubdirectories;
watcher.Path = dirPath;
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = "*。lua";
watcher.Changed += handler;
watcher.EnableRaisingEvents = true;
watcher.InternalBufferSize = 10240;
这里涉及到一个C#的系统类 FileSystemWatcher
FileSystemWatcher
监控指定文件或目录的文件的创建、删除、改动、重命名等活动。可以动态地定义需要监控的文件类型及文件属性改动的类型。
- IncludeSubdirectories 是否包含子文件。
- Path 目标路径。
- NotifyFilter 设置文件的哪些属性的变动会触发 Changed事件。这里设置成了当文件内容发生变化时会触发。
- Filter 设置筛选字符串,用于确定在目录中监视哪些类型的文件。这里只需要筛选 .lua后缀文件 即可。
- Changed 文件发生改变时的监听事件,需要一个 FileSystemEventHandler 类型的委托。除了 Changed 外还可以监听 Renamed 、 Deleted 、 Created 。
- EnableRaisingEvents 设置是否开始监控,默认为false
- InternalBufferSize 能够监听的改动大小。如果监听事件没有触发,请把这个值设得大一点。 还有一些其他属性,详细看 MSDN关于FileSystemWatcher
LuaFileWatcher
public class LuaFileWatcher
//private static ReloadDelegate ReloadFunction;
private static HashSet<string> _changedFiles = new HashSet<string>();
public static void CreateLuaFileWatcher(LuaEnv luaEnv)
var scriptPath = Path.Combine(Application.dataPath, "LuaScripts");
var directoryWatcher =
new DirectoryWatcher(scriptPath, new FileSystemEventHandler(LuaFileOnChanged));
//ReloadFunction = luaEnv.Global.Get<ReloadDelegate>("hotfix");
EditorApplication.update -= Reload;
EditorApplication.update += Reload;
private static void LuaFileOnChanged(object obj, FileSystemEventArgs args)
var fullPath = args.FullPath;
var luaFolderName = "LuaScripts";
var requirePath = fullPath.Replace(".lua", "");
var luaScriptIndex = requirePath.IndexOf(luaFolderName) + luaFolderName.Length + 1;
requirePath = requirePath.Substring(luaScriptIndex);
requirePath = requirePath.Replace('\\','.');
_changedFiles.Add(requirePath);
private static void Reload()
if (EditorApplication.isPlaying == false)
return;
if (_changedFiles.Count == 0)
return;
foreach (var file in _changedFiles)
//ReloadFunction(file);
Debug.Log("Reload:" + file);
_changedFiles.Clear();