ARTICLE DETAIL

资讯详情

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

微信小游戏 Unity 适配器 xLua 复杂值类型(struct)GC 优化完整指南

微信小游戏 Unity 适配器 xLua 复杂值类型(struct)GC 优化完整指南 游戏开发移动开发WebAssembly【免费下载链接】minigame-unity-webgl-transform微信小游戏Unity引擎适配器文档。项目地址https://gitcode.com/GitHub_Trending/mi/minigame-unity-webgl-transform点击查看免费下载xLua 中C# 复杂值类型struct默认以引用方式传递到 Lua 侧每次传递都会产生一次 boxing 与一次 C# GC 分配。本文以 xLua 复杂值类型structgc 优化指南 为骨架结合仓库内 xLua 的配置、无 GC 示例 与生成器源码 Generator.cs系统讲解 struct GC 优化方案的工作原理、满足条件、三种配置方式与落地步骤帮助读者在微信小游戏场景下把 Vector 系列、Quaternion、Color 等值类型在 Lua 与 C# 之间的传递彻底做到无 GC 分配。复杂值类型为什么会产生 GC在 xLua 中复杂值类型struct的默认传递方式是引用传递。这个机制的工作过程是值类型需要先进行boxing装箱在托管堆上创建一个新对象将装箱后的引用传递给 Lua 侧使用Lua 侧使用完毕、释放该对象的引用时触发一次 C# GC。由于每次 boxing 都会产生一个新对象因此只要 struct 在 Lua 与 C# 之间来回传递一次托管堆上就会多出一个待回收的垃圾对象。在高频调用如每帧更新位置、逐物体同步坐标的场景下这类分配会迅速累积直接表现为 GC 频率上升、CPU 峰值抖动在内存更敏感的微信小游戏运行时WASM上影响尤为明显。为此xLua 实现了一套struct 的 GC 优化方案只要通过简单配置满足条件的 struct 在传递到 Lua 侧时即可做到无 GC。struct 需要满足什么条件要让一个 struct 享受到 GC 优化需要同时满足以下三个条件1. 字段类型约束struct 允许嵌套其它 struct但它以及它嵌套的 struct 只能包含以下基本类型byte、sbyte、short、ushort、int、uint、long、ulong、float、double例如 Unity 定义的大多数值类型Vector 系列、Quaternion、Color 等以及用户自定义的满足上述字段约束的 struct 均符合条件。字段不能包含 string、引用类型对象或其它不符合约束的类型。2. 配置 GCOptimize 属性该 struct 必须配置GCOptimize属性。对于常用的 UnityEngine 值类型Vector 系列、Quaternion、Color 等xLua 已经内置配置好了用户无需重复配置用户自定义 struct 则需要自行配置。属性可以通过配置文件或C# Attribute两种方式实现详见下文。3. 加入生成代码列表使用到该 struct 的地方struct 自身、相关 delegate、相关数组等需要添加到生成代码列表并执行代码生成让 xLua 为该类型生成专门的 GC 优化包装代码。如何配置三种方式详解xLua 的所有配置都支持三种方式打标签Attribute、静态列表、动态列表。GCOptimize同样遵循这一规则。下面逐一展开。方式一C# Attribute 打标签直接为 struct 类型打上GCOptimizeAttribute同时建议打上LuaCallCSharp。仓库示例 05_NoGc/NoGc.cs 中的自定义 struct 就是这样配置的using XLua; [GCOptimize] [LuaCallCSharp] public struct Pedding { public byte c; } [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; }注意MyStruct嵌套了同样打了GCOptimize标签的Pedding且所有字段均为值类型完全符合前述字段类型约束。从属性定义源码 GenAttributes.cs 可以看到GCOptimizeAttribute还带一个可选的OptimizeFlag参数默认值为OptimizeFlag.Default[Flags] public enum OptimizeFlag { Default 0, PackAsTable 1 } public class GCOptimizeAttribute : Attribute { OptimizeFlag flag; public OptimizeFlag Flag { get { return flag; } } public GCOptimizeAttribute(OptimizeFlag flag OptimizeFlag.Default) { this.flag flag; } }方式二静态列表配置有时我们无法直接给一个类型打标签例如系统 API、没有源码的第三方库中的类型这时可以在一个静态类里声明一个静态字段字段类型只要实现了IEnumerableType即可BlackList和AdditionalProperties例外再为这个字段打上GCOptimize标签using System; using System.Collections.Generic; using XLua; public static class MyGCOptimizeConfig { [GCOptimize] public static ListType gc_optimize_list new ListType() { typeof(MyNamespace.MyStruct), typeof(MyNamespace.MyStruct2), }; }字段需要放在静态类中且建议放在Editor 目录下。这也是 xLua 对几个 UnityEngine 值类型的官方内置配置方式。在 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), }; } } ... }因此 Vector2/Vector3/Vector4、Color、Quaternion、Ray、Bounds、Ray2D 这些常用值类型无需任何额外配置即可享受 GC 优化直接满足原文档中UnityEngine 的几个 struct 已经配置了该属性的说明。方式三动态列表配置如果希望按名字空间、按程序集等规则动态收集类型可以声明一个打了相应标签的静态属性。GCOptimize同样支持这种方式using System; using System.Collections.Generic; using System.Linq; using XLua; public static class MyGCOptimizeConfig { [GCOptimize] public static IEnumerableType by_property { get { return from type in Assembly.Load(Assembly-CSharp).GetTypes() where type.Namespace MyGame.Data type.IsValueType select type; } } }Getter 是代码可以实现很多效果比如按名字空间配置、按程序集配置等等。该属性同样需要放到静态类中建议放到Editor 目录。配置通用建议来自 xLua 的配置列表方式均必须是static的字段/属性且必须放到一个static类建议尽量不用标签方式il2cpp 下会增加不少代码量建议列表方式配置放在 Editor 目录。使用到该 struct 的地方也要加入生成列表除了 struct 本身凡是在 Lua 与 C# 之间传递该 struct 的入口也需要加入生成代码列表才能让无 GC 路径真正生效。这些入口包括携带 struct 参数的delegateC# 调用 Lua 函数时——需要打CSharpCallLua携带 struct 参数的 C# 方法——所在的类型需要打LuaCallCSharpstruct 数组——数组访问路径同样走 GC 优化代码。仓库 05_NoGc/NoGc.cs 给出了完整示例把 struct、枚举、delegate、数组全部串联起来[GCOptimize] [LuaCallCSharp] public struct MyStruct { ... } [LuaCallCSharp] public enum MyEnum { E1, E2 } [CSharpCallLua] public delegate int IntParam(int p); [CSharpCallLua] public delegate Vector3 Vector3Param(Vector3 p); [CSharpCallLua] public delegate MyStruct CustomValueTypeParam(MyStruct p); [CSharpCallLua] public delegate MyEnum EnumParam(MyEnum p); [CSharpCallLua] public delegate decimal DecimalParam(decimal p); [CSharpCallLua] public delegate void ArrayAccess(Array arr);配置完成后需要执行XLua/Generate Code菜单生成代码。生成器源码 Generator.cs 中的逻辑印证了收集流程GCOptimizeList收集所有被GCOptimizeAttribute标注的类型包括SysGenConfig静态列表里的内置类型随后通过GenPackUnpack(types, save_path)为这些类型生成解包/打包代码最终输出到Assets/XLua/Gen/目录路径可通过CSObjectWrapEditor.GenPath配置修改。无 GC 场景与示例验证完成配置后以下场景中的值类型传递均不产生C#GC 分配来自 FAQ 与官方无 GC 示例的验证所有基本值类型所有整数、所有浮点数、decimal——本身无 GC所有枚举类型——需要把该枚举加入GCOptimize字段只包含值类型的 struct可嵌套其它只包含值类型的 struct——需要把该 struct 加入GCOptimize。使用方式包括用 delegate 调用 Lua 函数、传值类型参数用LuaTable、LuaFunction的无 GC 接口如FuncT1, T2, TResult传值类型传递值类型数组如Vector3[]、自定义 struct 数组并允许 Lua 侧直接读写数组元素通过 interfaceCSharpCallLua让 Lua 实现回调并交换数组内容通过LuaEnv.Global.Set/Get在 Lua 与 C# 之间传递 struct 值。NoGc 示例的运行时验证代码展示了几条关键路径NoGc.cs// C# 通过 delegate 调用 Lua传 Vector3 / 自定义 struct / 枚举均无 GC f2(new Vector3(1, 2, 3)); MyStruct mystruct1 new MyStruct(5, 6); f3(mystruct1); f4(MyEnum.E1); // Lua 访问 C# 值类型数组无 GC含 primitive / Vector3 / 自定义 struct / enum / decimal 数组 farr(a1); farr(a2); farr(a3); farr(a4); farr(a5); // Lua 侧也可用 table 构造 struct 参数传入 C#例如 -- local r monoBehaviour:Vector3ParamMethod({x 1, y 2, z rnd}) -- local r monoBehaviour:StructParamMethod({a 1, b rnd, e {c rnd}})Lua 侧同样可以无 GC 地读写 C# 的 struct 数组元素例如交换Vector3[]中两个元素function array_exchange(arr) arr[0], arr[1] arr[1], arr[0] end进阶AdditionalProperties 处理私有字段默认情况下GCOptimize只对public 的 field打解包。如果某些 struct 把字段做成私有、通过 property 对外访问就需要用到XLua.AdditionalProperties这一扩展配置。标签方式比较简单在对应属性上加标签即可配置方式要求是DictionaryType, Liststring类型Key 是要生效的类型Value 是属性名列表。可参考 xLua 对几个 UnityEngine 值类型的内置配置SysGenConfig类GenAttributes.cs[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 } }, }; } }可以看到Ray、Ray2D、Bounds这类 struct 虽然底层字段是私有如m_Origin、m_Direction、m_Center、m_Extents但通过AdditionalProperties把origin、direction、center、extents属性纳入 GC 优化的解包范围从而让这些类型的完整属性访问也做到无 GC。实操清单与注意事项在微信小游戏 Unity 工程中落地 struct GC 优化按以下步骤执行即可确认字段约束struct含嵌套 struct只包含byte/sbyte/short/ushort/int/uint/long/ulong/float/double字段配置 GCOptimize自定义 struct 打[GCOptimize]标签或在 Editor 目录的静态类中通过静态列表/动态列表配置UnityEngine 的 Vector 系列、Quaternion、Color 等已内置配置无需处理登记使用入口把携带该 struct 的 delegate/interface 加入CSharpCallLua把暴露该 struct 方法的 C# 类型加入LuaCallCSharp执行代码生成在 Unity 菜单执行XLua/Generate Code生成代码默认输出到Assets/XLua/Gen/按无 GC 接口调用优先使用 delegate 调用、LuaTable/LuaFunction无 GC 接口与数组访问路径私有字段场景如 struct 通过 property 暴露私有字段用AdditionalProperties补充配置。需要注意的前提与限制字段包含 string、引用类型等不符合约束的 struct无法启用 GC 优化会退回默认的引用传递boxing 释放路径GC 优化依赖生成代码配置变更后必须重新执行 Generate Code该方案解决的是C# 侧 GC 分配Lua 侧自身的 GC 策略如LuaEnv.FullGc、GcPause、GcStepmul调优属于另一个话题可参考 FAQ 中的相关说明本仓库的 xLua 位于微信小游戏 WebGL 工程 Demo/xLua_WebGL 中上述配置与生成流程在 Unity WebGL / 微信小游戏导出目标下同样适用。赞分享游戏开发移动开发WebAssembly【免费下载链接】minigame-unity-webgl-transform微信小游戏Unity引擎适配器文档。项目地址https://gitcode.com/GitHub_Trending/mi/minigame-unity-webgl-transform点击查看免费下载相关推荐xLua 复杂值类型structGC 优化指南免 GC 传递原理、配置条件与实战示例xLua 复杂值类型structGC 优化指南免 GC 传递原理、配置条件与实战示例 xLua 中 C struct值类型默认以引用方式跨语言传递每游戏开发脚本语言集成一次跑通 yuzu Switch 模拟器PC 免费玩起 Switch 游戏一次跑通 yuzu Switch 模拟器PC 免费玩起 Switch 游戏 yuzu 是一款开源的 Switch 模拟器能让你的 PC 直接免费运行 Swi虚拟化桌面应用图形学Unity游戏微信小游戏快速适配完整指南微信小游戏Unity WebGL适配方案简称Unity WebGL小游戏适配是一套专门为Unity游戏开发者设计的完整解决方案。该方案基于WebAssemb游戏开发移动开发WebAssembly上一篇告别单元测试痛点FakeItEasy 零基础到精通实战指南下一篇未来已来2025年基于大语言模型的机器人技术发展趋势预测创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表