ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

xLua 在 Unity WebGL 中的 API 实战指南:LuaEnv、LuaTable、LuaFunction 与类型映射全解析

xLua 在 Unity WebGL 中的 API 实战指南:LuaEnv、LuaTable、LuaFunction 与类型映射全解析 游戏开发移动开发WebAssembly【免费下载链接】minigame-unity-webgl-transform微信小游戏Unity引擎适配器文档。项目地址https://gitcode.com/GitHub_Trending/mi/minigame-unity-webgl-transform点击查看免费下载本文以本仓库Demo/xLua_WebGL项目中的 XLua_API_EN.md 为骨架系统讲解 xLua 面向 Unity 开发者公开的 C# 侧 APILuaEnv / LuaTable / LuaFunction与 Lua 侧 APICS 命名空间、typeof、uint64 等并结合仓库内 LuaEnv.cs 的实现细节与 Tutorial 中的可运行示例逐条解读 API 签名、参数含义、返回值与注意事项。读完本文你将能独立完成在 Unity 中创建 Lua 虚拟机、执行/加载 Lua 代码、双向调用 C# 与 Lua、处理类型映射的完整链路并理解 WebGL 平台下 LuaEnv 生命周期管理的关键点。一、xLua 与本文要解决的核心问题xLua 是腾讯开源的 Unity 下 Lua 编程解决方案它让 Lua 代码可以流畅地调用 C# 代码、C# 代码也可以反向调用 Lua 函数。在本仓库的Demo/xLua_WebGL工程中xLua 被完整集成源码位于 Assets/XLua/Src其核心目标是把这套能力在 WebGL 平台上跑通——这也是本仓库微信小游戏 Unity WebGL 适配选择它的原因Lua 脚本既承担热更逻辑又能在小游戏环境中轻量运行。要掌握 xLua首先必须理解三组对象LuaEnv一个 Lua 虚拟机的 C# 封装是 C# 与 Lua 之间所有交互的入口LuaTableLua 表table的 C# 包装LuaFunctionLua 函数function的 C# 包装。下面按文档脉络逐类展开。二、C# 侧核心 APILuaEnvLuaEnv 对应 C# 侧的虚拟机实例。仓库实现位于 LuaEnv.cs其构造函数内部会依次完成校验 Lua 库版本LIB_VERSION_EXPECT 105、创建 Lua state、加载基础库luaopen_xlua、luaopen_i64lib、注册print、注册 searcher 与CS命名空间、执行init_xlua初始化脚本等最终把_GLua 全局表包装成 LuaTable 存入Global属性。2.1 DoString执行一段 Lua 代码object[] DoString(string chunk, string chunkName chunk, LuaTable env null)chunkLua 代码字符串chunkName仅用于报错信息出错时帮助定位第几段代码的第几行env该代码块的运行环境表相当于setfenv指定的环境返回值代码块中return语句返回的值组成的数组。例如return 1, helloDoString 会返回一个数组其中一个是double类型的1另一个是字符串hello。文档给出的最小示例LuaEnv luaenv new LuaEnv(); object[] ret luaenv.DoString(print(hello)\r\nreturn 1); UnityEngine.Debug.Log(ret ret[0]); luaenv.Dispose();从源码看DoString(string)会先把字符串按 UTF-8 编码为字节数组再走DoString(byte[])的重载调用xluaL_loadbuffer编译 chunk若编译成功且传入 env 则执行lua_setfenv随后用lua_pcall调用成功后通过translator.popValues把 Lua 栈上的返回值弹成object[]见 LuaEnv.cs。因此返回值在 Lua 与 C# 之间已经过类型转换例如 Lua 的数字回到 C# 侧是double。仓库中的可运行例子 ByString.cs 展示了在 MonoBehaviour 中的完整用法void Start() { luaenv new LuaEnv(); luaenv.DoString(print(hello world)); } void Update() { if (luaenv ! null) luaenv.Tick(); // 周期回收未手动释放的 Lua 对象 } void OnDestroy() { luaenv.Dispose(); // 销毁时释放虚拟机 }注意文档示例中luaenv.Dispose()前后要保证执行顺序实际工程中通常放在OnDestroy/OnApplicationQuit中调用并避免在 Dispose 之后继续使用 LuaEnv源码中L属性会在rawL Zero时抛出InvalidOperationException(this lua env had disposed!)。2.2 LoadString只加载不执行T LoadStringT(string chunk, string chunkName chunk, LuaTable env null)与 DoString 的差别在于LoadString 只编译/加载代码块不会执行它而是把这段代码块包装成指定类型T返回。T只能是委托类型或 LuaFunction。返回委托luaenv.LoadStringSystem.Action(print(hi))随后调用这个委托即执行对应 Lua 代码返回 LuaFunctionLuaFunction func luaenv.LoadString(return 11)。源码里对这一限制有显式校验if (typeof(T) ! typeof(LuaFunction) !typeof(T).IsSubclassOf(typeof(Delegate)))会抛出InvalidOperationException见 LuaEnv.cs说明这个泛型约束是硬性的。加载路径同样是 UTF-8 转字节后走xluaL_loadbuffer并支持env参数设置环境表。2.3 GlobalLua 全局环境表LuaTable Global;Global是代表 Lua 全局环境_G的 LuaTable。任何在 Lua 侧定义为全局的变量/函数都可以通过luaenv.Global.GetT(name)在 C# 侧取到。源码在构造函数末尾用translator.Get(rawL, -1, out _G)把全局表包装为LuaTable并缓存见 LuaEnv.cs。2.4 Tick周期回收 Lua 对象void Tick()Tick()的作用是清理未手动释放的 LuaBase 对象如 LuaTable、LuaFunction以及其它需要周期处理的资源。它需要在MonoBehaviour.Update中周期调用。从源码看Tick 做了两件事LuaEnv.cs把refQueue中排队待释放的引用依次交给translator.ReleaseLuaBase真正释放调用translator.objects.Check(...)对 C# 侧缓存的 Lua 对象做增量检查单帧最多检查max_check_per_tick 20个对象并过滤已被销毁的UnityEngine.Object。另外LuaEnv还提供别名GC()等价于Tick()以及一整套 GC 调优 API见 LuaEnv.csGcPauseLUA_GCSETPAUSE默认 200GC 启动新周期的等待时间值越大越不激进小于 100 表示不等待GcStepmulLUA_GCSETSTEPMUL默认 200GC 相对内存分配的执行速度倍率默认 200 表示以两倍于内存分配的速度回收FullGc()强制执行一次完整 GCLUA_GCCOLLECTStopGc()/RestartGc()暂停/重启 GCLUA_GCSTOP / LUA_GCRESTARTGcStep(int data)手动推进 GC 一步Memroy获取当前 Lua 内存用量LUA_GCCOUNT。在 WebGL/小游戏这类对内存敏感的场景建议在 UI 切后台、关卡切换等时机用FullGc()主动回收。2.5 AddLoader自定义 require 加载器void AddLoader(CustomLoader loader)CustomLoader是委托类型public delegate byte[] CustomLoader(ref string filepath);当 Lua 侧执行require(xxx)时xLua 会回调该委托参数filepath是require传入的名字ref类型加载器可以改写它若加载器找到了文件把内容读入内存并返回UTF-8 编码的 byte[]若需要调试支持如配合 IDE 定位源码行应把filepath改写为 IDE 能识别的相对/绝对路径找不到文件时返回null。仓库教程 CustomLoader.cs 给出一个内存态加载器的完整示例luaenv.AddLoader( (ref string filename) { if (filename InMemory) { string script return {ccc 9999}; return System.Text.Encoding.UTF8.GetBytes(script); } return null; } ); luaenv.DoString(print(InMemory.ccc, require(InMemory).ccc));源码层面AddLoader只是把委托加入customLoaders列表LuaEnv.cs实际查找发生在StaticLuaCallbacks.LoadFromCustomLoaders这个 searcher 中构造函数里以索引 3 注册见 LuaEnv.cs。同一张 searcher 表里还注册了内置库加载器索引 2、Resources 加载器索引 4非 XLUA_GENERAL 时和 StreamingAssets 加载器索引 -1即表尾自定义加载器优先级高于 Resources/StreamingAssets。这也意味着在 WebGL 平台上你完全可以用自定义加载器对接小游戏的包内资源读取接口。2.6 Dispose销毁虚拟机void Dispose()Dispose 释放整个 LuaEnv。源码实现LuaEnv.cs会依次执行FullGc()→ 两次System.GC.Collect()WaitForPendingFinalizers→ 校验所有 C# 回调桥已释放否则抛InvalidOperationException(try to dispose a LuaEnv with C# callback!)→ 从ObjectTranslatorPool移除 →lua_close(L)→ 把rawL置零。因此只要还有 C# 回调delegate bridge挂在 LuaEnv 上Dispose 就会抛异常应确保先解绑所有回调再销毁。文档给出的使用建议全局只用一个 LuaEnv 实例在Update中调用 GCTick不再需要时调用 Dispose。这条建议在移动端与 WebGL 端都适用多实例会造成额外内存与 GC 负担。三、C# 侧核心 APILuaTableLuaTable 是 Lua 表的 C# 包装。你可以通过luaenv.Global、DoString返回值或NewTable()见 LuaEnv.cs拿到 LuaTable 对象。3.1 Get / GetInPath / SetInPathT GetT(string key) // 取 key 上的值不存在或类型不匹配返回 null T GetInPathT(string path) // 支持 . 路径如 tbl.GetInPathint(a.b.c) void SetInPathT(string path, T val) // GetInPath 对应的 setterGetInPath与Get的关键区别GetInPath会把字符串中的.当作层级分隔符解析。例如var i tbl.GetInPathint(a.b.c)等价于在 Lua 中执行i tbl.a.b.c。它避免了多次调用 Get、也不用取出中间变量执行效率更高。3.2 非字符串 key 的读写void GetTKey, TValue(TKey key, out TValue value) void SetTKey, TValue(TKey key, TValue value)上面所有 API 的 key 都只能是字符串而这两个 API对 key 的类型没有限制可用于以数字、其它表等作为 key 的读写场景。3.3 Cast转换为指定类型T CastT()把 LuaTable 转换成T类型。文档说明T可以是声明了CSharpCallLua的接口带默认构造函数的类或 structDictionary、List等集合类型等等。这在Lua 数据表 → C# 强类型对象的批量转换中非常实用仓库 Gen 目录下的*Bridge.cs与*Wrap.cs文件即为这类转换生成的桥接代码。3.4 SetMetaTablevoid SetMetaTable(LuaTable metaTable)为表设置 metatable等价于 Lua 中的setmetatable(t, mt)。四、C# 侧核心 APILuaFunctionLuaFunction 是 Lua 函数的 C# 包装。重要性能提示文档原文用 LuaFunction 访问 Lua 函数会带来装箱/拆箱boxing/unboxing开销。高频调用时不要使用该类型。推荐用table.GetABCDelegate()取到 C# 委托后再调用假设 ABCDelegate 是 C# 定义的委托并在使用table.GetABCDelegate之前把 ABCDelegate 加入生成代码列表通过[CSharpCallLua]特性或生成配置这样才能享受预生成代码带来的性能收益。4.1 Callobject[] Call(params object[] args) object[] Call(object[] args, Type[] returnTypes)Call(params object[] args)以可变参数调用 Lua 函数返回调用结果返回值数组Call(object[] args, Type[] returnTypes)调用并显式指定各返回值的 C# 类型系统会按指定类型自动转换。4.2 SetEnvvoid SetEnv(LuaTable env)等价于 Lua 的setfenv函数为这个 Lua 函数设置运行环境表。在 Lua 5.3/5.4 中对应_ENVupvalue 的替换xLua 的init_xlua脚本里通过debug.upvaluejoin兼容实现了setfenv/getfenv见 LuaEnv.cs。五、Lua 侧 API访问 C# 世界5.1 CS 命名空间在 Lua 中通过全局表CS访问 C# 类型语法与 C# 类型路径一一对应-- 调用 C# 构造函数返回类型实例 local v1 CS.UnityEngine.Vector3(1,1,1) -- 访问 C# 静态成员 print(CS.UnityEngine.Vector3.one) -- 访问 C# 枚举值 -- CS.namespace.enum.fieldCS命名空间对象由源码中AddBuildin(CS, StaticLuaCallbacks.LoadCS)注册见 LuaEnv.cs其背后的类型解析逻辑就是init_xlua脚本里metatable:__index通过xlua.import_type按完整类型名惰性加载并缓存见 LuaEnv.cs。C# 对象在 Lua 中可以被当作表访问取字段、调方法、用运算符都按 Lua 习惯来。文档给出的综合示例local v1 CS.UnityEngine.Vector3(1,1,1) local v2 CS.UnityEngine.Vector3(1,1,1) v1.x 100 v2.y 100 print(v1, v2) local v3 v1 v2 print(v1.x, v2.x) print(CS.UnityEngine.Vector3.one) print(CS.UnityEngine.Vector3.Distance(v1, v2))注意v1.x 100这类字段写入要求类型在生成代码中支持 setter 访问否则需要xlua.private_accessible或生成配置配合。5.2 typeof获取 Type 对象typeof与 C# 的typeof关键字类似返回一个 Type 对象。典型用途是GameObject.AddComponent其某个重载需要 Type 参数newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem))在init_xlua中typeof被定义为function(t) return t.UnderlyingSystemType end见 LuaEnv.cs。5.3 无符号 64 位整数支持Lua 的数字精度不足以完整表示ulonguint64xLua 通过luaopen_i64lib注册了一组 uint64 操作函数函数说明uint64.tostring无符号数转字符串uint64.divide无符号数除法uint64.compare无符号数比较相等返回 0大于返回正数小于返回负数uint64.remainder无符号数取模uint64.parse字符串转无符号数5.4 xlua.structclone 与 xlua.private_accessiblexlua.structclone(csharpStruct) -- 克隆一个 C# 结构体 xlua.private_accessible(class) -- 让某类型的私有字段/属性/方法可被 Lua 访问xlua.private_accessible适用于需要在 Lua 侧读写 C# 类私有成员的场景例如通过反射类访问内部实现。它与热更xlua.hotfix配合时也会被自动调用init_xlua中 hotfix 函数末尾会执行xlua.private_accessible(cs)。5.5 cast按接口访问对象cast(calc, typeof(CS.PerformentTest.ICalc))当对象的实现类型不可访问如 internal 类型时可用cast把它当作指定接口来访问。cast在init_xlua中被定义为cast xlua.cast见 LuaEnv.cs要求目标接口声明了CSharpCallLua或在生成配置中登记仓库 Gen 目录下的Tutorial_ICalcWrap.cs、XLuaTest_InvokeLua_ICalcBridge.cs等文件就是这类接口桥接的生成产物。文档同时提示一旦对某个对象执行了cast就没有其它 API 可用了该对象会以接口形式暴露原类型成员不再直接可见需要按需重新取对象或调整设计。六、类型映射C# 与 Lua 的互转规则6.1 基本数据类型映射C# 类型Lua 类型sbyte, byte, short, ushort, int, uint, double, char, floatnumberdecimaluserdatalong, ulonguserdata / lua_IntegerLua 5.3 及以上byte[]stringboolbooleanstringstring几个要点除long/ulong外的整型、浮点、字符统一映射为 Lua 的number即 double往返会自动做精度适配decimal在 Lua 侧是 userdata不参与隐式算术转换byte[]在 Lua 侧表现为 string这是传输二进制数据如加密脚本、贴图字节流的约定通道——注意字符串在 UTF-8/byte 之间转换的边界仓库里自定义加载器返回的正是 UTF-8 的byte[]long/ulong在 Lua 5.3 下可映射为原生lua_Integer否则为 userdata配合上文的uint64.*函数族使用。6.2 复杂数据类型映射C# 类型Lua 类型LuaTabletableLuaFunctionfunctionclass 或 struct 实例userdata、tablemethod、delegatefunction逐条规则LuaTable若 C# 方法的入参或 Lua 方法的返回值显式声明为 LuaTable则 Lua 侧必须传 table若 C# 侧未声明类型Lua 的 table 会被自动转换为 LuaTableLuaFunction规则同上Lua 的函数会转换为 LuaFunctionLuaUserData与 C# 托管对象无关的非托管对象对应的 Lua userdata类/结构体实例C# 侧传入的实例映射为 Lua userdata通过__index访问成员。若 C# 声明入参为指定类型Lua 侧可直接使用该类型的 userdata若该类型有默认构造函数Lua 的 table 可被自动转换——转换规则是调用默认构造创建实例再按 table 中的字段名逐个赋值给 C# 对应的 setter 成员方法/委托C# 的普通成员方法和委托都对应 Lua 函数。参数与返回值的映射顺序约定为C# 普通参数、引用参数ref→ Lua 函数的参数C# 返回值 → Lua 函数的第一个返回值C# 的 ref/out 参数 → Lua 函数返回值的第 2 到第 N 个按参数顺序。这一约定意味着带 out/ref 参数的 C# 方法在 Lua 侧要按多返回值的方式接取例如 C# 方法bool TryGet(out int v)在 Lua 侧应写作local ok, v obj:TryGet()。七、编译宏功能开关文档列出三个关键编译宏宏作用HOTFIX_ENABLE开启热更新hotfix功能NOT_GEN_WARNING在发生反射未走生成代码时打印警告GEN_CODE_MINIMIZE以最小化代码段的方式生成代码HOTFIX_ENABLE在仓库 LuaEnv.cs 中被大量使用第 49、73、209、253 等行开启后 LuaEnv 内部会启用锁与热更初始化分支配合xlua.hotfixinit_xlua中定义实现 C# 方法替换GEN_CODE_MINIMIZE开启后构造函数会注册InternalGlobals.CSharpWrapperCallerPtrLuaEnv.cs通过更紧凑的包装调用方式降低生成代码体积——这在 WebGL 包体紧张的场景下尤其值得开启NOT_GEN_WARNING用于开发期审计某个类型/方法没走预生成而走了反射路径时给出警告帮助开发者补齐生成配置以获得最佳性能。宏的具体配置方式在 Player Settings 的 Scripting Define Symbols 中定义见仓库 XLua 配置文档生成代码的产物集中在 Gen 目录含EnumWrap.cs、各*Wrap.cs、*Bridge.cs、XLuaGenAutoRegister.cs与link.xml可作为理解生成机制的第一手材料。八、实战落地一个 WebGL 场景下的最小生命周期模板综合文档与仓库示例在 Unity WebGL 工程中使用 xLua 的标准姿势是单例 LuaEnv 周期 Tick 恰当释放using XLua; public class LuaManager : UnityEngine.MonoBehaviour { private static LuaManager _inst; public static LuaManager Inst _inst ?? (_inst new UnityEngine.GameObject(LuaManager) .AddComponentLuaManager()); private LuaEnv _luaenv; public LuaEnv Env _luaenv; void Awake() { if (_inst ! null _inst ! this) { Destroy(this); return; } _inst this; _luaenv new LuaEnv(); // 按需接入自定义 require 加载器可对接小游戏包内资源读取 _luaenv.AddLoader((ref string filepath) { var textAsset UnityEngine.Resources.LoadUnityEngine.TextAsset(Lua/ filepath); return textAsset ! null ? System.Text.Encoding.UTF8.GetBytes(textAsset.text) : null; }); } void Update() { _luaenv.Tick(); } // 每帧回收 Lua 对象 void OnDestroy() { _luaenv.Dispose(); } // 全部 Lua 引用释放后再调用 }在此基础上Lua 侧即可通过CS.UnityEngine.*访问引擎 API通过require组织模块并通过luaenv.Global.Get委托()把 Lua 函数暴露给 C# 高频调用记得把委托加入[CSharpCallLua]生成配置。九、深入阅读指引本文对应文档的英文原版位于 XLua_API_EN.md同目录还提供中文版 XLua_API.md 与配套教程 XLua_Tutorial_EN.md。若需要继续深化仓库内还有以下第一手材料LuaEnv 全部实现LuaEnv.cs涵盖 DoString/LoadString/Tick/AddLoader/Dispose 及全部 GC 调优 API 的逐行实现C# 调用 Lua 教程CSharpCallLua委托、接口、List/Dictionary 等转换方式Lua 调用 C# 教程LuaCallCSharp自定义加载器可运行示例CustomLoader.cs热更与配置Hotfix_EN.md、Configure_EN.md、faq.md生成代码产物Gen 目录下的 Wrap/Bridge/Register 文件与 link.xml用于理解预生成机制与裁剪规则。结合本仓库的定位建议下一步把 HOTFIX_ENABLE 宏 与 WebGL 构建配置Demo/xLua_WebGL/ProjectSettings配合起来验证热更在小游戏运行时的表现这是 xLua 选型价值最大的落点。赞分享游戏开发移动开发WebAssembly【免费下载链接】minigame-unity-webgl-transform微信小游戏Unity引擎适配器文档。项目地址https://gitcode.com/GitHub_Trending/mi/minigame-unity-webgl-transform点击查看免费下载相关推荐xLua 核心 API 全解析LuaEnv / LuaTable / LuaFunction 与 C-Lua 类型映射实战指南xLua 核心 API 全解析LuaEnv / LuaTable / LuaFunction 与 C Lua 类型映射实战指南 导读 本文以 xLua 仓库官游戏开发脚本语言集成xLua 官方 API 完整指南LuaEnv、LuaTable、LuaFunction 与 C/Lua 双向互操作详解xLua 官方 API 完整指南LuaEnv、LuaTable、LuaFunction 与 C /Lua 双向互操作详解 本篇技术指南以 xLua 官方文档游戏开发脚本语言集成xLua 核心 API 全解析从 C 调用 Lua 到类型映射与内置宏xLua 核心 API 全解析从 C 调用 Lua 到类型映射与内置宏 导读 本文以 xLua 官方 API 参考文档 XLua_API_EN.md htt游戏开发脚本语言集成上一篇Volatility 项目推荐下一篇如何快速掌握 OpenH264免费开源的 H.264 编解码工具完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表