跳到主要内容

从 toLua(tolua#)迁移到 ZLua

特性背景: compare/FEATURES.md toLua / tolua# 以 预生成 WrapLuaState、全局导出类为特征;ZLua 以 懒绑定CSharp 根表Il2Cpp 内嵌桥 为特征。 类型路径适配(可选): 规范 12 · tolua adaptor · 迁移索引


1. 概念对照

toLua / tolua#ZLua
LuaState / ToLuaLuaAppDomain
*.Wrap.cs 导出类无 WrapEnsureBinding 写三表
CustomSettings.cs 导出列表访问控制白名单;public 懒 Bind(适配清单仅迁移期)
全局 / 命名空间 UnityEngine.GameObject原生:CSharp[asm][full] adaptor 挂 _G.UnityEngine.GameObject
LuaFunction / LuaTableGetFunction、形参隐式 marshal、require 模块 table(适配不覆盖
ToLua.Push / 手动绑定自动 marshal(spec/marshal)
out 多返回值StructUserData(Type(...))/ 拷贝语义(视签名)
Binder 注册CSharp 懒加载 + Codegen(Il2Cpp)

2. 逐步迁移

步骤 1:移除 toLua 生成物

  1. 删除 Source/Generate/ 或工程中全部 *Wrap.cs*Binder.cs
  2. 移除 toLua #if 宏、LuaClientLuaState 单例。
  3. 卸载 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:类型与调用(二选一)

3A. 使用 tolua adaptor(减改写,推荐过渡)

包内:ZLua~/adaptors/tolua/ExportTypes.cs + 共用 ZLua~/adaptors/adaptor.lua。契约见 规范 12

  1. 仍含 toLua 的工程复制 ExportTypes.cs → Editor,菜单 ZLua/ExportTypes
  2. CustomSettings.customTypeList 生成 tolua_export_types.luaexport_nameUnityEngine.GameObject 这类命名空间路径)
  3. 将清单与 adaptor.lua 放入 ZLua 工程可 require 的目录
  4. 入口:
local export_types = require "tolua_export_types"
local adaptor = require "adaptor"
adaptor.init(export_types)
-- 此后 UnityEngine.GameObject 等可用(清单内;冲突会 fail-fast)

注意: 与运行时 BeginModule 一致,默认挂 命名空间链,不是「仅全局短名」。SetNameSpace(null) / SetLibName 会反映到 export_name。成员调用等仍按 ZLua 语义。

3B. 改写为原生 CSharp

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:

static readonly Action<float> LuaUpdate =
LuaAppDomain.GetFunction<Action<float>>("game", "Update");

步骤 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 函数再调): 使用 GetFunction<Action>/GetFunction<Func<…>>,或 GetFunction<Delegate> + zlua.to_delegate。见 Function 与 Delegatefrom-xlua 步骤 5

static readonly Func<Action> GetOnClick =
LuaAppDomain.GetFunction<Func<Action>>("ui", "get_on_click");

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 userdataref/out observable 写回须 Type(...) StructUserData;boxed 场景用 zlua.box

步骤 8:Unity 引擎 API

toLua 预导出大量 UnityEngine.* Wrap。迁移时:

  1. 实际使用CSharp[assembly][typeFullName](程序集名查 .asmdef / Inspector)。
  2. 不必一次性改完全部引擎 API;按模块迁移。
  3. Il2Cpp Player 需程序集在 Codegen 输入内。

3. 常见坑

说明
依赖全局类名Demo 未定义 → tolua adaptorNamespace.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
```csharp
static readonly Action<float> OnUpdate =
LuaAppDomain.GetFunction<Action<float>>("game", "OnUpdate");

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 CustomSettingsZLua
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 冲突
  • (若用 adaptor)tolua_export_types + adaptor.initUnityEngine.GameObject 等命名空间路径可用;冲突/缺失类型有明确 error

相关文档

文档内容
migration/共用清单与适配层总览
spec/12-MIGRATION-ADAPTORS.mdtolua adaptor 契约
from-xlua.mdxLua 对照(C#→Lua 更详)
spec/05-LIB.mdzlua.* API
TESTING.md回归测试