找回密码
 立即注册
查看: 278|回复: 0

Unity XLua 配置 标签

[复制链接]
发表于 2021-8-14 08:30 | 显示全部楼层 |阅读模式
根据前面的文章,我们会发现我们会在很多类前面加很多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标签:
  1. [LuaCallCSharp]
  2. public classA {
  3. }
复制代码
该方式方便,但在il2cpp下会增加不少的代码量,不建议使用
静态列表

有时我们无法直接给一个类型打标签,比如系统api,没源码的库,或者实例化的泛化类型,这时你可以在一个静态类里声明一个静态字段,该字段的类型除BlackList和AdditionalProperties之外只要实现了IEnumerable<Type>就可以了(这两个例外后面具体会说),然后为这字段加上标签:
  1. [LuaCallCSharp]
  2. public static List<Type> mymodule_lua_call_cs_list = new List<Type>() {
  3.     typeof(GameObject),
  4.     typeof(Dictionary<string, int>),
  5. };
复制代码
这个字段需要放到一个静态类里头,建议放到Editor目录动态列表

声明一个静态属性,打上相应的标签即可。
  1. [Hotfix]
  2. public static List<Type> by_property {
  3.     get {
  4.         return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
  5.                 where type.Namespace == "XXXX"
  6.                 select type).ToList();
  7.     }
  8. }
复制代码
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文件中会添加对应的配置,如下:
  1. public class CSCallLua{
  2.     [CSharpCallLua]  
  3.     public interface ItfD {  
  4.         int f1 { get; set; }  
  5.         int f2 { get; set; }  
  6.         int add(int a, int b);  
  7.     }
  8. }
复制代码




这种方式就属于上述的打标签,是官方不建议使用的方式,那我们看看如何用静态列表,动态列表来实现:
我们先用静态列表实现,按照官方建议,我们现在Editor目录下新建一个XLuaConfig.cs的脚本,内容如下(记得把之前在interface前加的CSharpCallLua的标签删除)
  1. namespace MyExamples {
  2.     public static class XLuaConfig {
  3.         [CSharpCallLua]
  4.         public static List<Type> cSharpCallLuaList = new List<Type>(){
  5.             typeof(CSCallLua.ItfD),
  6.         };
  7.     }
  8. }
复制代码
然后我们执行XLua->Clean Generate Code后执行XLua->Generate Code。会发现相比之前的在gen下生产的代码是一样的。

接着是动态列表的写法,动态相比静态,我们可以用函数来实现需要添加的类别,这样就可以进行例如读配置,按条件筛选等等功能,如下:
  1. [CSharpCallLua]
  2. public static List<Type> by_property {
  3.     get {
  4.         //可以添加一些逻辑代码筛选,比如
  5.         List<Type> list = new List<Type>(){
  6.             typeof(CSCallLua.ItfD),
  7.         };
  8.         return (from type in list
  9.                 where type.FullName.Contains("CSCallLua")
  10.                 select type).ToList();
  11.     }
  12. }
复制代码
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三个类
  1. local GameObject = CS.UnityEngine.GameObject;
  2. local go = GameObject('go');
  3. --typeof
  4. go:AddComponent(typeof(CS.UnityEngine.ParticleSystem));
  5. --访问成员方法属性
  6. local Test = CS.MyExamples.Test;
  7. local test = Test();
  8. test.index = 66;
  9. print('test.index---'..test.index);
复制代码
在C#处,我们只需执行lua代码的前后记录下时间算个时间差即可
  1. void Start () {
  2.     luaenv = new LuaEnv();
  3.     float start = Time.realtimeSinceStartup;
  4.     luaenv.DoString(script);
  5.     float end = Time.realtimeSinceStartup;
  6.     Debug.Log(String.Format("{0:F6}", end - start));
  7. }
复制代码
在不打标签的情况下,即反射访问,运行结果如下:


