|
Unity XLua 配置 标签
根据前面的文章,我们会发现我们会在很多类前面加很多XLua的标签,有LuaCallCSharp,CSharpCallLua,Hotfix 等等。关于这些配置的作用官方文档也有相应的说明:https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/configure.md。这里我已个人理解和一些demo,对几个比较重要的标签详细记录一下。
打标签的方式
xLua所有的配置都支持三种方式:打标签;静态列表;动态列表。
配置有两必须,两建议:
列表方式均必须是static的字段/属性列表方式均必须放到一个static类建议不用标签方式建议列表方式配置放Editor目录
打标签
xLua用白名单来指明生成哪些代码,而白名单通过attribute来配置,比如你想从lua调用c#的某个类,希望生成适配代码,你可以为这个类型打一个LuaCallCSharp标签:
[LuaCallCSharp] public classA { }
该方式方便,但在il2cpp下会增加不少的代码量,不建议使用。
静态列表
有时我们无法直接给一个类型打标签,比如系统api,没源码的库,或者实例化的泛化类型,这时你可以在一个静态类里声明一个静态字段,该字段的类型除BlackList和AdditionalProperties之外只要实现了IEnumerable就可以了(这两个例外后面具体会说),然后为这字段加上标签:
[LuaCallCSharp] public static List mymodule_lua_call_cs_list = new List() { typeof(GameObject), typeof(Dictionary), };
这个字段需要放到一个 静态类 里头,建议放到 Editor目录 。动态列表
声明一个静态属性,打上相应的标签即可。
[Hotfix] public static List by_property { get { return (from type in Assembly.Load("Assembly-CSharp").GetTypes() where type.Namespace == "XXXX" select type).ToList(); } }
Getter是代码,你可以实现很多效果,比如按名字空间配置,按程序集配置等等。
这个字段需要放到一个静态类里头,建议放到Editor目录。
[CSharpCallLua]
首先是[CSharpCallLua]这个标签,看过C#调用Lua的小伙伴们,肯定不会陌生。当我们要把lua中的table映射到C#中的interface或把lua中的function映射到C#中的delegate的时候,就需要给interface和delegate添加该标签,否则就会报错(InvalidCastException: This type must add to CSharpCallLua: XXX)。添加好标签后需要执行XLua->Generate Code,这个时候XLua会将我们打上标签的代码生成对应的代码(生成代码默认放在"Assets/XLua/Gen/"下)。
热更的建议:用反射找出所有函数参数、字段、属性、事件涉及的delegate类型,标注CSharpCallLua;
XLua会针对每个interface生成一个对应的脚本(文件名为 interface全路径名称 + Bridge),例如我们前面的例子中用到的如下interface,XLua会生成CSCallLuaItfDBridge脚本。同时在生成XLuaGenAutoRegister.cs文件中会添加对应的配置,如下:
public class CSCallLua{ [CSharpCallLua] public interface ItfD { int f1 { get; set; } int f2 { get; set; } int add(int a, int b); } }
这种方式就属于上述的打标签,是官方不建议使用的方式,那我们看看如何用静态列表,动态列表来实现:
我们先用静态列表实现,按照官方建议,我们现在Editor目录下新建一个XLuaConfig.cs的脚本,内容如下(记得把之前在interface前加的CSharpCallLua的标签删除)
namespace MyExamples { public static class XLuaConfig { [CSharpCallLua] public static List cSharpCallLuaList = new List(){ typeof(CSCallLua.ItfD), }; } }
然后我们执行XLua->Clean Generate Code后执行XLua->Generate Code。会发现相比之前的在gen下生产的代码是一样的。
接着是动态列表的写法,动态相比静态,我们可以用函数来实现需要添加的类别,这样就可以进行例如读配置,按条件筛选等等功能,如下:
[CSharpCallLua] public static List by_property { get { //可以添加一些逻辑代码筛选,比如 List list = new List(){ typeof(CSCallLua.ItfD), }; return (from type in list where type.FullName.Contains("CSCallLua") select type).ToList(); } }
delegate和interface同理。但是有一点不同的事,将delegate打入标签生成代码时,并不会生成一个新的脚本文件,而是会将所有的delegat配置到DelegatesGensBridge.cs文件中,如下我们为CSCallLua.FDelegate打标签生成代码后。
[LuaCallCSharp]
和上面的一样,我们在前面Lua调用C#的时候会用到这个标签,我们先来看看官方给的定义:
一个C#类型加了这个配置,xLua会生成这个类型的适配代码(包括构造该类型实例,访问其成员属性、方法,静态属性、方法),否则将会尝试用性能较低的反射方式来访问。
一个类型的扩展方法(Extension Methods)加了这配置,也会生成适配代码并追加到被扩展类型的成员方法上。
xLua只会生成加了该配置的类型,不会自动生成其父类的适配代码,当访问子类对象的父类方法,如果该父类加了LuaCallCSharp配置,则执行父类的适配代码,否则会尝试用反射来访问。
反射访问除了性能不佳之外,在il2cpp下还有可能因为代码剪裁而导致无法访问,后者可以通过下面介绍的ReflectionUse标签来避免。
热更建议:业务代码、引擎API、系统API,需要在Lua补丁里头高性能访问的类型,加上LuaCallCSharp。引擎API、系统API可能被代码剪裁调(C#无引用的地方都会被剪裁),如果觉得可能会新增C#代码之外的API调用,这些API所在的类型要么加LuaCallCSharp,要么加ReflectionUse。
上面这些就是官方给的解释,一样的,我们在之前里例子中,添加该标签然后Generate Code之后会发现,同样会生成一个新的C#脚本(namespace+classname+wrap)。
根据官方解释我们知道该标签并不是必要的,如果没有该标签会用反射的方式去访问,只是性能较差,这里我们可以用前面的demo简单的测试一下:
lua代码如下,我们用到了C#中的GameObject,ParticleSystem和Test三个类
local GameObject = CS.UnityEngine.GameObject; local go = GameObject('go'); --typeof go:AddComponent(typeof(CS.UnityEngine.ParticleSystem)); --访问成员方法属性 local Test = CS.MyExamples.Test; local test = Test(); test.index = 66; print('test.index---'..test.index);
在C#处,我们只需执行lua代码的前后记录下时间算个时间差即可
void Start () { luaenv = new LuaEnv(); float start = Time.realtimeSinceStartup; luaenv.DoString(script); float end = Time.realtimeSinceStartup; Debug.Log(String.Format("{0:F6}", end - start)); }
在不打标签的情况下,即反射访问,运行结果如下:
然后我们打标签,Generate Code之后运行,结果如下:
[LuaCallCSharp] public static List luaCallCSharpList = new List() { typeof(Action), typeof(GameObject), typeof(ParticleSystem), };
可以发现短短的几行代码,三个类的访问,反射访问慢了四五倍。
ReflectionUse
上面说到,如果没有添加LuaCallCSharp标签,lua会使用反射的方式去C#中的类型,但是这种方式除了性能不好外,还有个隐患就是在il2cpp下还有可能因为代码剪裁而导致无法访问。所以就轮到ReflectionUse这个标签出场了。
一个C#类型类型加了这个配置,xLua会生成link.xml阻止il2cpp的代码剪裁。
对于扩展方法,必须加上LuaCallCSharp或者ReflectionUse才可以被访问到。
建议所有要在Lua访问的类型,要么加LuaCallCSharp,要么加上ReflectionUse,这才能够保证在各平台都能正常运行。
BlackList
顾名思义,黑名单的功能,如果你不要生成一个类型的一些成员的适配代码,你可以通过这个配置来实现。
标签方式比较简单,对应的成员上加就可以了。
列表配置的方式就比较复杂(由于考虑到有可能需要把重载函数的其中一个重载列入黑名单),类型是List>,List>中的每一个List对应一个类别的其中一个屏蔽(即若同一个类型需要屏蔽多个成员,则要多个List一一对应)。每个List里面的String即对应屏蔽信息,第一个string为类别的全称(namespace name+class name),第二个string对应要屏蔽的成员,若成员为方法,则需要在第三个以及之后(第四,第五...)加上方法参数的全称(这样就可以做到重载方法的区分)。
例如,我们有如下的class:
public class NeedBlackListClass { public int x; public string s; public void Add(int y) { x += y; Debug.Log("add---x:" + x); } public void Add(int y, GameObject go) { x += y; Debug.Log("add---x:" + x + "----go---" + go.name); } }
现需要屏蔽s属性和Add(int y, GameObject go)方法,对应的列表定义如下:
[BlackList] public static List> blackList = new List>() { new List(){ "MyExamples.NeedBlackListClass", "s"}, new List(){ "MyExamples.NeedBlackListClass", "Add", "System.Int32", "UnityEngine.GameObject"}, };
然后Generate Code之后,在lua中访问s的时候就会报错(LuaException: cannot set s, no such field)访问Add(int y, GameObject go)方法则会去调用Add(int y)方法。
备注:我们也可以去看生成的对应wrap代码里面的内容:
Hotfix
hotfix标签,看过前面热更新的话就知道这是用于标识要热更的类型(可以是class或namespace)。若不在该标签下的类型,则无法用xlua.hotfix进行热更。
[Hotfix] public static List by_field = new List() { typeof(HotFixSubClass), typeof(GenericClass<>), }; [Hotfix] public static List by_property { get { return (from type in Assembly.Load(&#34;Assembly-CSharp&#34;).GetTypes() where type.Namespace == &#34;XXXX&#34; select type).ToList(); } }
生成期配置
下面配置,必须放到Editor目录下
CSObjectWrapEditor.GenPath
配置生成代码的放置路径,类型是string。默认放在&#34;Assets/XLua/Gen/&#34;下,如。
[CSObjectWrapEditor.GenPath] public static string genPath = &#34;Assets/Scripts/Tools/XLua/Gen/&#34;;
CSObjectWrapEditor.GenCodeMenu
该配置用于生成引擎的二次开发,一个无参数函数加了这个标签,在执行&#34;XLua/Generate Code&#34;菜单时,执行结束会触发这个函数的调用。
[CSObjectWrapEditor.GenCodeMenu] public static void XLuaGenerateCodeFinish() { Debug.Log(&#34;XLuaGenerateCodeFinish&#34;); } |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|