当前位置: 首页 > news >正文

Unity中 Xlua使用整理(二)

1.Xlua的配置应用

xLua所有的配置都支持三种方式:打标签静态列表动态列表
配置要求
列表方式均必须是static的字段/属性
列表方式均必须放到一个static类
建议不用标签方式
建议列表方式配置放Editor目录(如果是Hotfix配置,而且类位于Assembly-CSharp.dll之外的其它dll,必须放Editor目录)

1.打标签

xLua用白名单来指明生成哪些代码,而白名单通过attribute来配置,该方式方便,但在il2cpp下会增加不少的代码量,不建议使用

1.LuaCallCSharp

一个C#类型加了这个配置,xLua会生成这个类型的适配代码(包括构造该类型实例,访问其成员属性、方法,静态属性、方法)。一个类型的扩展方法(Extension Methods)加了这配置,也会生成适配代码并追加到被扩展类型的成员方法上。xLua只会生成加了该配置的类型,不会自动生成其父类的适配代码,当访问子类对象的父类方法,如果该父类加了LuaCallCSharp配置,则执行父类的适配代码。如果没有适配代码则会尝试用反射来访问,但反射访问性能不佳,在il2cpp下有可能因为代码剪裁而导致无法访问。

1.打标签

[LuaCallCSharp]
public class TestObj
{public void test(testStruct t){Debug.Log($"{t.a},{t.c}");}
}

2.静态列表

   [LuaCallCSharp]public static List<Type> luaCallCsharp = new List<Type>{typeof(TestObj),};

  上面的类加了此标签,就会生成适配代码

 2.ReflectionUse

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

如何配置?

[ReflectionUse]
public class TestReflectionUseObj
{}

link.xml保存着打上LuaCallCSharp和ReflectionUse标签的类

3.DoNotGen

指明一个类里头的部分函数、字段、属性不生成代码,通过反射访问。
只能标准 Dictionary<Type, List<string>> 的field或者property。key指明的是生效的类,value是一个列表,配置的是不生成代码的函数、字段、属性的名字。

如何配置?

[LuaCallCSharp]
public class TestDontGen
{public int a1;public int a2;
}

1.打标签(这种方式行不通)

在生成代码中,a2还是生成和a1一样的适配函数

2.静态列表

public static class ExportCfg
{[DoNotGen]public static Dictionary<Type,List<string>> dontGenDic = new Dictionary<Type, List<string>> {[typeof(TestDontGen)] = new List<string> { "a2" },};
}

4.CSharpCallLua

把一个lua函数适配到一个C# delegate(一类是C#侧各种回调:UI事件,delegate参数,比如List<T>:ForEach;另外一类场景是通过LuaTable的Get函数指明一个lua函数绑定到一个delegate),或者把一个lua table适配到一个C# interface时,使用该标签。

如何配置?

1.打标签:

   [CSharpCallLua]public interface IPlayerPosition{int x { get; set; }int y { get; set; }int z { get; set; }void add(int x, int y, int z);void sub(int x, int y, int z);}[CSharpCallLua]public delegate void AddMethod(LuaTable self, int x, int y, int z);[CSharpCallLua]public delegate Action addAc(LuaTable t, int x, int y, int z);[CSharpCallLua]public delegate void test1(int x);[CSharpCallLua]public delegate Action test2(int x);

2.静态列表:

[CSharpCallLua]
public static List<Type> CsharpCallLua = new List<Type> { typeof(IPlayerPosition),typeof(AddMethod),typeof(addAc),typeof(test1),typeof(test2),
};

 生成代码如下:接口生成独立文件,委托事件生成在DelegateGenBridge文件中

 

5.GCOptimize

xLua无参构造函数的复杂类型(struct)的默认传递方式是引用传递,当lua侧使用完毕释放该对象的引用时,会产生一次gc。
一个C#纯值类型(注:指的是一个只包含值类型的struct,可以嵌套其它只包含值类型的struct)或者C#枚举值加上这个标签之后,XLua会生成gc优化代码,在lua和c#间传递将不产生(C#)gc alloc,该类型的数组(一维?)访问也不产生gc。

如果没有配置该标签的值类型传递

public class Test{   [CSharpCallLua]public delegate Action testAction(TestGCOptimizeValue x);void Start(){luaenv = new LuaEnv();luaenv.AddLoader(customLoader);luaenv.DoString("require 'main'");ta = luaenv.Global.Get<testAction>("testAction");}private void Update(){if (Application.isPlaying){ta?.Invoke(ts);}}
}public struct TestGCOptimizeValue
{public int a;public int b;private float f;//[AdditionalProperties]public float F { get => f; set => f = value; }
}