然后我们打标签,Generate Code之后运行,结果如下:
  1. [LuaCallCSharp]
  2. public static List<Type> luaCallCSharpList = new List<Type>() {
  3.     typeof(Action),
  4.     typeof(GameObject),
  5.     typeof(ParticleSystem),
  6. };
复制代码


可以发现短短的几行代码,三个类的访问,反射访问慢了四五倍。

ReflectionUse

上面说到,如果没有添加LuaCallCSharp标签,lua会使用反射的方式去C#中的类型,但是这种方式除了性能不好外,还有个隐患就是在il2cpp下还有可能因为代码剪裁而导致无法访问。所以就轮到ReflectionUse这个标签出场了。

一个C#类型类型加了这个配置,xLua会生成link.xml阻止il2cpp的代码剪裁。
对于扩展方法必须加上LuaCallCSharp或者ReflectionUse才可以被访问到。
建议所有要在Lua访问的类型,要么加LuaCallCSharp,要么加上ReflectionUse,这才能够保证在各平台都能正常运行。
BlackList

顾名思义,黑名单的功能,如果你不要生成一个类型的一些成员的适配代码,你可以通过这个配置来实现。
标签方式比较简单,对应的成员上加就可以了。

列表配置的方式就比较复杂(由于考虑到有可能需要把重载函数的其中一个重载列入黑名单),类型是List<List<string>>,List<List<string>>中的每一个List<string>对应一个类别的其中一个屏蔽(即若同一个类型需要屏蔽多个成员,则要多个List<string>一一对应)。每个List<string>里面的String即对应屏蔽信息,第一个string为类别的全称(namespace name+class name),第二个string对应要屏蔽的成员,若成员为方法,则需要在第三个以及之后(第四,第五...)加上方法参数的全称(这样就可以做到重载方法的区分)。
例如,我们有如下的class:
  1. public class NeedBlackListClass {
  2.     public int x;
  3.     public string s;
  4.     public void Add(int y) {
  5.         x += y;
  6.         Debug.Log("add---x:" + x);
  7.     }
  8.     public void Add(int y, GameObject go) {
  9.         x += y;
  10.         Debug.Log("add---x:" + x + "----go---" + go.name);
  11.     }
  12. }
复制代码
现需要屏蔽s属性和Add(int y, GameObject go)方法,对应的列表定义如下:
  1. [BlackList]
  2. public static List<List<string>> blackList = new List<List<string>>()  {
  3.      new List<string>(){ "MyExamples.NeedBlackListClass", "s"},
  4.      new List<string>(){ "MyExamples.NeedBlackListClass", "Add", "System.Int32", "UnityEngine.GameObject"},
  5. };
复制代码
然后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进行热更。
  1. [Hotfix]
  2. public static List<Type> by_field = new List<Type>() {
  3.     typeof(HotFixSubClass),
  4.     typeof(GenericClass<>),
  5. };
  6. [Hotfix]
  7. public static List<Type> by_property {
  8.     get {
  9.         return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
  10.                 where type.Namespace == "XXXX"
  11.                 select type).ToList();
  12.     }
  13. }
复制代码

生成期配置

下面配置,必须放到Editor目录下
CSObjectWrapEditor.GenPath

配置生成代码的放置路径,类型是string。默认放在"Assets/XLua/Gen/"下,如。
  1. [CSObjectWrapEditor.GenPath]
  2. public static string genPath = "Assets/Scripts/Tools/XLua/Gen/";
复制代码
CSObjectWrapEditor.GenCodeMenu

该配置用于生成引擎的二次开发,一个无参数函数加了这个标签,在执行"XLua/Generate Code"菜单时,执行结束会触发这个函数的调用。
  1. [CSObjectWrapEditor.GenCodeMenu]
  2. public static void XLuaGenerateCodeFinish() {
  3.     Debug.Log("XLuaGenerateCodeFinish");
  4. }
复制代码

其他一些不常用的标签大家自己可以自己看一下官方文档。若之后发现有需要注意的点会再补充。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 11:48 , Processed in 0.092286 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表