从 toLua(tolua#)迁移到 ZLua
特性背景: compare/FEATURES.md
toLua / tolua# 以 预生成 Wrap、LuaState、全局导出类为特征;ZLua 以 懒绑定、CSharp 根表、Il2Cpp 内嵌桥 为特征。
1. 概念对照
| toLua / tolua# | ZLua |
|---|---|
LuaState / ToLua | LuaAppDomain |
*.Wrap.cs 导出类 | 无 Wrap;EnsureBinding 写三表 |
CustomSettings.cs 导出列表 | 无导出列表;public 懒 Bind |
全局 UnityEngine.GameObject | CSharp['UnityEngine.CoreModule']['UnityEngine.GameObject'] |
LuaFunction / LuaTable | [LuaInvoke]、返回 delegate、形参隐式 marshal、require 模块 table |
ToLua.Push / 手动绑定 | 自动 marshal(spec/marshal) |
out 多返回值 | StructUserData(Type(...))/ 拷贝语义(视签名) |
| Binder 注册 | CSharp 懒加载 + Codegen(Il2Cpp) |
2. 逐步迁移
步骤 1:移除 toLua 生成物
- 删除
Source/Generate/或工程中全部*Wrap.cs、*Binder.cs。 - 移除 toLua
#if宏、LuaClient、LuaState单例。 - 卸载 tolua# 插件 dll(若有独立 native)。
步骤 2:初始化
Before(tolua#):
LuaState lua = new LuaState();
lua.Start();
LuaBinder.Bind(lua);
lua.DoFile("Main.lua");
After(ZLua):
LuaAppDomain.Initialize(LoadModule);
// bootstrap + require 由 ZLua 与宿主 loader 负责
步骤 3:类型与调用改写
toLua 习惯 扁平全局;ZLua 要求 程序集 + 全名。
Before:
local go = GameObject.Find("Player")
local demo = Demo.New()
demo:SetValue(10)
local x = demo.x
After:
local GameObject = CSharp['UnityEngine.CoreModule']['UnityEngine.GameObject']
local Demo = CSharp['Assembly-CSharp']['Demo']
local go = GameObject.Find("Player")
local demo = Demo() -- 构造:Type(),非 Demo.New()(除非静态方法名如此)
demo:SetValue(10)
local x = demo.x
注意: toLua 的 Type.New() 在 ZLua 中一般为 Type()(类型表 __call → 构造)。若 C# 仅有静态工厂,仍用 Demo.New()。
步骤 4:自定义 Loader
Before: LuaState.AddSearchPath / 自定义 LuaFileUtils
After: LuaAppDomain.Initialize(moduleLoader) 统一解析:
Func<string, object> loader = module =>
{
var path = Path.Combine(projectRoot, "Lua", module.Replace('.', '/') + ".lua");
return File.ReadAllText(path);
};
Player 路径规则见 TESTING.md §5。
步骤 5:C# 调 Lua
Before:
LuaFunction func = lua.GetFunction("Update");
func.Call(Time.deltaTime);
func.Dispose();
After:
[LuaInvoke("game", "Update")]
static extern void LuaUpdate(float dt);
步骤 6:Delegate 与 tolua 事件
toLua 项目常用 LuaFunction.ToDelegate / 长期持有 LuaFunction:
Before:
LuaFunction lf = lua.GetFunction("onClick");
Button.onClick.AddListener(lf.ToDelegate<Action>());
After(推荐:Lua 传 function 给 C#):
public static void SetClickHandler(Action cb) { button.onClick.AddListener(() => cb()); }
ui:SetClickHandler(function() print("click") end)
After(C# 主动取回 Lua 函数再调): 使用 [LuaInvoke] 返回 Action/Func,或返回 Delegate + zlua.to_delegate。见 回调与 Delegate §3、from-xlua 步骤 5。
[LuaInvoke("ui", "get_on_click")]
static extern Action GetOnClick();
button.onClick.AddListener(GetOnClick());
步骤 7:值类型与 out
toLua 常用 多返回值 out:
Before:
local ok, result = luaObj:TryParse(str)
After:
-- struct out:Type(...) 构造 StructUserData
local outPoint = Point2D()
local ok = obj:TryGetPoint(outPoint)
-- 基元 out:裸实参走拷贝/default 分支;须 observable 写回时用 struct 形参或 C# 多返回值
struct 默认 ByVal userdata;ref/out observable 写回须 Type(...) StructUserData;boxed 场景用 zlua.box。
步骤 8:Unity 引擎 API
toLua 预导出大量 UnityEngine.* Wrap。迁移时:
- 按 实际使用 改
CSharp[assembly][typeFullName](程序集名查.asmdef/ Inspector)。 - 不必一次性改完全部引擎 API;按模块迁移。
- Il2Cpp Player 需程序集在 Codegen 输入内。
3. 常见坑
| 坑 | 说明 |
|---|---|
| 依赖全局类名 | Demo 未定义 → 必须 CSharp[...]['Demo'] 或局部 alias |
Demo.New() vs Demo() | ZLua 构造优先 Type() |
| Wrap 删除后链接错误 | C# 侧仍引用 LuaInterface 类型 → 一并删 |
tolua #if UNITY_EDITOR 双份逻辑 | 合并为 ZLua 双端同一套 Lua |
| 导出列表当安全边界 | 改 非 public API |
LuaTable 强依赖 | 改用 Lua module return table + require |
| 性能假设 | toLua 与 xLua 类似经 Wrap;ZLua Player 路径不同,见 compare/PERFORMANCE.md |
4. Before / After 示例
4.1 模块组织
Before(toLua + 全局):
-- Main.lua
UpdateBeat = UpdateBeat or {}
function UpdateBeat.OnUpdate(dt)
-- ...
end
After(ZLua 模块):
-- game.lua
local M = {}
function M.OnUpdate(dt)
-- ...
end
return M
[LuaInvoke("game", "OnUpdate")]
static extern void OnUpdate(float dt);
4.2 数组
Before:
local arr = System.Array.CreateInstance(typeof(int), 10)
After:
local arr = zlua.new_szarray_by_element_type(zlua.types.int32, 10)
-- 或
local intArrType = zlua.make_szarray_type(zlua.types.int32)
local arr2 = zlua.new_szarray_by_szarray_type(intArrType, 10)
4.3 清理 Wrap 引用(C#)
Before:
DemoWrap.Register(L);
After: 删除;首次 CSharp[...]['Demo'] 访问时自动 Bind。
5. CustomSettings 迁移对照
| toLua CustomSettings | ZLua |
|---|---|
customTypeList | 删除;按需 internal 隐藏 |
staticClassList | 不需要;静态成员在类型表 |
dynamicList | 不需要 |
outList | 遵循 C# 签名 + marshal spec |
sealedList | 无对应;继承规则见 type system |
6. 验收
- 无
LuaInterface/ToLua/*Wrap残留 -
Tests/Lua覆盖原 toLua 关键 API - Editor + Il2Cpp Player manifest 全绿
- 移除 toLua 后包体与启动无 lib 冲突
相关文档
| 文档 | 内容 |
|---|---|
| from-xlua.md | xLua 对照(C#→Lua 更详) |
| spec/05-LIB.md | zlua.* API |
| TESTING.md | 回归测试 |