跳到主要内容

从 toLua(tolua#)迁移到 ZLua

特性背景: compare/FEATURES.md
toLua / tolua# 以 预生成 WrapLuaState、全局导出类为特征;ZLua 以 懒绑定CSharp 根表Il2Cpp 内嵌桥 为特征。


1. 概念对照

toLua / tolua#ZLua
LuaState / ToLuaLuaAppDomain
*.Wrap.cs 导出类无 WrapEnsureBinding 写三表
CustomSettings.cs 导出列表导出列表;public 懒 Bind
全局 UnityEngine.GameObjectCSharp['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 生成物

  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:类型与调用改写

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 §3from-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 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 未定义 → 必须 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 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 冲突

相关文档

文档内容
from-xlua.mdxLua 对照(C#→Lua 更详)
spec/05-LIB.mdzlua.* API
TESTING.md回归测试