Unity XLua 配置 标签

版权声明:辛辛苦苦写的,转载请注明出处噢 https://blog.csdn.net/wangjiangrong/article/details/79916646

根据前面的文章,我们会发现我们会在很多类前面加很多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<Type>就可以了(这两个例外后面具体会说),然后为这字段加上标签:

[LuaCallCSharp]
public static List<Type> mymodule_lua_call_cs_list = new List<Type>() {
    typeof(GameObject),
    typeof(Dictionary<string, int>),
};
这个字段需要放到一个 静态类 里头,建议放到 Editor目录

动态列表

声明一个静态属性,打上相应的标签即可。

[Hotfix]
public static List<Type> 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<Type> cSharpCallLuaList = new List<Type>(){
            typeof(CSCallLua.ItfD),
        }; 
    }
}
然后我们执行XLua->Clean Generate Code后执行XLua->Generate Code。会发现相比之前的在gen下生产的代码是一样的。

接着是动态列表的写法,动态相比静态,我们可以用函数来实现需要添加的类别,这样就可以进行例如读配置,按条件筛选等等功能,如下:

[CSharpCallLua]
public static List<Type> by_property {
    get {
        //可以添加一些逻辑代码筛选,比如
        List<Type> list = new List<Type>(){
            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<Type> luaCallCSharpList = new List<Type>() {
    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<string>>,List<List<string>>中的每一个List<string>对应一个类别的其中一个屏蔽(即若同一个类型需要屏蔽多个成员,则要多个List<string>一一对应)。每个List<string>里面的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<List<string>> blackList = new List<List<string>>()  {
     new List<string>(){ "MyExamples.NeedBlackListClass", "s"},
     new List<string>(){ "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<Type> by_field = new List<Type>() {
    typeof(HotFixSubClass),
    typeof(GenericClass<>),
};

[Hotfix]
public static List<Type> by_property {
    get {
        return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
                where type.Namespace == "XXXX"
                select type).ToList();
    }
}


生成期配置

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

CSObjectWrapEditor.GenPath

配置生成代码的放置路径,类型是string。默认放在"Assets/XLua/Gen/"下,如。

[CSObjectWrapEditor.GenPath]
public static string genPath = "Assets/Scripts/Tools/XLua/Gen/";

CSObjectWrapEditor.GenCodeMenu

该配置用于生成引擎的二次开发,一个无参数函数加了这个标签,在执行"XLua/Generate Code"菜单时,执行结束会触发这个函数的调用。

[CSObjectWrapEditor.GenCodeMenu]
public static void XLuaGenerateCodeFinish() {
    Debug.Log("XLuaGenerateCodeFinish");
}


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

猜你喜欢

转载自blog.csdn.net/wangjiangrong/article/details/79916646