生成的代码如下:

将struct类型装箱转换为object再进行处理。

如何配置?

[GCOptimize]
public struct TestGCOptimizeValue
{public int a;public int b;public float f;
}

 加上此标签后,就会在PackUnpack中生成适配代码

 

然后调用函数生成代码如下:

struct 满足条件如下:

1.struct允许嵌套其它struct,但它以及它嵌套的struct只能包含这几种基本类型:byte、sbyte、short、ushort、int、uint、long、ulong、float、double;例如UnityEngine定义的大多数值类型:Vector系列,Quaternion,Color。。。均满足条件,或者用户自定义的一些struct
2.该struct配置了GCOptimize属性(对于常用的UnityEngine的几个struct,Vector系列,Quaternion,Color。。。均已经配置了该属性),这个属性可以通过配置文件或者C# Attribute实现;
3.使用到该struct的地方,需要添加到生成代码列表;

6.AdditionalProperties

该标签GCOptimize的扩展配置,如果struct有的字段是私有的,需要通过属性来访问,这时就需要用到该配置(默认情况下GCOptimize只对public的field打解包)

如何配置?

如果不加此标签

[GCOptimize]
public struct TestGCOptimizeValue
{public int a;public int b;private float f;public float F { get => f; set => f = value; }
}

生成代码如下:无法生成访问f字段的代码

1.打标签方式

[GCOptimize]
public struct TestGCOptimizeValue
{public int a;public int b;private float f;[AdditionalProperties]public float F { get => f; set => f = value; }
}

 2.静态列表

  [AdditionalProperties]static Dictionary<Type, List<string>> AdditionalProperties = new Dictionary<Type, List<string>>{[typeof(TestGCOptimizeValue)] = new List<string> { "F" },};

生成代码如下:

7.BlackList

一个类型的一些成员不需要适配代码,可以通过加上该标签来实现。考虑到有可能需要把重载函数的其中一个重载列入黑名单,配置方式比较复杂,类型是 List<List<string>>,对于每个成员,在第一层List有一个条目,第二层List是个string的列表,第一个string是类型的全路径名第二个string是成员名如果成员是一个方法,还需要从第三个string开始,把其参数的类型全路径全列出来

如何配置?

[LuaCallCSharp]
public class TestBlackList
{public int a1;public int a2;public void Test(){}
}

若不加处理生成代码如下: 

1.打标签

C#代码:

[LuaCallCSharp]
public class TestBlackList
{public int a1;[BlackList]public int a2;[BlackList]public void Test(){}
}

生成代码如下: 

2.静态列表

列表中的第一个字符串是类型,第二个及以后是要屏蔽的字段

  [BlackList]public static List<List<string>> BlackList = new List<List<string>>(){new List<string>(){ "TestBlackList", "a2"},new List<string>(){ "TestBlackList", "Test"}};

生成代码:

注:
DoNotGen和ReflectionUse的区别是:1、ReflectionUse指明的是整个类;2、当第一次访问一个函数(字段、属性)时,ReflectionUse会把整个类都wrap,而DoNotGen只wrap该函数(字段、属性);
DoNotGen和BlackList的区别是:1、BlackList配了就不能用;2、BlackList能指明某重载函数,DoNotGen不能;

2.静态列表

有时我们无法直接给一个类型打标签,比如系统api,没源码的库,或者实例化的泛化类型,这时可以在一个静态类里声明一个静态字段,然后为这字段加上标签,并且这个字段需要放到一个静态类里,建议放到 Editor目录。

如何配置?

public static class ExportCfg
{[DoNotGen]public static Dictionary<Type, List<string>> dontGenDic = new Dictionary<Type, List<string>>{[typeof(TestDontGen)] = new List<string> { "a2" },};[BlackList]public static List<List<string>> BlackList = new List<List<string>>(){new List<string>(){ "TestBlackList", "a2"},new List<string>(){ "TestBlackList", "Test"}};[AdditionalProperties]public static Dictionary<Type, List<string>> AdditionalProperties = new Dictionary<Type, List<string>>{[typeof(TestGCOptimizeValue)] = new List<string> { "F" },};[LuaCallCSharp]public static List<Type> luaCallCsharp = new List<Type>{typeof(TestObj),};[CSharpCallLua]public static List<Type> CsharpCallLua = new List<Type> { typeof(IPlayerPosition),typeof(AddMethod),typeof(addAc),typeof(test1),typeof(test2),};
}

