XLua教程之入门篇
简介
xLua 是腾讯开源的一个热更新解决方案,它为 Unity、.NET、Mono 等 C# 环境提供了与 Lua 脚本语言的无缝集成。其核心特点有:
支持热更新:在不重新发布客户端的情况下,修复 Bug 或更新游戏逻辑。
脚本化:将易变的业务逻辑用 Lua 编写,提高开发灵活性。
性能优秀:本身非常高效,且提供了强大的性能优化工具。
轻量高效:虚拟机轻量,绑定性能高。
无缝集成:C# 和 Lua 之间可以方便地互相调用,访问彼此的对象和函数。
强大的工具:提供 XLua.Hotfix
特性进行 C# 热补丁。
平台兼容:支持 Windows、macOS、Android、iOS 等全平台。
导入XLua
Github下载链接
https://github.com/Tencent/xLua
将下载的Xlua文件夹中的Plugins和XLua文件夹放入Assets文件夹中
查看左上角菜单栏是否有XLua按钮,说明是否导入成功
LuaEnv常用接口
在 C# 中,所有 Lua 操作都始于LuaEnv对象。
LuaEnv.Tick()
作用:手动触发 Lua 虚拟机的垃圾回收(GC)。在 Update
中调用 luaEnv.Tick()
,是使用 xLua 进行项目开发时一个非常重要的性能优化习惯。
说明:
Lua 的自动垃圾回收机制:
Lua 语言本身有一套自动垃圾回收机制,它会定期检查并释放不再使用的 Lua 对象(如 table、function、string 等)占用的内存。这个“定期”是由 Lua 虚拟机内部控制的,它根据内存分配的增长速度等启发式算法来决定何时运行。
xLua 在 Unity 中的默认行为:
在 xLua 中,默认情况下,Lua 虚拟机的全量垃圾回收(Full GC)是与 Mono 或 IL2CPP 的垃圾回收同步的。也就是说,通常会在 Unity 的 C# GC 发生时(或者帧末等时机),xLua 会自动调用一次 LuaGC.LuaGCMode.Forced
模式的 GC,这可能会带来不可控的卡顿。
Tick()
方法的引入:
为了给开发者提供更精细、更可控的性能管理手段,xLua 提供了 LuaEnv.Tick()
方法。调用 Tick()
会手动触发一次 Lua 虚拟机增量的一步垃圾回收。它不是一次性的、全量的垃圾回收,而是每次调用只进行一小步回收工作。这可以将一次可能引起卡顿的完整 GC 过程,分摊到多帧中去完成。
使用 Tick()
,你可以这样做:
void Update() {// 每帧执行一次Lua的增量GCluaEnv.Tick();
}
通过每帧调用 Tick()
,你将 Lua 的 GC 工作量均匀地分摊到了每一帧。虽然每一帧都有一点点开销,但避免了单帧的严重卡顿,使得帧时间更加平稳,游戏体验更流畅。
LuaEnv.Dispose()
作用:释放、清理和销毁 LuaEnv
实例所占用的资源,防止内存泄露。
每一个 new LuaEnv()
都必须有一个与之对应的 luaEnv.Dispose()
。最安全、最常用的地方就是在 Unity 的 OnDestroy
生命周期方法中调用。
void OnDestroy()
{if (luaEnv != null){luaEnv.Dispose();luaEnv = null;}
}
LuaEnv.DoString(str)
1、执行Lua语句
执行一个字符串str,字符串str内容必须符合Lua语法
LuaEnv luaEnv = null;
void Start()
{luaEnv = new LuaEnv();luaEnv.DoString("print('hello world')");
}void Update()
{// 每帧进行增量GC,平滑分摊GC开销if (luaEnv != null){luaEnv.Tick();}
}void OnDestroy()
{//在对象被销毁时,清理Lua环境if (luaEnv != null){luaEnv.Dispose();luaEnv = null; // 将其置为null是一个好习惯,防止后续误用}
}
2、加载Lua模块(不推荐)
调用路径默认在Resources下,即只能加载放在Unity的Resources文件夹下的lua脚本。并且,lua脚本后缀必须为.lua.txt
建议使用LuaEnv.AddLoader来加载脚本
例如:加载First.lua.txt脚本
luaEnv.DoString("require ('First')");
LuaEnv.AddLoader
Lua脚本路径重定向,用于从指定的lua脚本路径进行加载。
luaEnv.AddLoader(CustomLoader);
luaEnv.DoString("require ('Main')");public byte[] CustomLoader(ref string filepath)
{//传入的参数是require执行的脚本文件名string path = Application.dataPath + "/LuaScripts/" + filepath + ".lua.txt";if (File.Exists(path))//判断该路径是否存在{return File.ReadAllBytes(path);}else{Debug.Log("未找到该文件!");}return null;
}
LuaEnv启动模板
public class LuaLauncher : MonoBehaviour
{LuaEnv luaEnv = null;void Start(){luaEnv = new LuaEnv();luaEnv.AddLoader(CustomLoader);luaEnv.DoString("require ('Main')");}public byte[] CustomLoader(ref string filepath) {//传入的参数是require执行的脚本文件名string path = Application.dataPath + "/LuaScripts/" + filepath + ".lua.txt";if (File.Exists(path))//判断该路径是否存在{return File.ReadAllBytes(path);}else{Debug.Log("未找到该文件!");}return null;}void Update(){// 每帧进行增量GC,平滑分摊GC开销if (luaEnv != null){luaEnv.Tick();}}void OnDestroy(){//在对象被销毁时,清理Lua环境if (luaEnv != null){luaEnv.Dispose();luaEnv = null; // 将其置为null是一个好习惯,防止后续误用}}
}