ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

jynew 中 xLua 配置全指南:Lua 与 C 互操作的代码生成白名单详解

2026/9/16 16:50:29 拓冰建站 浏览量
jynew 中 xLua 配置全指南:Lua 与 C 互操作的代码生成白名单详解 jynew 中 xLua 配置全指南Lua 与 C# 互操作的代码生成白名单详解【免费下载链接】jynewJinYongLegend-like RPG Game Framework with full Modding support and 10 hours playable samples of game.项目地址: https://gitcode.com/GitHub_Trending/jy/jynew导读xLua 通过白名单 代码生成机制让 Lua 高效访问 C# 类型其全部配置都围绕告诉生成器哪些类型需要适配代码展开。本文以《金庸群侠传3D重制版》jynew 仓库自带的 xLua 配置文档 为骨架结合 GenAttributes.cs 中的 attribute 定义、ExampleGenConfig.cs 与 ExampleConfig.cs 中的实战示例系统讲解打标签、静态列表、动态列表三种配置方式以及LuaCallCSharp、CSharpCallLua、GCOptimize、BlackList等核心标签的语义与适用场景。读完本文你将能够为任意 C# 类型/成员正确配置 xLua 代码生成规避 il2cpp 裁剪与反射性能陷阱并复现 jynew 项目中的配置实践。一、配置总览三种方式与两条必须xLua 的所有配置即代码生成白名单都支持以下三种声明方式打标签Attribute直接在类型或成员上标注[LuaCallCSharp]、[GCOptimize]等特性静态列表在一个静态类中声明一个打了标签的static字段字段类型只要实现了IEnumerableType即可BlackList与AdditionalProperties两个例外有专门的类型要求下文详述动态列表在一个静态类中声明一个打了标签的static属性Getter 是代码可在运行时按名字空间、按程序集等条件动态筛选类型。无论采用哪种方式配置都受两必须两建议约束列表方式必须为static字段/属性列表方式必须放在一个static类中建议不用标签方式标签在 il2cpp 下会增加不少代码量建议列表配置放在 Editor 目录如果是 Hotfix 配置且类位于 Assembly-CSharp.dll 之外的其它 dll则必须放 Editor 目录。从源码看这些约束与 GenAttributes.cs 中 attribute 的定义一一对应LuaCallCSharpAttribute、CSharpCallLuaAttribute、BlackListAttribute、GCOptimizeAttribute、ReflectionUseAttribute、DoNotGenAttribute、AdditionalPropertiesAttribute、HotfixAttribute均通过特性标注由编辑器侧的代码生成器扫描装配。二、打标签Attribute方式直接在 C# 类型上标注[LuaCallCSharp]xLua 即会为该类型生成适配代码。例如文档给出的最小示例[LuaCallCSharp] public class A { }该方式使用方便但应避免作为主力配置。原因是 il2cpp 下每个标签都会让生成器为该类型产出固定适配代码从而增加不少代码量。jynew 仓库中 NoGc.cs 示例即展示了标签方式与GCOptimize的配合[GCOptimize] [LuaCallCSharp] public struct MyStruct { public MyStruct(int p1, int p2) { a p1; b p2; c p2; e.c (byte)p1; } public int a; public int b; public decimal c; public Pedding e; }三、静态列表方式当无法直接给类型打标签时——例如系统 API、无源码的第三方库、实例化的泛型类型——应在静态类中声明一个实现IEnumerableType的静态字段并打上标签[LuaCallCSharp] public static ListType mymodule_lua_call_cs_list new ListType() { typeof(GameObject), typeof(Dictionarystring, int), };字段必须放在静态类中建议放在 Editor 目录。仓库中 ExampleGenConfig.cs 给出了一个完整的 LuaCallCSharp 静态列表涵盖 Unity 常用类型与标准库public static class ExampleGenConfig { [LuaCallCSharp] public static ListType LuaCallCSharp new ListType() { typeof(System.Object), typeof(UnityEngine.Object), typeof(Vector2), typeof(Vector3), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Ray), typeof(Bounds), typeof(Ray2D), typeof(Time), typeof(GameObject), typeof(Component), typeof(Behaviour), typeof(Transform), typeof(Resources), typeof(TextAsset), typeof(Keyframe), typeof(AnimationCurve), typeof(AnimationClip), typeof(MonoBehaviour), typeof(ParticleSystem), typeof(SkinnedMeshRenderer), typeof(Renderer), typeof(WWW), typeof(Light), typeof(Mathf), typeof(System.Collections.Generic.Listint), typeof(Actionstring), typeof(UnityEngine.Debug) }; // ... }四、动态列表方式声明一个静态属性并打上标签Getter 中可编写任意筛选逻辑。文档给出的 Hotfix 示例按名字空间过滤整个程序集[Hotfix] public static ListType by_property { get { return (from type in Assembly.Load(Assembly-CSharp).GetTypes() where type.Namespace XXXX select type).ToList(); } }该属性同样必须放在静态类中建议放在 Editor 目录。Getter 是代码因此可以实现按名字空间配置、按程序集配置等任意效果。仓库 ExampleConfig.cs 中给出了一套纯 Lua 编程的自动化配置参考——把UnityEngine、UnityEngine.UI等整个命名空间的所有导出类型排除 delegate、interface、enum 及exclude列表中的类型全部注入白名单[LuaCallCSharp] public static IEnumerableType LuaCallCSharp { get { Liststring namespaces new Liststring() { UnityEngine, UnityEngine.UI }; var unityTypes (from assembly in AppDomain.CurrentDomain.GetAssemblies() where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) from type in assembly.GetExportedTypes() where type.Namespace ! null namespaces.Contains(type.Namespace) !isExcluded(type) type.BaseType ! typeof(MulticastDelegate) !type.IsInterface !type.IsEnum select type); // 再拼接 Assembly-CSharp 自定义类型... return unityTypes.Concat(customTypes); } }同文件还提供了自动把 LuaCallCSharp 涉及到的 delegate 追加到 CSharpCallLuaL101-L136与热补丁全程序集注入L140-L149两份自动化模板适合全 Lua 编程或大面积热更场景。五、核心配置标签逐一解析以下每个标签在 GenAttributes.cs 中都有对应 attribute 定义语义以源码注释与文档为准。5.1 XLua.LuaCallCSharp —— 生成 Lua 调用 C# 的适配代码一个 C# 类型加上该配置xLua 会生成该类型的适配代码覆盖构造该类型实例、访问其成员属性/方法、静态属性/方法。未配置的类型将退化为性能较低的反射方式访问。关键规则扩展方法Extension Methods加该配置后适配代码会追加到被扩展类型的成员方法上xLua只生成加了该配置的类型不会自动生成其父类的适配代码。访问子类对象的父类方法时若父类也加了LuaCallCSharp执行父类适配代码否则走反射反射访问除性能不佳外在 il2cpp 下还可能因代码剪裁而无法访问可用下述ReflectionUse规避。5.2 XLua.ReflectionUse —— 阻止 il2cpp 代码剪裁一个 C# 类型加该配置后xLua 会生成link.xml阻止 il2cpp 对它的代码剪裁。要点对扩展方法必须加LuaCallCSharp或ReflectionUse才能被 Lua 访问到官方建议所有要在 Lua 访问的类型要么加LuaCallCSharp要么加ReflectionUse这样才能保证各平台尤其 il2cpp正常运行。5.3 XLua.DoNotGen —— 部分成员不生成代码指明某个类中的部分函数、字段、属性不生成代码改为反射访问。仅支持DictionaryType, Liststring类型的字段或属性key 是生效的类value 是不生成代码的成员名列表。与ReflectionUse的区别ReflectionUse指明的是整个类第一次访问某成员时ReflectionUse会把整个类都 wrap而DoNotGen只 wrap 该成员——DoNotGen 更 lazy。与BlackList的区别BlackList配置了就不能用BlackList能指明某个重载DoNotGen不能。5.4 XLua.CSharpCallLua —— 生成 C# 调用 Lua 的适配代码若要把lua 函数适配为 C# delegate典型场景C# 侧各种回调、UI 事件、delegate 参数如ListT.ForEach或通过LuaTable.Get将 lua 函数绑定到 delegate或把lua table 适配为 C# interface则对应 delegate/interface 需要加该配置。jynew 项目在 Jyx2LuaToCsBridge.cs 中正是用[CSharpCallLua]标注LBattleConfig接口来解读 Lua 侧的战斗配置表/// summary用来解读Lua的Battle配置表/summary [CSharpCallLua] public interface LBattleConfig { int Id { get; set; } string Name { get; set; } string MapScene { get; set; } //地图 int Exp { get; set; } //获得经验 int Music { get; set; } //音乐 Listint TeamMates { get; set; } //队友 Listint AutoTeamMates { get; set; } Listint Enemies { get; set; } //敌人 ListRoleInstance DynamicTeammate { get; set; } ListRoleInstance DynamicEnemies { get; set; } }同文件的CsBattleConfig : LBattleConfig则作为该接口的 C# 侧实现用于在 C# 侧生成配置对象——这正是lua table 适配 C# interface的典型落地。CSharpCallLua的静态列表示例见 ExampleGenConfig.cs[CSharpCallLua] public static ListType CSharpCallLua new ListType() { typeof(Action), typeof(Funcdouble, double, double), typeof(Actionstring), typeof(Actiondouble), typeof(UnityEngine.Events.UnityAction), typeof(System.Collections.IEnumerator) };5.5 XLua.GCOptimize —— 值类型免 GC 优化C# 纯值类型只包含值类型的 struct可嵌套其它只包含值类型的 struct或C# 枚举加该配置后xLua 会为其生成 gc 优化代码效果是该值类型在 Lua 与 C# 间传递不产生 C# gc alloc其数组访问也不产生 gc。各种无 GC 场景可参考 05_NoGc 示例。除枚举外包含无参构造函数的复杂类型都会生成lua table ↔ 该类型及其一维数组的转换代码优化转换性能更少 gc alloc。jynew 仓库中xLua 对 UnityEngine 内置值类型的 GCOptimize 配置可直接在 GenAttributes.cs 的SysGenConfig类中看到它采用的就是动态列表 属性形式public static class SysGenConfig { [GCOptimize] static ListType GCOptimize { get { return new ListType() { typeof(UnityEngine.Vector2), typeof(UnityEngine.Vector3), typeof(UnityEngine.Vector4), typeof(UnityEngine.Color), typeof(UnityEngine.Quaternion), typeof(UnityEngine.Ray), typeof(UnityEngine.Bounds), typeof(UnityEngine.Ray2D), }; } } // ... }NoGc.cs 还示范了GCOptimize与CSharpCallLua的组合自定义 structMyStruct、枚举MyEnum、decimal等均通过 delegate 参数在Update中高频传递实现零分配。5.6 XLua.AdditionalProperties —— GCOptimize 的扩展这是GCOptimize的扩展配置。某些 struct 习惯把 field 做成私有、通过 property 访问此时需要该配置——因为默认情况下GCOptimize只对 public 的 field 打解包。配置类型要求为DictionaryType, Liststringkey 是生效的类型value 是属性名列表。仓库 GenAttributes.cs 中对 UnityEngine 值类型的配置即为此模式的官方范本[AdditionalProperties] static DictionaryType, Liststring AdditionalProperties { get { return new DictionaryType, Liststring() { { typeof(UnityEngine.Ray), new Liststring() { origin, direction } }, { typeof(UnityEngine.Ray2D), new Liststring() { origin, direction } }, { typeof(UnityEngine.Bounds), new Liststring() { center, extents } }, }; } }5.7 XLua.BlackList —— 黑名单如果不需要生成某个类型某些成员的适配代码可用BlackList实现。标签方式最简单——直接在对应成员上加[BlackList]即可。由于需要考虑把重载函数中的某一个重载列入黑名单配置方式的类型较复杂为ListListstring第一层 List 的每个条目对应一个成员第二层 List 是 string 列表第一个 string 是类型的全路径名第二个 string 是成员名如果成员是方法还需从第三个 string 开始把其参数的类型全路径全部列出。文档给出的经典示例把GameObject.networkView属性与FileInfo.GetAccessControl方法列入黑名单[BlackList] public static ListListstring BlackList new ListListstring() { new Liststring(){UnityEngine.GameObject, networkView}, //new Liststring(){ typeof(UnityEngine.GameObject).FullName, networkView}, new Liststring(){System.IO.FileInfo, GetAccessControl, System.Security.AccessControl.AccessControlSections}, //new Liststring(){ typeof(System.IO.FileInfo).FullName, GetAccessControl, typeof(System.Security.AccessControl.AccessControlSections).FullName }, };注释中给出了用typeof(...).FullName替代手写字符串的等价写法。仓库 ExampleConfig.cs 保留了完整的企业级黑名单涉及 XmlNodeList、WWW、Texture2D、Security、Light、FileInfo、DirectoryInfo、MonoBehaviour 等并额外提供了基于FuncMemberInfo, bool的MethodFilter动态过滤器L277-L305可对泛型Dictionary,的构造器与方法如TryAdd、两参Remove进行更精细的排除——这是标签方式无法表达的场景。六、生成期配置GenPath 与 GenCodeMenu以下配置属于生成期配置必须放到 Editor 目录下配置类型说明CSObjectWrapEditor.GenPathstring配置生成代码的放置路径默认放在Assets/XLua/Gen/下CSObjectWrapEditor.GenCodeMenu无参数函数 标签用于生成引擎的二次开发执行XLua/Generate Code菜单时会触发该函数的调用GenPath用于自定义适配代码的输出目录GenCodeMenu则提供钩子让开发者可以在 Unity 菜单栏执行XLua/Generate Code生成代码的同时自动触发自己的扩展逻辑例如生成后自动编译、拷贝产物等。七、配置策略建议与实战要点结合本文配置与 jynew 仓库实践总结如下落地要点白名单优先、标签兜底日常配置以 Editor 目录下的静态列表/动态列表为主标签仅用于少量确定类型避免 il2cpp 下代码量膨胀。平台安全双保险凡 Lua 需要访问的类型要么LuaCallCSharp生成适配、性能最优要么ReflectionUse防 il2cpp 裁剪、反射兜底否则多平台发布存在运行时不可访问风险。值类型优先 GCOptimize战斗、UI 高频传递的Vector2/3/4、Color、Quaternion及自定义纯值类型 struct配合AdditionalProperties处理私有字段可显著减少 GC 分配。delegate/interface 记得 CSharpCallLuaC# 事件回调、ListT.ForEach、LuaTable 绑定 delegate以及 jynew 中LBattleConfig这类Lua 配置表 → C# interface的数据桥接都必须显式配置。用 BlackList 处理平台差异不同平台如 WebGL不可用或不需要的成员参考 ExampleConfig.cs 中的#if UNITY_WEBGL分支应通过黑名单或MethodFilter排除避免生成无法编译或运行时出错的代码。相关文件索引配置总文档jyx2/Assets/XLua/Doc/configure.md所有配置 Attribute 的源码定义与SysGenConfig内置配置jyx2/Assets/XLua/Src/GenAttributes.cs静态列表配置示例jyx2/Assets/XLua/Examples/ExampleGenConfig.cs动态列表、自动化配置与黑名单示例jyx2/Assets/XLua/Editor/ExampleConfig.cs免 GCGCOptimize CSharpCallLua完整示例jyx2/Assets/XLua/Examples/05_NoGc/NoGc.cs项目实战CSharpCallLua 接口解读 Lua 战斗配置表jyx2/Assets/Scripts/LuaCore/Jyx2LuaToCsBridge.csLua 侧配置数据与初始化jyx2/Assets/LuaScripts、jyx2/Assets/Mods/JYX2【免费下载链接】jynewJinYongLegend-like RPG Game Framework with full Modding support and 10 hours playable samples of game.项目地址: https://gitcode.com/GitHub_Trending/jy/jynew创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考