3.动态列表

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

见HotFix。

2.HotFix

1.标识要热更新的类型

1.打标签:(不建议,在高版本 Unity 不支持)

[Hotfix]
public class TestHotFix
{public void TestPrint(){Debug.Log("Before Hotfix");}
}

2.static列表:(高版本 Unity 需要把配置文件放 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();}
}[Hotfix]
public static List<Type> by_field = new List<Type>()
{typeof(HotFixSubClass),typeof(GenericClass<>),
};

2.API

1.xlua.hotfix(class, [method_name], fix)

class :C#类,CS.Namespace.TypeName或者字符串方式"Namespace.TypeName"表示,字符串格式和C#的Type.GetType要求一致,如果是内嵌类型(Nested Type)是非Public类型,只能用字符串方式表示"Namespace.TypeName+NestedTypeName";
method_name : 方法名,可选;
fix : 如果传了method_name,fix将会是一个function,否则通过table提供一组函数。table的组织按key是method_name,value是function的方式。

2.base(csobj)

子类override函数通过base调用父类实现。
csobj : 对象
返回值 : 新对象,可以通过该对象base上的方法

3.util.hotfix_ex(class, method_name, fix)

可以在fix函数里头执行原来的函数缺点是fix的执行会略慢。
method_name : 方法名;
fix : 用来替换C#方法的lua function

Lua侧示例:

xlua.hotfix(CS.BaseTest, 'Foo', function(self, p)print('BaseTest', p)base(self):Foo(p)
end)

3.打补丁

Xlua可以用lua函数替换 C# 的构造函数,函数,属性,事件的替换。

C#侧代码:

1.函数

method_name 传函数名,支持重载,不同重载都是转发到同一个 lua 函数。

2.构造函数

构造函数对应的 method_name 是 ".ctor"。和普通函数不一样的是,构造函数的热补丁是执行原有逻辑后调用lua。

3.属性

method_name 等于 get_AProp,setter 的 method_name 等于 set_AProp。

4.[]操作符

赋值对应 set_Item,取值对应 get_Item。第一个参数是 self,赋值后面跟 key,value,取值只有 key 参数,返回值是取出的值。

5.其他操作符

6.事件

7.析构函数

method_name 是 "Finalize",传一个 self 参数。析构函数的热补丁并不是替换,而是开头调用 lua 函数后继续原有逻辑。

8.泛型

其他规则一致,每个泛化类型实例化后都是一个独立的类型,只能针对实例化后的类型分别打补丁。

9.Unity协程

通过 util.cs_generator 可以用一个 function 模拟一个 IEnumerator,yield return使用在里头的 coroutine.yield模拟。

10.整个类

如果要替换整个类,使用一个 table,按 method_name = function 组织即可。

3.其他

 1.Unity协程的使用

 2.Async的使用

未完待续。。。

参考链接:

介绍 — XLua (tencent.github.io)


http://www.mrgr.cn/news/83054.html

相关文章:

  • RabbitMQ高级篇之MQ可靠性 数据持久化
  • 计算机网络之---物理层标准与协议
  • 嵌入式系统 (2.嵌入式硬件系统基础)
  • hive在大数据体系里面起到什么作用
  • 怎样用云手机进行海外社媒矩阵引流?
  • 二、BIO、NIO编程与直接内存、零拷贝
  • c语言———标准IO fgetc fputc fprintf fscanf【内附练习及代码】
  • 一、二极管(应用篇)
  • docker一键安装脚本(docker安装)
  • OSPF - 1类LSA(Router-LSA)
  • Python数据可视化-Pandas
  • JVM生产环境常用参数配置及调优建议
  • EXCEL: (二) 常用图表
  • 基于FPGA的洗衣机控制器电子定时器
  • mysql性能测试优化
  • IO模型与NIO基础
  • Ardupilot开源无人机之Geek SDK进展2024
  • PID学习资料
  • SSL VPN
  • rabbitmq——岁月云实战笔记
  • WebSocket 实现指南
  • 排序算法:冒泡排序
  • windows从0开始配置llamafactory微调chatglm3-6b
  • 【C语言】可移植性陷阱与缺陷(八): 随机数的大小
  • UE5 打包要点
  • 【学习笔记】数据结构(十)