Xlua学习笔记
作者:互联网
简述:应用场景,快速学习-----------------------------博主:mx
Xlua-热更新优势:
直观优势
可以运行时把C#实现(方法,操作符,属性,事件等等)替换成lua实现;
出色的GC优化,自定义struct,枚举在Lua和C#间传递无C# gc alloc;【名词释义:GC ALLOC大概就是托管堆临时分配的内存】
编辑器下无需生成代码,开发更轻量;
并且支持为如下C#实现打补丁
构造函数
析构函数
成员函数
静态函数
泛化函数
操作符重载
成员属性
静态属性
事件
Xlua-Atrribute重要配置知识:
XLua.LuaCallCSharp
一个C#类型加了这个配置,xLua会生成这个类型的适配代码(包括构造该类型实例,访问其成员属性、方法,静态属性、方法),否则将会尝试用性能较低的反射方式来访问。
一个类型的扩展方法(Extension Methods)加了这配置,也会生成适配代码并追加到被扩展类型的成员方法上。
xLua只会生成加了该配置的类型,不会自动生成其父类的适配代码,当访问子类对象的父类方法,如果该父类加了LuaCallCSharp配置,则执行父类的适配代码,否则会尝试用反射来访问。
反射访问除了性能不佳之外,在il2cpp下还有可能因为代码剪裁而导致无法访问,后者可以通过下面介绍的ReflectionUse标签来避免。
XLua.ReflectionUse
一个C#类型类型加了这个配置,xLua会生成link.xml阻止il2cpp的代码剪裁。
对于扩展方法,必须加上LuaCallCSharp或者ReflectionUse才可以被访问到。
建议所有要在Lua访问的类型,要么加LuaCallCSharp,要么加上ReflectionUse,这才能够保证在各平台都能正常运行。
XLua.DoNotGen
指明一个类里头的部分函数、字段、属性不生成代码,通过反射访问。
只能标准Dictionary<Type, List>的field或者property。key指明的是生效的类,value是一个列表,配置的是不生成代码的函数、字段、属性的名字。
和ReflectionUse的区别是:1、ReflectionUse指明的是整个类;2、当第一次访问一个函数(字段、属性)时,ReflectionUse会把整个类都wrap,而DoNotGen只wrap该函数(字段、属性),换句话DoNotGen更lazy一些;
和BlackList的区别是:1、BlackList配了就不能用;2、BlackList能指明某重载函数,DoNotGen不能;
XLua.CSharpCallLua
如果希望把一个lua函数适配到一个C# delegate(一类是C#侧各种回调:UI事件,delegate参数,比如List:ForEach;另外一类场景是通过LuaTable的Get函数指明一个lua函数绑定到一个delegate)。或者把一个lua table适配到一个C# interface,该delegate或者interface需要加上该配置。
XLua.GCOptimize
一个C#纯值类型(注:指的是一个只包含值类型的struct,可以嵌套其它只包含值类型的struct)或者C#枚举值加上了这个配置。xLua会为该类型生成gc优化代码,效果是该值类型在lua和c#间传递不产生(C#)gc alloc,该类型的数组访问也不产生gc。各种无GC的场景,可以参考05_NoGc例子。
除枚举之外,包含无参构造函数的复杂类型,都会生成lua table到该类型,以及改类型的一维数组的转换代码,这将会优化这个转换的性能,包括更少的gc alloc。
XLua.AdditionalProperties
这个是GCOptimize的扩展配置,有的时候,一些struct喜欢把field做成是私有的,通过property来访问field,这时就需要用到该配置(默认情况下GCOptimize只对public的field打解包)。
标签方式比较简单,配置方式复杂一点,要求是Dictionary<Type, List>类型,Dictionary的Key是要生效的类型,Value是属性名列表。可以参考XLua对几个UnityEngine下值类型的配置,SysGCOptimize类。
XLua.BlackList
如果你不要生成一个类型的一些成员的适配代码,你可以通过这个配置来实现。
标签方式比较简单,对应的成员上加就可以了。
由于考虑到有可能需要把重载函数的其中一个重载列入黑名单,配置方式比较复杂,类型是List<List>,对于每个成员,在第一层List有一个条目,第二层List是个string的列表,第一个string是类型的全路径名,第二个string是成员名,如果成员是一个方法,还需要从第三个string开始,把其参数的类型全路径全列出来。
例如下面是对GameObject的一个属性以及FileInfo的一个方法列入黑名单:
[BlackList] public static List<List> BlackList = new List<List>()
{ new List(){“UnityEngine.GameObject”, “networkView”},
//new List(){ typeof(UnityEngine.GameObject).FullName, “networkView”},
new List(){“System.IO.FileInfo”, “GetAccessControl”, “System.Security.AccessControl.AccessControlSections”},
//new List(){ typeof(System.IO.FileInfo).FullName, “GetAccessControl”,typeof(System.Security.AccessControl.AccessControlSections).FullName },
};
-----博主:mx
下面是生成期配置,必须放到Editor目录下
CSObjectWrapEditor.GenPath
配置生成代码的放置路径,类型是string。默认放在"Assets/XLua/Gen/"下。
CSObjectWrapEditor.GenCodeMenu
该配置用于生成引擎的二次开发,一个无参数函数加了这个标签,在执行"XLua/Generate Code"菜单时会触发这个函数的调用。
Xlua-教程:
Lua文件加载方式:
1.执行字符串: luaenv.DoString(“print(‘hello world’)”)
2.加载lua文件: DoString(“require ‘byfile’”)
require实际上是调一个个的loader去加载,有一个成功就不再往下尝试,全失败则报文件找不到。 目前xLua除了原生的loader外,还添加了从Resource加载的loader,需要注意的是因为Resource只支持有限的后缀,放Resources下的lua文件得加上txt后缀(见附带的例子)。
建议的加载Lua脚本方式是:整个程序就一个DoString(“require ‘main’”),然后在main.lua加载其它脚本(类似lua脚本的命令行执行:lua main.lua)
3.自定义loader: public delegate byte[] CustomLoader(ref string filepath); public void LuaEnv.AddLoader(CustomLoader loader)
C#访问Lua:
1.访问全局基本数据类型
luaenv.Global.Get(“a”)
luaenv.Global.Get(“b”)
luaenv.Global.Get(“c”)
2.访问全局table
①映射到class或struct
定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。
LUA:
d = {
f1 = 12, f2 = 34,
1, 2, 3,
add = function(self, a, b)
print(‘d.add called’)
return a + b
end
}
C#:
public class DClass
{
public int f1;
public int f2;
}
DClass d = luaenv.Global.Get(“d”);//映射到有对应字段的class,by value
②映射到interface
这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
LUA:
d = {
f1 = 12, f2 = 34,
1, 2, 3,
add = function(self, a, b)
print(‘d.add called’)
return a + b
end
}
C#:
ItfD d3 = luaenv.Global.Get(“d”); //映射到interface实例,by ref,这个要求interface加到生成列表,否则会返回null,建议用法
③by value方式:映射到Dictionary<>,List<>
前提table下key和value的类型都是一致的。
LUA:
d = {
f1 = 12, f2 = 34,
1, 2, 3,
add = function(self, a, b)
print(‘d.add called’)
return a + b
end
}
C#:
Dictionary<string, double> d1 = luaenv.Global.Get<Dictionary<string, double>>(“d”);//映射到Dictionary<string, double>,by value
List d2 = luaenv.Global.Get<List>(“d”); //映射到List,by value
④by ref方式:映射到LuaTable类
这种方式好处是不需要生成代码,但也有一些问题,比如慢,比方式2要慢一个数量级,比如没有类型检查。
LUA:
d = {
f1 = 12, f2 = 34,
1, 2, 3,
add = function(self, a, b)
print(‘d.add called’)
return a + b
end
}
C#:
LuaTable d4 = luaenv.Global.Get(“d”);//映射到LuaTable,by ref
3.访问一个全局的function
①映射到delegate
这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。
声明规则:对于function的每个参数就声明一个输入类型的参数。 多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。
参数/返回值支持类型:都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。
LUA:
function e()
print(‘i am e’)
end
function f(a, b)
print(‘a’, a, ‘b’, b)
return 1, {f1 = 1024}
end
function ret_e()
print(‘ret_e called’)
return e
end
C#:
[CSharpCallLua]
public delegate int FDelegate(int a, string b, out DClass c);
[CSharpCallLua]
public delegate Action GetE();
Action e = luaenv.Global.Get(“e”);//映射到一个delgate,要求delegate加到生成列表,否则返回null,建议用法
e();
FDelegate f = luaenv.Global.Get(“f”);
DClass d_ret;
int f_ret = f(100, “John”, out d_ret);//lua的多返回值映射:从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数
②映射到LuaFunction
这种方式的优缺点刚好和第一种相反。 使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。
LUA:
function e()
print(‘i am e’)
end
C#:
LuaFunction d_e = luaenv.Global.Get(“e”);
d_e.Call();
-----博主:mx
4.使用建议
访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。
如果lua侧的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。
Lua访问C#:
1.new C#对象
C#:var newGameObj = new UnityEngine.GameObject();
对应的Lua : local newGameObj = CS.UnityEngine.GameObject()
基本类似,除了:
- lua里头没有new关键字; 2. 所有C#相关的都放到CS下,包括构造函数,静态成员属性、方法;
xlua支持重载,比如你要调用GameObject的带一个string参数的构造函数,这么写:
local newGameObj2 = CS.UnityEngine.GameObject(‘helloworld’)
2.访问C#静态属性,方法
读静态属性
CS.UnityEngine.Time.deltaTime
写静态属性
CS.UnityEngine.Time.timeScale = 0.5
调用静态方法
CS.UnityEngine.GameObject.Find(‘helloworld’)
小技巧:如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能:
local GameObject = CS.UnityEngine.GameObject
3.访问C#成员属性,方法
读成员属性
testobj.DMF
写成员属性
testobj.DMF = 1024
调用成员方法
注意:调用成员方法,第一个参数需要传该对象,建议用冒号语法糖,如下
testobj:DMFunc()
父类属性,方法
xlua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基类的成员属性,成员方法
testobj:BMFunc()–基类方法调用
4.参数的输入输出属性(out,ref)
Lua调用侧的参数处理规则:C#的普通参数算一个输入形参,ref修饰的算一个输入形参,out不算,然后从左往右对应lua 调用侧的实参列表;
Lua调用侧的返回值处理规则:C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。
–复杂方法调用
local ret, p2, p3, csfunc = testobj:ComplexFunc({x=3, y = ‘john’}, 100, function()
print(‘i am lua callback’)
end)
print(‘ComplexFunc ret:’, ret, p2, p3, csfunc)
csfunc()
5.重载方法
直接通过不同的参数类型进行重载函数的访问,例如:
testobj:TestFunc(100)
testobj:TestFunc(‘hello’)
注意:xlua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一对多的情况,比如C#的int,float,double都对应于lua的number,上面的例子中TestFunc如果有这些重载参数,第一行将无法区分开来,只能调用到其中一个(生成代码中排前面的那个)
6.操作符
支持的操作符有:+,-,*,/,==,一元-,<,<=, %,[]
local testobj2 = DerivedClass()
testobj2.DMF = 2048
print(’(testobj + testobj2).DMF = ', (testobj + testobj2).DMF)
7.参数带默认值的方法
和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。
8.可变参数方法
对于C#的如下方法:
void VariableParamsFunc(int a, params string[] strs)
可以在lua里头这样调用:
testobj:VariableParamsFunc(5, ‘hello’, ‘john’)
9.使用Extension methods
Extension methods相关说明:https://blog.csdn.net/zxz414644665/article/details/9793205?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163125776616780366572082%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163125776616780366572082&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-9793205.first_rank_v2_pc_rank_v29&utm_term=Extension+methods&spm=1018.2226.3001.4187
C#:
[LuaCallCSharp]
public static class DerivedClassExtensions
{
public static int GetSomeData(this DerivedClass obj)
{
Debug.Log("GetSomeData ret = " + obj.DMF);
return obj.DMF;
}
public static int GetSomeBaseData(this BaseClass obj)
{
Debug.Log("GetSomeBaseData ret = " + obj.BMF);
return obj.BMF;
}
public static void GenericMethodOfString(this DerivedClass obj)
{
obj.GenericMethod();
}
}
Lua:
print(testobj:GetSomeData())
print(testobj:GetSomeBaseData()) --访问基类的Extension methods
testobj:GenericMethodOfString() --通过Extension methods实现访问泛化方法
10.泛化(模版)方法
不直接支持,可以通过Extension methods功能进行封装后调用。
-----博主:mx
11.枚举类型
枚举值就像枚举类型下的静态属性一样。
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
上面的EnumTestFunc函数参数是Tutorial.TestEnum类型的。
枚举类支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换,例如:
CS.Tutorial.TestEnum.__CastFrom(1) CS.Tutorial.TestEnum.__CastFrom(‘E1’)
12.delegate使用(调用,+,-)
C#的delegate调用:和调用普通lua函数一样
+操作符:对应C#的+操作符,把两个调用串成一个调用链,右操作数可以是同类型的C# delegate或者是lua函数。
-操作符:和+相反,把一个delegate从调用链中移除。
Ps:delegate属性可以用一个luafunction来赋值。
testobj.TestDelegate('hello') --直接调用
local function lua_delegate(str)
print('TestDelegate in lua:', str)
end
testobj.TestDelegate = lua_delegate + testobj.TestDelegate --combine,这里演示的是C#delegate作为右值,左值也支持
testobj.TestDelegate('hello')
testobj.TestDelegate = testobj.TestDelegate - lua_delegate --remove
testobj.TestDelegate('hello')
13.event
增加事件回调
testobj:TestEvent(’+’, lua_event_callback)
移除事件回调
testobj:TestEvent(’-’, lua_event_callback)
14.64位整数支持
Lua53版本64位整数(long,ulong)映射到原生的64位整数,而luajit版本,相当于lua5.1的标准,本身不支持64位,xlua做了个64位支持的扩展库,C#的long和ulong都将映射到userdata: 支持在lua里头进行64位的运算,比较,打印 支持和lua number的运算,比较 要注意的是,在64扩展库中,实际上只有int64,ulong也会先强转成long再传递到lua,而对ulong的一些运算
15.C#复杂类型和table的自动转换
对于一个有无参构造函数的C#复杂类型,在lua侧可以直接用一个table来代替,该table对应复杂类型的public字段有相应字段即可,支持函数参数传递,属性赋值等
public struct A
{
public int a;
}
public struct B
{ public A b;
public double c;
}
某个类有成员函数如下:
void Foo(B b)
在lua可以这么调用
obj:Foo({b = {a = 100}, c = 200})
16.获取类型(相当于C#的typeof)
比如要获取UnityEngine.ParticleSystem类的Type信息,可以这样
typeof(CS.UnityEngine.ParticleSystem)
17.“强”转
lua没类型,所以不会有强类型语言的“强转”,但有个有点像的东西:告诉xlua要用指定的生成代码去调用一个对象,这在什么情况下能用到呢?有的时候第三方库对外暴露的是一个interface或者抽象类,实现类是隐藏的,这样我们无法对实现类进行代码生成。该实现类将会被xlua识别为未生成代码而用反射来访问,如果这个调用是很频繁的话还是很影响性能的,这时我们就可以把这个interface或者抽象类加到生成代码,然后指定用该生成代码来访问
local calc = testobj:GetCalc()
print('assess instance of InnerCalc via reflection', calc:add(1, 2))
assert(calc.id == 100)
cast(calc, typeof(CS.Tutorial.ICalc))
print('cast to interface ICalc', calc:add(1, 2))
assert(calc.id == nil)
18.协程下使用
local co = coroutine.create(function()
print(’------------------------------------------------------’)
demo()
end)
assert(coroutine.resume(co))
*Xlua-热补丁操作:
宏:
HOTFIX_ENABLE
打开hotfix功能。
-----博主:mx
使用方式
1、打开该特性
添加HOTFIX_ENABLE宏,(在Unity3D的File->Build Setting->Scripting Define Symbols下添加)。编辑器、各手机平台这个宏要分别设置!如果是自动化打包,要注意在代码里头用API设置的宏是不生效的,需要在编辑器设置。
(建议平时开发业务代码不打开HOTFIX_ENABLE,只在build手机版本或者要在编译器下开发补丁时打开HOTFIX_ENABLE)
2、执行XLua/Generate Code菜单。
3、注入,构建手机包这个步骤会在构建时自动进行,编辑器下开发补丁需要手动执行"XLua/Hotfix Inject In Editor"菜单。打印“hotfix inject finish!”或者“had injected!”才算成功,否则会打印错误信息。
如果已经打印了“hotfix inject finish!”或者“had injected!”,执行xlua.hotfix仍然报类似“xlua.access, no field __Hitfix0_Update”的错误,要么是该类没配置到Hotfix列表,要么是注入成功后,又触发了编译,覆盖了注入结果。
约束
不支持静态构造函数。
目前只支持Assets下代码的热补丁,不支持引擎,c#系统库的热补丁。
API
xlua.hotfix(class, [method_name], fix)
描述 : 注入lua补丁
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的方式。
base(csobj)
描述 : 子类override函数通过base调用父类实现。
csobj : 对象
返回值 : 新对象,可以通过该对象base上的方法
例子(位于HotfixTest2.cs):
xlua.hotfix(CS.BaseTest, ‘Foo’, function(self, p) print(‘BaseTest’, p) base(self):Foo§ end)
util.hotfix_ex(class, method_name, fix)
描述 : xlua.hotfix的增强版本,可以在fix函数里头执行原来的函数,缺点是fix的执行会略慢。
method_name : 方法名;
fix : 用来替换C#方法的lua function
打补丁
函数
method_name传函数名,支持重载,不同重载都是转发到同一个lua函数。
// 要fix的C#类
[Hotfix]
public class HotfixCalc
{
public int Add(int a, int b)
{
return a - b;
}
public Vector3 Add(Vector3 a, Vector3 b)
{
return a - b;
}
}
xlua.hotfix(
CS.HotfixCalc,
‘Add’,
function(self, a, b)
return a + b
end
)
静态函数和成员函数的区别是,成员函数会加一个self参数,这个self在Stateless方式下是C#对象本身(对应C#的this)
普通参数对于lua的参数,ref参数对应lua的一个参数和一个返回值,out参数对于lua的一个返回值。
泛化函数的打补丁规则和普通函数一样。
构造函数
构造函数对应的method_name是".ctor"。
和普通函数不一样的是,构造函数的热补丁并不是替换,而是执行原有逻辑后调用lua。
属性
对于名为“AProp”的属性,会对应一个getter,method_name等于get_AProp,setter的method_name等于set_AProp。
[]操作符
赋值对应set_Item,取值对应get_Item。第一个参数是self,赋值后面跟key,value,取值只有key参数,返回值是取出的值。
其它操作符
C#的操作符都有一套内部表示,比如+号的操作符函数名是op_Addition(其它操作符的内部表示可以去请参照相关资料),覆盖这函数就覆盖了C#的+号操作符。
事件
比如对于事件“AEvent”,+=操作符是add_AEvent,-=对应的是remove_AEvent。这两个函数均是第一个参数是self,第二个参数是操作符后面跟的delegate。
通过xlua.private_accessible(版本号大于2.1.11不需要调用xlua.private_accessible)来直接访问事件对应的私有delegate的直接访问后,可以通过对象的"&事件名"字段直接触发事件,例如self’&MyEvent’,其中MyEvent是事件名。
析构函数
method_name是"Finalize",传一个self参数。
和普通函数不一样的是,析构函数的热补丁并不是替换,而是开头调用lua函数后继续原有逻辑。
泛化类型
其它规则一致,需要说明的是,每个泛化类型实例化后都是一个独立的类型,只能针对实例化后的类型分别打补丁。比如:
public class GenericClass { }
你只能对GenericClass,GenericClass这些类,而不是对GenericClass打补丁。
对GenericClass打补丁的实例如下:
luaenv.DoString(@"
xlua.hotfix(CS.GenericClass(CS.System.Double), {
[’.ctor’] = function(obj, a)
print(‘GenericClass’, obj, a)
end;
Func1 = function(obj)
print(‘GenericClass.Func1’, obj)
end;
Func2 = function(obj)
print(‘GenericClass.Func2’, obj)
return 1314
end
})
");
-----博主:mx
Unity协程
通过util.cs_generator可以用一个function模拟一个IEnumerator,在里头用coroutine.yield,就类似C#里头的yield return。比如下面的C#代码和对应的hotfix代码是等同效果的
[XLua.Hotfix]
public class HotFixSubClass : MonoBehaviour {
IEnumerator Start()
{
while (true)
{
yield return new WaitForSeconds(3);
Debug.Log(“Wait for 3 seconds”);
}
}
}
luaenv.DoString(@"
local util = require ‘xlua.util’
xlua.hotfix(CS.HotFixSubClass,{
Start = function(self)
return util.cs_generator(function()
while true do
coroutine.yield(CS.UnityEngine.WaitForSeconds(3))
print(‘Wait for 3 seconds’)
end
end)
end;
})
");
整个类
如果要替换整个类,不需要一次次的调用xlua.hotfix去替换,可以整个一次完成。只要给一个table,按method_name = function组织即可
xlua.hotfix(CS.StatefullTest, {
[’.ctor’] = function(csobj)
return util.state(csobj, {evt = {}, start = 0, prop = 0})
end;
set_AProp = function(self, v)
print(‘set_AProp’, v)
self.prop = v
end;
get_AProp = function(self)
return self.prop
end;
get_Item = function(self, k)
print(‘get_Item’, k)
return 1024
end;
set_Item = function(self, k, v)
print(‘set_Item’, k, v)
end;
add_AEvent = function(self, cb)
print(‘add_AEvent’, cb)
table.insert(self.evt, cb)
end;
remove_AEvent = function(self, cb)
print(‘remove_AEvent’, cb)
for i, v in ipairs(self.evt) do
if v == cb then
table.remove(self.evt, i)
break
end
end
end;
Start = function(self)
print(‘Start’)
for _, cb in ipairs(self.evt) do
cb(self.start, 2)
end
self.start = self.start + 1
end;
StaticFunc = function(a, b, c)
print(a, b, c)
end;
GenericTest = function(self, a)
print(self, a)
end;
Finalize = function(self)
print(‘Finalize’, self)
end
})
-----博主:mx
使用建议
对所有较大可能变动的类型加上Hotfix标识;
建议用反射找出所有函数参数、字段、属性、事件涉及的delegate类型,标注CSharpCallLua;
业务代码、引擎API、系统API,需要在Lua补丁里头高性能访问的类型,加上LuaCallCSharp;
引擎API、系统API可能被代码剪裁调(C#无引用的地方都会被剪裁),如果觉得可能会新增C#代码之外的API调用,这些API所在的类型要么加LuaCallCSharp,要么加ReflectionUse;
学习链接:
1.https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/XLua%E6%95%99%E7%A8%8B.md#%E5%8F%82%E6%95%B0%E7%9A%84%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E5%B1%9E%E6%80%A7outref
2.https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/custom_generate.md
3.https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/configure.md
4.https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/hotfix.md
5.https://zhuanlan.zhihu.com/p/38322991
-----博主:mx
标签:function,end,C#,self,笔记,学习,lua,Xlua,print 来源: https://blog.csdn.net/m0_56399931/article/details/120216155