##《Lua热更新》发布说明:
++++“Lua热更新”开始了,立钻哥哥终于开始此部分的探索了。
++++作为游戏发布迭代的重要技术:Lua热更新在网络游戏迭代更新中非常重要,特别是对于AppStore这样的平台,我们只需要定期更新主App,原则上可以随时灵活更新我们的以Lua热更新框架为基础的代码。
++++当然,作为一项新引入的技术,我们通常是以【快速入门】=>【基础夯实】=>【中级进阶】=>【高级实战】=>【立钻哥哥带您学Lua热更新】等几个阶段进行探索。
##《Lua热更新》目录:
#第一篇:Lua快速入门篇
#第二篇:Lua基础夯实篇
#第三篇:Lua中级进阶篇
#第四篇:Lua高级实战篇
#第五篇:立钻哥哥带您学Lua热更新
++++Lua快速入门篇(XLua拓展):https://blog.csdn.net/VRunSoftYanlz/article/details/81173818
++++Lua快速入门篇(XLua教程):https://blog.csdn.net/VRunSoftYanlz/article/details/81141502
++++Lua快速入门篇(基础概述):https://blog.csdn.net/VRunSoftYanlz/article/details/81041359
#第一篇:Lua快速入门篇
++++第一章:Lua基础概述
++++第二章:xLua教程
++++第三章:Lua基础拓展
++++第四章:立钻哥哥带您学Lua热更新
++第二章:xLua教程
++++https://github.com/Tencent/xLua
++++xLua为Unity、.Net、Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用。
++++2.1、xLua教程
++++2.2、xLua的配置
++++2.3、热补丁操作指南
++++2.4、XLua增加删除第三方lua库
++++2.5、xLua API
++++2.6、生成引擎二次开发指南
++++2.7、xLua常见问题
++++2.8、xLua示例参考
++++2.9、xLua拓展总结
++++2.10、立钻哥哥带您学xLua
++2.1、xLua入门教程
++++xLua教程:https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/XLua教程.md
++++2.1.1、Lua文件加载
++++2.1.2、C#访问Lua
++++2.1.3、Lua调用C#
++2.1.1、Lua文件加载
++++Lua文件加载:1、执行字符串;2、加载Lua文件;3、自定义Loader;
++++1、执行字符串(不推荐使用):
--最基本是直接用LuaEnv.DoString()执行一个字符串(字符串得符合Lua语法):
----luaenv.DoString(“print(‘立钻哥哥’)”)
++++2、加载Lua文件:
--用lua的require函数即可:
----DoString(“require ‘byfile’”);
--require实际上是调用一个个的loader去加载,有一个成功就不再往下尝试,全失败则报文件找不到。(目前xLua除了原生的loader外,还添加了从Resource加载的loader,需要注意的是因为Resource只支持有限的后缀,放Resource下的Lua文件得加上txt后缀。)
--建议加载Lua脚本方式是:整个程序就一个DoString(“require ‘main’”),然后在main.lua加载其它脚本(类似lua脚本的命令执行:lua main.lua)
++++3、自定义Loader:
--在xLua加自定义loader是很简单的,只涉及到一个接口:
----public delegate byte[] CustomLoader(ref string filepath);
----public void LuaEnv.AddLoader(CustomLoader loader)
--通过AddLoader可以注册一个回调,该回调参数是字符串,lua代码里头调用require时,参数将会透传给回调,回调中就可以根据这个参数去加载指定文件,如果需要支持调试,需要把filepath修改为真实路径传出。(该回调返回一个byte数组,如果为空表示该loader找不到,否则则为lua文件的内容。)
++2.1.2、C#访问Lua
++++这里指的是C#主动发起对Lua数据结构的访问。
++++1、获取一个全局基本数据类型:
--访问LuaEnv.Global就可以了。
--luaenv.Global.Get<int>(“a”)
--luaenv.Global.Get<string>(“b”)
--luaenv.Global.Get<bool>(“c”)
++++2、访问一个全局的table:
--2.1、映射到普通class或struct:
----定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,比如对于{ f1 = 100, f2 = 100 }可以定义一个包含public int f1; public int f2;的class。
----table属性可多于或者少于class的属性。可以嵌套其它复杂类型。(立钻哥哥:这个过程是值拷贝,如果class比较复杂代价会比较大。)
----这个功能可以通过把类型加到GCOptimize生成降低开销。
--2.2、映射到一个interface:
----这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。(甚至可以通过interface的方法访问lua的函数。)
--2.3、更轻量级的by value方式:映射到Dictionary<>, List<>:
----不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的。
--2.4、另外一种by ref方式:映射到LuaTable类:
----这种方式好处是不需要生成代码,但也有一些问题,比较慢,没有类型检查。
++++3、访问一个全局的function:
--仍然是用Get方法,不同的是类型映射。
--3.1、映射到delegate:
----这种是建议的方式,性能好很多,而且类型安全。(缺点是要生成代码(如果没生成代码会抛InvalidCastException异常))。
----delegate要怎样声明呢?对于function的每个参数就声明一个输入类型的参数。(多返回值要怎么处理?从左往右映射到C#的输出参数,输出参数包括返回值,out参数,ref参数。)
----参数、返回值类型支持哪些呢?(都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。)
----delegate的使用就更简单了,直接像个函数那样用就可以了。
--3.2、映射到LuaFunction:
----这种方式的优缺点刚好和第一种相反。(使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。)
++++4、使用建议:
--4.1、访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。(table也类似)
--4.2、如果lua侧的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦;由一个专门的模块负责xLua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。
++2.1.3、Lua调用C#
++++lua调用C#:new C#对象;访问C#静态属性,方法;访问C#成员属性,方法;
++++【new C#对象】:
--可以像C#这样new一个对象:
----var newGameObj = new UnityEngine.GameObject(); //C#
----local newGameObject = CS.UnityEngine.GameObject(); //lua
--基本类似,除了:1、lua里头没有new关键字;2、所有C#相关的都放到CS下,包括构造函数,静态成员属性、方法;
--xLua支持重载(多个构造函数等),比如调用GameObject的带一个string参数的构造函数:
----local newGameObj2 = CS.UnityEngine.GameObject(‘立钻哥哥’)
++++【访问C#静态属性,方法】:
--【读静态属性】:CS.UnityEngine.Time.deltaTime
--【写静态属性】:CS.UnityEngine.Time.timeScale = 0.5
--【调用静态方法】:CS.UnityEngine.GameObject.Find(‘立钻哥哥’)
----立钻哥哥:如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能:
------local GameObject = CS.UnityEngine.GameObject
------GameObject.Find(‘立钻哥哥’)
++++【访问C#成员属性,方法】:
--【读成员属性】:testObj.YanlzScore
--【写成员属性】:testObj.YanlzScore = 1024
--【调用成员方法】:调用成员方法,第一参数需要传该对象,建议用冒号语法糖:
----testobj:MyTestFunc()
--【父类属性,方法】:
----xLua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基类的成员属性,成员方法。
--【参数的输入输出属性(out, ref)】:
----Lua调用侧的参数处理规则:C#的普通参数算一个输入形参,ref修饰的算一个输入形参,out不算,然后从左往右对应lua调用侧的实参列表。
----Lua调用侧的返回值处理规则:C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。
--【重载方法】:直接通过不同的参数类型进行重载函数的访问:
----testobj:TestFunc(100)
----testobj:TestFunc(‘立钻哥哥’)
----立钻哥哥:xLua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一对多的情况,比如C#的int,float,double都对应于lua的number。
--【操作符】:支持操作符有:+,-,*,/,==,<,<=,%,[]
--【参数带默认值的方法】:和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。
--【可变参数方法】:
--C#:void VariableParamsFunc(int a, params string[] strs);
--lua:testobj:VariableParamsFunc(5, ‘hello’, ‘立钻哥哥’)
--【使用Extension methods】:在C#里定义了,lua里就能直接使用。
--【泛化(模板)方法】:不直接支持,可以通过Extension methods功能进行封装后调用。
--【枚举类型】:枚举值就像枚举类型下的静态属性一样。
----testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
----枚举类支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换:
------CS.Tutorial.TestEnum.__CastFrom(1)
------CS.Tutorial.TestEnum.__CastFrom(‘E1’)
--【delegate使用(调用,+,-)】:
----C#的delegate调用:和调用普通lua函数一样;
----操作符+:对应C#的+操作符,把两个调用串成一个调用链,右操作数可以是同类型的C# delegate或者是lua函数。
----操作符-:和+相反,把一个delegate从调用链中移除。
----立钻哥哥:delegate属性可以用一个luafunction来赋值。
--【event】:比如testobj里头有事件定义是这样:public event Action TestEvent;
----增加事件回调:testobj:TestEvent(‘+’, lua_event_callback)
----移除事件回调:testobj:TestEvent(‘-’, lua_event_callback)
--【64位整数支持】:
----Lua53版本64位整数(long, ulong)映射到原生的64位整数,而luajit版本,相当于lua5.1标准,本身不支持64位,xlua做了个64位支持的扩展库,C#的long和ulong都将映射到userdate:
------支持在lua里头进行64位的运算,比较,打印;
------支持和lua number的运算,比较;
------立钻哥哥:在64位扩展库中,实际上只有int64,ulong也会强转成long再传递到lua,而对ulong的一些运算,比较,我们采取和java一样的支持方式,提供一组API。
--【C#复杂类型和table的自动转换】:对于一个无参构造函数的C#复杂类型,在lua侧可以直接用一个table来代替,该table对复杂类型的public字段有相应字段即可,支持函数参数传递,属性赋值等。
--【获取类型(相当于C#的typeof)】:
----比如要获取UnityEngine.ParticleSystem类的Type信息,可以这样:
----typeof(CS.UnityEngine.ParticleSystem)
--【“强”转】:lua没有类型,所以不会有强类型语言的“强转”。
----但有个有点像的东西:告诉xLua要用指定的生成代码去调用一个对象,这在什么情况下能用到呢?
----有的时候第三方库对外暴露的是一个interface或者抽象类,实现类的隐藏的,这样我们无法对实现类进行代码生成。
----该实现类将会被xLua识别为未生成代码而用反射来访问,如果这个调用是很频繁的话还是很影响性能的,这时我们就可以把这个interface或者抽象类加到生成代码,然后指定用该生成代码来访问:
----cast(calc, typeof(CS.Tutorial.Calc))
++2.2、xLua的配置
++++xLua的配置:https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/configure.md
++++xLua所有的配置都支持三种方式:打标签;静态列表;动态列表;
++++xLua配置有两必须和两建议:
--必须1:列表方式均必须是static的字段/属性;
--必须2:列表方式均必须放到一个static类;
--建议1:不用标签方式;
--建议2:列表方式配置放Editor目录(如果是Hotfix配置,而且类位于Assembly-CSharp.dll之外的其他dll,必须放Editor目录)
++++2.2.0、立钻哥哥带您xLua配置
++++2.2.1、打标签
++++2.2.2、静态列表
++++2.2.3、动态列表
++++2.2.4、XLua.LuaCallCSharp
++++2.2.5、XLua.ReflectionUse
++++2.2.6、XLua.DoNotGen
++++2.2.7、XLua.CSharpCallLua
++++2.2.8、XLua.GCOptimize
++++2.2.9、XLua.AdditionalProperties
++++2.2.10、XLua.BlackList
++++2.2.11、CSObjectWrapEditor.GenPath
++++2.2.12、CSObjectWrapEditor.GenCodeMenu
++++2.2.13、立钻哥哥带您xLua配置
++2.2.1、打标签(不推荐使用)
++++xLua用白名单来指明生成哪些代码,而白名单通过attribute来配置;
++++比如想从lua调用C#某个类,希望生成适配代码,可以为这个类型打一个LuaCallCSharp标签:
[LuaCallCSharp]
public class A{
}
++++该方式方便,但在il2cpp下会增加不少的代码量,不建议使用。
++2.2.2、静态列表
++++有时我们无法直接给一个类型打标签,比如系统api,没源码的库,或者实例化的泛化类型,这时可以在一个静态类里声明一个静态字段,该字段的类型除[BlackList]和[AdditionalProperties]之外只要实现了IEnumerable
[LuaCallCSharp]
public static List<Type> mymodule_lua_call_cs_list = new List<Type>(){
typeof(GameObject),
typeof(Dictionary<string, int>),
};
++++这个字段需要放到一个静态类里头,建议放到Editor目录。
++2.2.3、动态列表
++++声明一个静态属性,打上相应的标签即可。
[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目录。
++2.2.4、XLua.LuaCallCSharp
++++一个C#类型加了这个配置,xLua会生成这个类型的适配代码(包括构造该类型实例,访问其成员属性、方法,静态属性、方法),否则将会尝试用性能较低的反射方式来访问。
++++一个类型的扩展方法(Extension Methods)加了这配置,也会生成适配代码并追加到被扩展类型的成员方法上。
++++xLua只会生成加了该配置的类型,不会自动生成其父类的适配代码,当访问子类对象的父类方法,如果该父类加了[LuaCallCSharp]配置,则执行父类的适配代码,否则会尝试用反射来访问。
++++反射访问除了性能不佳之外,在il2cpp下还有可能因为代码剪裁而导致无法访问,后者可以通过[ReflectionUse]标签来避免。
++2.2.5、XLua.ReflectionUse
++++一个C#类型加了这个配置,xLua会生成link.xml阻止il2cpp的代码剪裁。
++++对于扩展方法,必须加上[LuaCallCSharp]或者[ReflectionUse]才可以被访问到。
++++建议所有要在Lua访问的类型,要么加[LuaCallCSharp],要么加上[ReflectionUse],这才能够保住在各平台都能正常运行。
++2.2.6、XLua.DoNotGen
++++指明一个类里头的部分函数、字段、属性不生成代码,通过反射访问。
++++标准Dictionary
++++[DoNotGen]和[ReflectionUse]的区别是:
--1、[ReflectionUse]指明的是整个类;
--2、当第一次访问一个函数(字段、属性)时,[ReflectionUse]会把整个类都wrap,而[DoNotGen]只wrap该函数(字段、属性),换句话[DoNotGen]更lazy一些;
++++[DoNotGen]和[BlackList]的区别:
--1、[BlackList]配了就不能用;
--2、[BlackList]能指明某重载函数,[DoNotGen]不能;
++2.2.7、XLua.CSharpCallLua
++++如果希望把一个lua函数适配到一个C# delegate(
--一类是C#侧各种回调:UI事件,delegate参数,比如List
--另外一类场景是通过LuaTable的Get函数指明一个lua函数绑定到一个delegate)。
++++或者把一个lua table适配到一个C# interface,该delegate或者interface需要加上该配置。
++2.2.8、XLua.GCOptimize
++++一个C#纯值类型(立钻哥哥:指的是一个只包含值类型的struct,可以嵌套其它只包含值类型的struct)或者C#枚举值加上了这个配置。
++++xLua会为该类型生成gc优化代码,效果是该值类型在lua和C#间传递不产生(C#)gc alloc,该类型的数组访问也不产生gc。各种无GC的场景。
++++除枚举之外,包含无参构造函数的复杂类型,都会生成lua table到该类型,以及该类型的一维数组的转换代码,这将会优化这个转换的性能,包括更少的gc alloc。
++2.2.9、XLua.AdditionalProperties
++++这个是[GCOptimize]的扩展配置,有的时候,一些struct喜欢把field做成是私有的,通过property来访问field,这时就需要用到该配置(默认情况下[GCOptimize]只对public的field打解包。)
++++标签方式比较简单,配置方式复杂一些,要求是Dictionary
++++可以参考XLua对几个UnityEngine下值类型的配置,SysGCOptimize类等。
++2.2.10、XLua.BlackList
++++如果不要生成一个类型的一些成员的适配代码,可以通过这个配置来实现。
++++标签方式比较简单,对应的成员上加上就可以了。
++++由于考虑到有可能需要把重载函数的其中一个重载列入黑名单,配置方式比较复杂,类型是List>,对于每个成员,
--在第一层List有一个条目,
--第二层List是个string的列表,
--第一个string是类型的全路径名,
--第二个string是成员名,
++++如果成员是一个方法,还需要从第三个string开始,把其参数的类型全路径全列出来。
++++例如:对GameObject的一个属性以及FileInfo的一个方法列入黑名单[BlackList]:
[BlackList]
public static List<List<string>> BlackList = new List<List<string>>(){
new List<string>(){ “UnityEngine.GameObject”, “networkView” },
new List<string>(){ “System.IO.FileInfo”, “GetAccessControl”, “System.Security.AccessControl.AccessControlSections” },
};
++2.2.11、CSObjectWrapEditor.GenPath
++++立钻哥哥:生成期配置,必须放到Editor目录下。
++++配置生成代码的放置路径,类型是string。
++++默认放在“Assets/XLua/Gen/”下。
++2.2.12、CSObjectWrapEditor.GenCodeMenu
++++立钻哥哥:生成期配置,必须放到Editor目录下。
++++该配置用于生成引擎的二次开发,一个无参数函数加上了这个标签,在执行“XLua/Generate Code”菜单时会触发这个函数的调用。
++2.3、热补丁操作指南
++++https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/hotfix.md
++++热补丁操作指南:使用方式;约束;API;标识要热更新的类型;使用建议;打补丁;
++++2.3.1、使用方式
++++2.3.2、约束
++++2.3.3、API
++++2.3.4、标识要热更新的类型
++++2.3.5、使用建议
++++2.3.6、打补丁
++2.3.1、使用方式
++++步骤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!”)
++2.3.2、约束
++++立钻哥哥:不支持静态构造函数。
++++目前只支持Assets下代码的热补丁(不支持引擎、C#系统库的热补丁)。
++2.3.3、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上的方法。
++++立钻哥哥:util.hotfix_ex(class, method_name, fix)
--描述:xlua.hotfix的增强版本,可以在fix函数里头执行原来的函数,缺点是fix的执行会略慢。
--method_name:方法名;
--fix:用来替换C#方法的lua function;
++++立钻哥哥:示例参考:
xlua.hotfix(CS.BaseTest, ‘Foo’, function(self, p)
print(‘BaseTest’, p)
base(self) : Foo(p)
end)
++2.3.4、标识要热更新的类型
++++立钻哥哥:和其它配置一样,有两种方式。
++++方式一:直接在类里头打Hotfix标签(不推荐)。
++++方式二:在一个static类的static字段或者属性里头配置一个列表。属性可以用于实现的比较复杂的配置,比如根据Namespace做白名单。
//立钻哥哥:如果涉及到Assembly-CSharp.dll之外的其他dll(需要放到Editor目录)
public static class YanlzHotfixCfg{
[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();
}
}
} //立钻哥哥:public static class YanlzHotfixCfg
++2.3.5、Hotfix Flag
++++立钻哥哥:Hotfix标签可以设置一些标志位对生成代码及插桩定制化。
++++Hotfix标签:Stateless、Stateful;ValueTypeBoxing;IgnoreProperty;IgnoreNotPublic;Inline;IntKey;
++++【Stateless、Stateful】:遗留设置,Stateful方式在新版本已经删除,因为这种方式可以用xlua.util.state接口达到类似的效果。(由于没有Statefull,默认就是Stateless,所以也没有必要设置该标志位。)
++++【ValueTypeBoxing】:值类型的适配delegate会收敛到object,好处是代码量更少,不好的值类型会产生boxing及gc,适用于对text段敏感的业务。
++++【IgnoreProperty】:不对属性注入及生成适配代码,一般而言,大多数属性的实现都很简单,出错几率比较小,建议不注入。
++++【Inline】:不生成适配delegate,直接在函数体注入处理代码。
++++【IntKey】:不生成静态字段,而是把所有注入点放到一个数组集中管理。
++2.3.6、使用建议
++++立钻哥哥:对所有较大可能变动的类型加上[Hotfix]标识。
++++建议用反射找出所有函数的参数、字段、属性、事件涉及的delegate类型,标注[CSharpCallLua];
++++业务代码、引擎API、系统API,需要在Lua补丁里头高性能访问的类型,加上[LuaCallCSharp];
++++引擎API、系统API可能被代码剪裁掉(C#无引用的地方都会被剪裁),如果觉得可能会新增C#代码之外的API调用,这些API所在的类型要么加上[LuaCallCSharp],要么加[ReflectionUse]。
++2.3.7、打补丁
++++立钻哥哥:xLua可以用lua函数替换C#的构造函数,函数,属性,事件的替换。(lua实现都是函数,比如属性对于一个getter函数和一个setter函数,事件对应一个add函数和一个remove函数。)
++++打补丁:函数;构造函数;属性;[]操作符;其他操作符;事件;析构函数;泛化类型;Unity协程;整个类;
++++【函数】: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;
}
} //立钻哥哥:public class HotfixCalc{}
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函数后继续原有逻辑。
++++【泛化类型】:其他规则一致,需要说明的是,每个泛化类型实例化后都是一个独立的类型,只能针对实例化后的类型分别打补丁。
++++【Unity协程】:通过util.cs_generator可以用一个function模拟一个IEnumerator,在里头用coroutine.yield,就类似C#里头的yield return。
++++【整个类】:如果要替换整个类,不需要一次次的调用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
}) //立钻哥哥:xlua.hotfix(CS.StatefullTest, {})
++2.4、XLua增加删除第三方lua库
++++https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/XLua增加删除第三方lua库.md
++++立钻哥哥:XLua增加删除第三方lua库。
++++XLua目前内置的扩展库:
--1、针对luajit的64位整数支持;
--2、函数调用耗时以及内存泄露定位工具;
--3、用于支持ZeroBraneStudio的luasocket库;
--4、tdr 4 lua;
++++随着使用项目的增加以及项目使用的深入程度,仅有这几个扩展已经没法满足项目组了,而由于各个项目对扩展差异化比较大,以及手机平台对安装包大小的敏感,XLua是无法通过预集成去满足这些需求。
++++如何往xLua添加扩展库,分三步:
--步骤1:修改build文件、工程设置,把要集成的扩展编译到XLua Plugin里头;
--步骤2:调用xLua的C# API,使得扩展可以被按需(在lua代码里头require的时候)加载;
--步骤3:可选,如果扩展里头需要用到64位整数,可以通过XLua的64位扩展库来实现和C#的配合。
++2.4.1、添加扩展&编译(以lua-rapidjson为例)
++++步骤1:把xLua的C源码包解压到Unity工程的Assets同级目录下。
--下载lua-rapidjson代码,按习惯放置。
--rapidjson头文件放到:$YanlzUnityPro\build\lua-rapidjson\include目录下,
--扩展的源码rapidjson.cpp放到:$YanlzUnityPro\build\lua-rapidjson\source目录下。
++++步骤2:在CMakeLists.txt加入扩展:
--xLua的各平台Plugins编译使用cmake编译,好处是所有平台的编译都写在一个makefile,大部分编译处理逻辑是跨平台的。
--xLua配套的CMakeLists.txt为第三方扩展提供了扩展点(都是list):
--i.THIRDPART_INC:第三方扩展的头文件搜索路径;
--ii.THIRDPART_SRC:第三方扩展的源代码。
--iii.THIRDPART_LIB:第三方扩展依赖的库。
++++立钻哥哥:rapidjson扩展参考
#begin lua - rapidjson
set (RAPIDJSON_SRC lua-rapidjson/source/rapidjson.cpp)
set_property(
SOURCE ${ RAPIDJSON_SRC }
APPEND
PROPERTY COMPILE_DEFINITIONS
LUA_LIB
)
list(APPEND THIRDPART_INC lua-rapidjson/include)
set (THIRDPART_SRC ${THIRDPART_SRC} ${RAPIDJSON_SRC})
#end lua-rapidjson
++++步骤3:各平台编译:
--所有编译脚本都是按这个方式命名:make_平台_lua版本.后缀
--比如:windows 64位lua53版本是:make_win64_lua53.bat;
--比如:android的luajit版本是:make_android_luajit.sh;
--要编译哪个版本就执行相应的脚本即可。
--执行完编译脚本会自动拷贝到plugin_lua53或者plugin_luajit目录。
--配套的android脚本是在linux下使用的,脚本开头的NDK路径要根据实际情况修改。
++2.4.2、C#侧集成
++++所有lua的C扩展库都会提供:luaopen_xxx的函数(xxx是动态库的名字):
--比如lua-rapidjson库,该函数是:luaopen_rapidjson,
--这类函数由lua虚拟机在加载动态库时自动调用,而在手机平台,由于ios的限制我们加载不了动态库,而是直接编译进进程里头。
++++xLua提供了一个API来替代这功能(LuaEnv的成员方法):
--public void AddBuildin(string name, LuaCSFunction initer)
----参数name:buildin模块的名字,require时输入的参数;
----参数initer:初始化函数,原型是这样的:public delegate int lua_CSFunction(IntPtr L),必须是静态函数,而且带MonoPInvokeCallbackAttribute属性修饰,这个api会检查这两个条件。
++++立钻哥哥:以luaopen_rapidjson的调用来分析使用:
--扩展LuaDLL.lua类,用pinvoke把luaopen_rapidjson导出到C#,然后写一个符合lua_CSFunction定义的静态函数,可以在里头做写初始化工作:
//立钻哥哥:以luaopen_rapidjson的调用为参考
namespace LuaDLL{
public partial class Lua{
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_rapidjson(System.IntPtr L);
[MonPInvokeCallback(typeof(LuaDLL.lua_CSFunction))]
public static int LoadRapidJson(System.IntPtr L){
return luaopen_rapidjson(L);
}
} //立钻哥哥:public partial class Lua
} //立钻哥哥:namespace LuaDLL{}
--然后调用AddBuildin:luaenv.AddBuildin(“rapidjson”, LuaDLL.Lua.LoadRapidJson)
//立钻哥哥:在lua代码中试试该扩展吧
local rapidjson = require(‘rapidjson’)
local myT = rapidjson.decode(‘{“a”:123}’)
print(myT.a)
myT.a = 888
local myS = rapidjson.encode(myT)
print(‘json’, myS)
++2.4.3、64位改造
++++立钻哥哥:把i64lib.h文件include到需要64位改造的文件里头。
++++[i64lib.h]该头文件的API有:
--1、往栈上放一个int64/uint64:
----void lua_pushint64(lua_State* L, int64_t n);
----void lua_pushuint64(lua_State* L, uint64_t n);
--2、判断栈上pos位置是否是int64/uint64:
----int lua_isint64(lua_State* L, int pos);
----int lua_isuint64(lua_State* L, int pos);
--3、从栈上pos位置取一个int64/uint64:
----int64_t lua_toint64(lua_State* L, int pos);
----uint64_t lua_touint64(lua_State* L, int pos);
++2.5、xLua API
++++https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/XLua_API.md
++++XLua中C# API:LuaEnv类、LuaTable类、LuaFunction类;
++++XLua中Lua API:CS对象、类型映射、复杂数据类型、宏;
++2.5.1、C#API
++++立钻哥哥:C#API包括:LuaEnv类;LuaTable类;LuaFunction类;
++++【LuaEnv类】:
--object[] DoString(string chunk, string chunkName = ”chuck”, LuaTable env = null);
--T LoadString(string chunk, string chunkName = “chuck”, LuaTable env = null);
--LuaTable GLobal;
--void Tick();
--void AddLoader(CustomLoader loader);
--void Dispose();
++++【LuaTable类】:
--T Get(string key);
--T GetInPath(string path);
--void SetInPath(string path, T val);
--void Get<TKey, TValue>(Tkey key, out TValue value);
--void Set<TKey, TValue>(TKey key, TValue value);
--T Cast();
--void SetMetaTable(LuaTable metaTable);
++++【LuaFunction类】:
--object[] Call(params object[] args);
--object[] Call(object[] args, Type[] returnTypes);
--void SetEnv(LuaTable env);
++2.5.1.1、LuaEnv类
++++LuaEnv类:DoString()、LoadString()、Tick()、AddLoader()、Dispose();
++++object[] DoString(string chunk, string chunkName = ”chuck”, LuaTable env = null);
++++T LoadString(string chunk, string chunkName = “chuck”, LuaTable env = null);
++++LuaTable GLobal;
++++void Tick();
++++void AddLoader(CustomLoader loader);
++++void Dispose();
++2.5.1.1.1、object[] DoString(string chunk, string chunkName = ”chuck”, LuaTable env = null);
++++描述:执行一个代码块。
++++参数:
--chunk:Lua代码的字符串;
--chunkName:发生error时的debug显示信息中使用,指明某某代码的某行错误;
--env:这个代码块的环境变量;
++++返回值:
--代码块里return语句的返回值;
--比如:return 1,“立钻哥哥”,DoString返回将包含两个object数组,一个doube类型的1,一个是string类型的“立钻哥哥”。
++++例如:
LuaEnv luaenv = new LuaEnv();
object[] ret = luaenv.DoString(“print(‘立钻哥哥’)\r\nreturn 1”);
UnityEngine.Debug.Log(“ret=”+ret[0]);
luaenv.Dispose();
++2.5.1.1.2、T LoadString(string chunk, string chunkName = “chuck”, LuaTable env = null);
++++描述:加载一个代码块,但不执行,只返回类型可以指定为一个delegate或者一个LuaFunciton。
++++参数:
--chunk:Lua代码的字符串;
--chunkName:发生error时的debug显示信息中使用,指明某某代码块的某行错误;
--env:这个代码块的环境变量;
++++返回值:代表该代码块的delegate或者LuaFunction类;
++2.5.1.1.3、LuaTable GLobal;
++++描述:代表Lua全局环境的LuaTable。
++2.5.1.1.4、void Tick();
++++描述:清除Lua的手动释放的LuaBase对象(比如:LuaTable,LuaFunction),以及其他一些事情。(需要定期调用,比如在MonoBehaviour的Update中调用。)
++2.5.1.1.5、void AddLoader(CustomLoader loader);
++++描述:增加一个自定义loader。
++++参数:
--loader:一个包括了加载函数的委托,其类型为delegate byte[] CustomLoader(ref string filepath),当一个文件被require时,这个loader会被回调,其参数是调用require所使用的参数,如果该loader找到文件,可以将其读进内存,返回一个byte数组。如果需要支持调试的话,而filepath要设置成IDE能找到的路径(相对路径或绝对路径都可以)。
++2.5.1.1.6、void Dispose();
++++描述:Dispose该LuaEnv。
++++立钻哥哥:LuaEnv的使用建议:全局就一个实例,并在Update中调用GC方法,完全不需要时调用Dispose。
++2.5.1.2、LuaTable类
++++LuaTable类:Get()、GetInPath()、SetInPath()、Get
++++T Get(string key);
++++T GetInPath(string path);
++++void SetInPath(string path, T val);
++++void Get<TKey, TValue>(Tkey key, out TValue value);
++++void Set<TKey, TValue>(TKey key, TValue value);
++++T Cast();
++++void SetMetaTable(LuaTable metaTable);
++2.5.1.2.1、T Get(string key);
++描述:获取在key下,类型为T的value,如果不存在或者类型不匹配,返回null。
++2.5.1.2.2、T GetInPath(string path);
++++描述:和Get的区别是,这个函数会识别path里头的“.”,比如var i = tbl.GetInPath
++2.5.1.2.3、void SetInPath(string path, T val);
++++描述:和GetInPath
++2.5.1.2.4、void Get<TKey, TValue>(Tkey key, out TValue value);
++++描述:上面的API的Key都只能是string,而这个API无此限制。
++2.5.1.2.5、void Set<TKey, TValue>(TKey key, TValue value);
++++描述:对应Get
++2.5.1.2.6、T Cast();
++++描述:把该table转成一个T指明的类型,可以是一个加了[CSharpCallLua]声明的interface,一个有默认构造函数的class或者struct,一个Dictionary,List等等。
++2.5.1.2.7、void SetMetaTable(LuaTable metaTable);
++++描述:设置metaTable为table的metatable。
++2.5.1.3、LuaFunction类
++++立钻哥哥:用该类访问Lua函数会有boxing、unboxing的开销,为了性能考虑,需要频繁调用的地方不要用该类。建议通过table.Get()获取一个delegate再调用(假设YanlzDelegate是C#的一个delegate)。在使用table.Get()之前,请先把YanlzDelegate加到代码生成列表。
++++LuaFunction类:Call()、Call()、SetEnv()、
++++object[] Call(params object[] args);
++++object[] Call(object[] args, Type[] returnTypes);
++++void SetEnv(LuaTable env);
++2.5.1.3.1、object[] Call(params object[] args);
++描述:以可变参数调用Lua函数,并返回该调用的返回值。
++2.5.1.3.2、object[] Call(object[] args, Type[] returnTypes);
++描述:调用lua函数,并指明返回参数的类型,系统会自动按指定类型进行转换。
++2.5.1.3.3、void SetEnv(LuaTable env);
++描述:相当于lua的setfenv函数。
++2.5.2、LuaAPI
++++立钻哥哥:CS对象、typeof函数、无符号64位支持、类型映射、宏、
++++【CS对象】:
--CS.namespace.class(...);
--CS.namespace.class.field;
--CS.namespace.enum.field;
++++【typeof函数】:
++++【无符号64位支持】:
--uint64.tostring;
--uint64.divide;
--uint64.compare;
--uint64.remainder;
--uint64.parse;
--xlua.structclone;
--xlua.private_accessible(class);
--cast函数;
++++【类型映射】:基本数据类型、复杂数据类型;
++++【宏】:
--HOTFIX_ENABLE;
--NOT_GEN_WARNING;
--GEN_CODE_MINIMIZE;
++2.5.2.1、CS对象
++++CS对象:class()、class.field、enum.field、
++++【CS对象】:
--CS.namespace.class(...);
--CS.namespace.class.field;
--CS.namespace.enum.field;
++2.5.2.1.1、CS.namespace.class(...);
++++描述:调用一个C#类型的构造函数,并返回类型实例。
++++例如:local v1 = CS.UnityEngine.Vector3(1,1,1)
++2.5.2.1.2、CS.namespace.class.field;
++++描述:访问一个C#静态成员。
++++例如:print(CS.UnityEngine.Vector3.one)
++2.5.2.1.3、CS.namespace.enum.field;
++++描述:访问一个枚举值。
++2.5.2.2、typeof函数
++++描述:类似C#里头的typeof关键字,返回一个Type对象,比如GameObject.AddComponent()其中一个重载需要一个Type参数。
++++例如:newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem))
++2.5.2.3、无符号64位支持
++++无符号64位支持:uint64.tostring、uint64.divide、uint64.compare、uint64.remainder、uint64.parse、xlua.structclone、xlua.private_accessible()、cast()、
++++uint64.tostring;
++++uint64.divide;
++++uint64.compare;
++++uint64.remainder;
++++uint64.parse;
++++xlua.structclone;
++++xlua.private_accessible(class);
++++cast函数;
++2.5.2.3.1、uint64.tostring;
++++描述:无符号数转字符串。
++2.5.2.3.2、uint64.divide;
++++描述:无符号数除法。
++2.5.2.3.3、uint64.compare;
++++描述:无符号比较,相等返回0,大于返回正数,小于返回负数。
++2.5.2.3.4、uint64.remainder;
++++描述:无符号取模。
++2.5.2.3.5、uint64.parse;
++++描述:字符串转无符号数。
++2.5.2.3.6、xlua.structclone;
++++描述:克隆一个C#结构体。
++2.5.2.3.7、xlua.private_accessible(class);
++++描述:让一个类的私有字段,属性,方法等可用。
++2.5.2.3.8、cast函数;
++++描述:指明以待定的接口访问对象,这在实现类无法访问的时候(比如internal修饰)很有用,这时可以这么来(假设下面的calc对象实现了C#的YanlzPerforment.ICalc接口)
++++例如:cast(calc, typeof(CS.YanlzPerforment.ICalc))
++++然后有没有其他API访问CSharp对象和访问一个table一样,调用函数跟调用lua函数一样,也可以通过操作符访问C#的操作符,例如:
local v1 = CS.UnityEngine.Vector3(1,1,1)
local v2 = CS.UnityEngine.Vector3(1,1,1)
v1.x = 100
v2.y = 100
print(v1, v2)
local v3 = v1 + v3
print(v1.x, v2.x)
print(CS.UnityEngine.Vector3.one)
print(CS.UnityEngine.Vector3.Distance(v1, v2))
++2.5.2.4、类型映射
++++类型映射:基本数据类型、复杂数据类型
++++基本数据类型(Lua类型):number、userdate、userdate/lua_Integer(lua53)、string、boolean、string、
C#类型 |
Lua类型 |
sbyte, byte, short, ushort, int, uint, double, char, float |
number |
decimal |
userdata |
long, ulong |
userdata/lua_Integer(lua53) |
bytes[] |
string |
bool |
boolean |
string |
string |
立钻哥哥:基本数据类型 |
++++复杂数据类型(Lua类型):table、function、userdata,table、function、
C#类型 |
Lua类型 |
LuaTable |
table |
LuaFunction |
function |
class或者struct的实例 |
userdate, table |
method, delegate |
function |
立钻哥哥:复杂数据类型 |
++2.5.2.4.1、复杂数据类型
C#类型 |
Lua类型 |
LuaTable |
table |
LuaFunction |
function |
class或者struct的实例 |
userdate, table |
method, delegate |
function |
立钻哥哥:复杂数据类型 |
++++【LuaTable】:C#侧指明从Lua侧输入(包括C#方法的输入参数或者Lua方法的返回值)LuaTable类型,则要求Lua侧为table。或者Lua侧的table,在C#侧未指明类型的情况下转换成LuaTable。
++++【LuaFunction】:C#侧指明从Lua侧输入(包括C#方法的输入参数或者Lua方法的返回值)LuaFunction类型,则要求Lua侧为function。或者Lua侧的function,在C#侧未指明类型的情况下转换成LuaFunction。
++++【LuaUserData】:对应非C# Managered对象的lua userdata。
++++【class或者struct的实例】:从C#传一个class或者struct的实例,将映射到Lua的userdata,并通过__index访问该userdata的成员,C#侧指明从Lua侧输入指定类型对象,Lua侧为该类型实例的userdata可以直接使用;如果该指明类型有默认构造函数,Lua侧是table则会自动转换,转换规则是:调用构造函数构造实例,并用table对应字段转换到C#对应值后赋值各成员。
++++【method,delegate】:成员方法以及delegate都是对应lua侧的函数。C#侧的普通参数以及引用参数,对应lua侧函数参数;C#侧的返回值对应于lua的第一个返回值;引用参数和out参数则按序对应于Lua的第2到第N个参数。
++2.5.2.5、宏
++++宏:HOTFIX_ENABLE、NOT_GEN_WARNING、GEN_CODE_MINIMIZE、
++++【HOTFIX_ENABLE】:打开hotfix功能。
++++【NOT_GEN_WARNING】:反射时打印warning。
++++【GEN_CODE_MINIMIZE】:以偏向减少代码段的方式生成代码。
++2.6、生成引擎二次开发指南
++++https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/custom_generate.md
++++xLua的生成引擎支持二次开发,可以利用它来生成一些文本类型的文件(比如代码,配置等)。xLua本身的link.xml文件的生成就是一个生成引擎插件做的。其他应用场景,比如生成Lua IDE的自动完成配置文件,都可以用这特性来完成。
++++插件需要提供两个东西:1、生成文件的模板;2、一个回调函数,该回调函数接受用户的配置,返回需要注入到模板的数据以及文件的输出流。
++2.6.1、模板语法
++++模板语法只有三种元素:eval、code、literal。
++++【eval】:语法是<%=exp%>,exp是任意表达式,将计算并以字符串形式输出exp的值;
++++【code】:语法是<% if true then end%>
++++【literal】:除了eval和code之外其它部分,literal原样输出。
//立钻哥哥:示例参考
<%
require “TemplateCommon”
%>
<%ForEachCsList(assembly_infos, function(assembly_info)%>
<%ForEachCsList(assembly_info.Types, function(type)
%>
<%end)%>
<%end)%>
++++TemplateCommon有一些预定义的函数可以使用,比如ForEachCsList。
++2.6.2、API
++++public static void CSObjectWrapEditor.Generator.CustomGen(string template_src, GetTasks get_tasks);
--template_src:模板的源码;
--get_tasks:回调函数,类型是GetTasks,用来接受用户的配置,返回需要注入到模板的数据以及文件的输出流;
++++public delegate IEnumerable<CustomGenTask> GetTasks(LuaEnv lua_env, UserConfig user_cfg);
--lua_env: LuaEnv对象,因为返回的模板数据需要放到LuaTable,需要用到LuaEnv.NewTable;
--user_cfg: 用户的配置;
--return: 返回值中,CustomGenTask代表的是一个生成文件,而IEnumerable类型表示同一个模板可以生成多个文件;
++2.6.3、标签
++++一般来说可以通过MenuItem开一个菜单来执行触发自定义生成操作,但有时希望生成操作直接由xLua的“Generate Code”菜单触发,就需要用到:CSObjectWrapEditor.GenCodeMenu。
++++示例:
[GenCodeMenu] //立钻哥哥:加到Generate Code菜单里头
public static void GenLinkXml(){
Generator.CustomGen(ScriptableObject.CreateInstance<LinkXmlGen>().Template.text, GetTasks);
}
++++立钻哥哥:以上所有相关代码都在“XLua/Src/Editor/LinkXmlGen”目录下,也是link.xml的生成功能的实现。
++2.7、xLua常见问题
++++https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/faq.md
++2.7.1、xLua发布包怎么用?
++++xLua目前以zip包形式发布,在工程目录下解压即可。
++2.7.2、报“please install the Tools”?
++++没有把Tools安装到Assets平级目录,安装包,或者master下都能找到这个目录。
++2.7.3、hotfix下怎么触发一个event?
++++首先通过xlua.private_accessible开启私有成员访问。
++++跟着通过对象的”&事件名”字段调用delegate,例如:self[‘&YanlzEvent’]()。
++2.7.4、什么是生成代码?
++++xLua支持的lua和C#间交互技术之一,这种技术通过生成两者间的适配代码来实现交互,性能较好,是推荐的方式。
++++另一种交互技术是反射,这种方式对安装包的影响更少,可以在性能要求不高或者对安装包大小很敏感的场景下使用。
++2.7.5、应该什么时候生成代码?
++++开发期不建议生成代码,可以避免很多由于不一致导致的编译失败,以及生成代码本身的编译等待。
++++build手机版本前必须执行生成代码,建议做成自动化的。
++++做性能调优,性能测试前必须执行生成代码,因为生成和不生成性能的区别还是很大的。
++2.7.6、CS命名空间下有所有C# API是不是很占内存?
++++由于用了lazyload,这个“有”只是个虚拟的概念,比如:UnityEngine.GameObject,是访问第一次CS.UnityEngine.GameObject或者第一个实例往lua传送才加载该类型方法,属性等。
++2.7.7、[LuaCallSharp]以及[CSharpCallLua]两种生成各在什么场景下用?
++++看调用者和被调用者,比如要在lua调用C#的GameObject.Find函数,或者调用gameobject的实例方法,属性等,GameObject类要加[LuaCallSharp],而想把一个lua函数挂到UI回调,这时调用者是C#,被调用的是一个lua函数,所以回调声明的delegate要加[CSharpCallLua]。
++++有时会比较迷惑人,比如List.Find(Predicate match)的调用,List当然是加[LuaCallSharp],而Predicate却要加[CSharpCallLua],因为match的调用者在C#,被调用的是一个lua函数。
++++立钻哥哥:更无脑一点的方式是看到“This delegate/interface must add to CSharpCallLua:XXX”,就把XXX加到[CSharpCallLua]即可。
++2.7.8、支持lua调用C#重载函数吗?
++++支持,但没有C#端支持的那么完善,比如重载方法void YanlzCalc(int a)和void YanlzCalc(short a),由于int和short都对应lua的number,是没法根据参数判断调用的是哪个重载。这时可以借助扩展方法来为其中一个起一个别名。
++2.7.9、调用LuaEnv.Dispose()崩溃?
++++很可能是这个Dispose操作是由lua那驱动执行,相当于在lua执行的过程中把lua虚拟机给释放了,改为只由C#执行即可。
++2.7.10、支持interface扩展方法么?
++++考虑到生成代码量,不支持通过obj:ExtentionMethod()的方式去调用,支持通过静态方法的方式去调用CS.ExtentionClass.ExtentionMethod(obj);
++2.8、xLua示例参考
++++一个完整的例子仅需3行代码(安装好xLua,建一个MonoBehaviour拖到场景,在Start()加入代码):
XLua.LuaEnv luaenv = new XLua.LuaEnv();
luaenv.DoString(“CS.UnityEngine.Debug.Log(‘立钻哥哥带您xLua热更新’)”);
luaenv.Dispose();
++++说明1:DoString参数为string,可输入任意合法的lua代码。
++++说明2:一个LuaEnv实例对应Lua虚拟机,处于开销的考虑,建议全局唯一。
++++C#主动调用lua很简单,比如要调用lua的系统函数,推荐方式是:声明、绑定、调用
--【声明】:
[XLua.CSharpCallLua]
public delegate double LuaMax(double a, double b);
--【绑定】:
var myLuaMax = luaenv.Global.GetInPath<LuaMax>(“math.max”);
--【调用】:
Debug.Log(“立钻哥哥:max: ” + myLuaMax(32, 12));
--立钻哥哥建议:建议绑定一次,重复使用。生成了代码的话,调用max是不产生gc alloc的。
++++更多示例:01_Helloworld、02_U#DScripting、03_UIEvent、04_LuaObjectOrented、05_NoGc、06_Coroutine、07_AsyncTest、08_Hotfix、09_GenericMethod、10_SignatureLoader、11_RawObject、12_ReImplementInLua、
++2.8.1、01_Helloworld:快速入门的例子
//立钻哥哥:快速入门的例子(\Assets\XLua\Examples\01_Helloworld\Helloworld.cs)
using UnityEngine;
using XLua;
public class Helloworld : MonoBehaviour{
//Use this for initialization
void Start(){
LuaEnv luaenv = new LuaEnv();
luaenv.DoString(“CS.UnityEngine.Debug.Log(‘立钻哥哥’)”);
luaenv.Dispose();
}
} //立钻哥哥:public class Helloworld : MonoBehaviour{}
++2.8.2、02_U3DScripting:展示怎么用lua来写MonoBehaviour
//立钻哥哥:展示怎么用lua来写MonoBehaviour
//(xLua-master\Assets\XLua\Examples\02_U3DScripting\LuaBehaviour.cs)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using XLua;
using System;
[System.Serializable]
public class Injection{
public string name;
public GameObject value;
} //立钻哥哥:public class Injection{}
[LuaCallCSharp]
public class LuaBehaviour : MonoBehaviour{
public TextAsset luaScript;
public Injection[] injections;
//立钻哥哥:all lua behaviour shared one luaenv only!
internal static LuaEnv luaEnv = new LuaEnv();
internal static float lastGCTime = 0;
internal const float GCInterval = 1; //1 second
private Action luaStart;
private Action luaUpdate;
private Action luaOnDestroy;
private LuaTable scriptEnv;
void Awake(){
scriptEnv = luaEnv.NewTable();
//为每个脚本设置一个独立的环境,可防止脚本间全局变量、函数冲突
LuaTable meta = luaEnv.NewTable();
meta.Set(“__index”, luaEnv.Global);
scriptEnv.SetMetaTable(meta);
meta.Dispose();
scriptEnv.Set(“self”, this);
foreach(var injection in injections){
scriptEnv.Set(injection.name, injection.value);
}
luaEnv.DoString(luaScript.text, “LuaBehaviour”, scriptEnv);
Action luaAwake = scriptEnv.Get<Action>(“awake”);
scirptEnv.Get(“start”, out luaStart);
scriptEnv.Get(“update”, out luaUpdate);
scriptEnv.Get(“ondestroy”, out luaOnDestroy);
if(luaAwake != null){
luaAwake();
}
}
//Use this for initialization
void Start(){
if(luaStart != null){
luaStart();
}
}
//Update is Called once per frame
void Update(){
if(luaUpdate != null){
luaUpdate();
}
if(Time.time - LuaBehaviour.lastGCTime > GCInterval){
luaEnv.Tick();
LuaBehaviour.lastGCTime = Time.time;
}
}
void OnDestroy(){
if(luaOnDestroy != null){
luaOnDestroy();
}
luaOnDestroy = null;
luaUpdate = null;
luaStart = null;
scriptEnv.Dispose();
Injections = null;
}
} //立钻哥哥:public class LuaBehaviour : MonoBehaviour{}
//立钻哥哥:lua部分(\Assets\XLua\Examples\02_U3DScripting\LuaTestScript.lua.txt)
local speed = 10
local lightCpnt = nil
function start()
print(“立钻哥哥:lua start...”)
print(“立钻哥哥:injected object”)
lightCpnt = lightObject:GetComponent(typeof(CS.UnityEngine.Light))
end
function update()
local r = CS.UnityEngine.Vector3.up * CS.UnityEngine.Time.deltaTime * speed
self.transform:Rotate(r)
lightCpnt.color = CS.UnityEngine.Color(CS.UnityEngine.Mathf.Sin(CS.UnityEngine.Time.time)/2 + 0.5, 0, 0, 1)
end
function ondestroy()
print(“立钻哥哥:lua destroy”)
end
++2.8.3、03_UIEvent:展示怎么用lua来写UI逻辑
//立钻哥哥:展示怎么用lua来写UI逻辑(\Examples\03_UIEvent\ButtonInteraction.lua.txt)
function start()
print(“立钻哥哥:lua start ...”)
self:GetComponent(“Button”).onClick:AddListener(function()
print(“立钻哥哥:clicked, you input is ‘ ”..input:GetComponent(“InputField”).text .. ”’ ”);
end)
end
++2.8.4、04_LuaObjectOrented:展示lua面向对象和C#的配合
//立钻哥哥:展示lua面向对象和C#的配合(\04_LuaObjectOriented\InvokeLua.cs)
using System;
using UnityEngine;
using XLua;
public class PropertyChangedEventArgs : EventArgs{
public string name;
public object value;
} //立钻哥哥:public class PropertyChangedEventArgs : EventArgs{}
public class InvokeLua : MonoBehaviour{
[CSharpCallLua]
public interface ICalc{
event EventHandler<PropertyChangedEventArgs> PropertyChanged;
int Add(int a, int b);
int Mult{ get; set; };
object this[int index]{ get; set; }
}
[CSharpCallLua]
public delegate ICalc CalcNew(int mult, params string[] args);
private string scrpit = @”
local calc_mt = {
__index = {
Add = function(self, a, b)
return (a + b) * self.Mult
end,
get_Item = function(self, index)
return self.list[index + 1]
end,
set_Item = function(self, index, value)
self.list[index + 1] = value
self:notify({ name = index, value = value })
end,
add_PropertyChanged = function(self, delegate)
if self.notifyList == nil then
self.notifylist = {}
end
table.insert(self.notifylist, delegate)
print(‘add’, delegate)
end,
remove_PropertyChanged = function(self, delegate)
for i = 1, #self.notifylist do
if CS.System.Object.Equals(self.notifylist[i], delegate) then
table.remove(self.notifylist, i)
break
end
end
print(‘remove’, delegate)
end,
notify = function(self, evt)
if self.notifylist ~= nil then
for i = 1, #self.notifylist do
self.notifylist[i](self, evt)
end
end
end,
} //立钻哥哥:__index = {}
} //立钻哥哥:local calc_mt = {}
Calc = {
New = functon(mult, ...)
print(...)
return setmetatable({Mult = mult, list = {‘aaaa’, ‘bbbb’, ‘cccc’}}, calc_mt)
end
}
”; //立钻哥哥:private string script = @” ”;
//Use this for initialization
void Start(){
LuaEnv luaenv = new LuaEnv();
//立钻哥哥:调用了带可变参数的delegate,函数结束都不会释放delegate,即使置空并调用GC
Test(luaenv);
luaenv.Dispose();
}
void Test(LuaEnv luaenv){
luaenv.DoString(script);
CalcNew calc_new = luaenv.Global.GetInPath<CalcNew>(“Calc.New”);
ICalc calc = calc_new(10, “hi”, “john”); //立钻哥哥:constructor
Debug.Log(“立钻哥哥:sum(*10)=” + calc.Add(1, 2));
calc.Mult = 100;
Debug.Log(“立钻哥哥:sum(*100)=” + calc.Add(1, 2));
Debug.Log(“立钻哥哥:list[0]=” + calc[0]);
Debug.Log(“立钻哥哥:list[1]=” + calc[1]);
calc.PropertyChanged += Notify;
calc[1] = “dddd”;
Debug.Log(“立钻哥哥:list[1]=” + calc[1]);
calc.PropertyChanged -= Notify;
calc[1] = “eeee”;
Debug.Log(“立钻哥哥:list[1]=” + calc[1]);
}
void Notify(object sender, PropertyChangedEventArgs e){
Debug.Log(string.Format(“{0} has property changed {1}={2}”, sender, e.name, e.value));
}
} //立钻哥哥:public class InvokeLua : MonoBehaviour{}
++2.8.5、05_NoGc:展示怎么去避免值类型的GC
//立钻哥哥:展示怎么去避免值类型的GC(\Assets\XLua\Examples\05_NoGc\NoGc.cs)
using UnityEngine;
using System;
using XLua;
namespace XLuaTest{
[GCOptimize]
[LuaCallCSharp]
public struct Pedding{
public byte c;
} //立钻哥哥:public struct Pedding{}
[GCOptimize]
[LuaCallCSharp]
public struct MyStruct{
public MyStruct(int p1, int p2){
a = p1;
b = p2;
c = p2;
e.c = (byte)p1;
}
public int a;
public int b;
public decimal c;
public Pedding e;
} //立钻哥哥:public struct MyStruct{}
[LuaCallCSharp]
public enum MyEnum{
E1,
E2
}
[CSharpCallLua]
public delegate int IntParam(int p);
[CSharpCallLua]
public delegate Vector3 Vector3Param(Vector3 p);
[CSharpCallLua]
public delegate MyStruct CustomValueTypeParam(MyStruct p);
[CSharpCallLua]
public delegate MyEnum EnumParam(MyEnum p);
[CSharpCallLua]
public delegate decimal DecimalParam(decimal p);
[CSharpCallLua]
Public delegate void ArrayAccess(Array arr);
[CSharpCallLua]
public interface IExchanger{
void exchange(Array arr);
}
[LuaCallCSharp]
public class NoGc : MonoBehaviour{
LuaEnv luaenv = new LuaEnv();
IntParam f1;
Vector3Param f2;
CustomValueTypeParam f3;
EnumParam f4;
DecimalParam f5;
ArrayAccess farr;
Action flua;
IExchanger ie;
LuaFunction add;
[NonSerialized]
public double[] a1 = new double[]{ 1, 2 };
[NonSerialized]
public Vector3[] a2 = new Vector3[]{ new Vector3(1,2,3), new Vector3(4,5,6) };
[NonSerialized]
public MyStruct[] a3 = new MyStruct[]{ new MyStruct(1,2), new MyStruct(3,4) };
[NonSerialized]
public MyEnum[] a4 = new MyEnum[]{ MyEnum.E1, MyEnum.E2 };
[NonSerialized]
public decimal[] a5 = new decimal[]{ 1.00001M, 2.00002M };
public float FloatParamMethod(float p){
return p;
}
public Vector3 Vector3ParamMethod(Vector3 p){
return p;
}
public MyStruct StructParamMethod(MyStruct p){
return p;
}
public MyEnum EnumParamMethod(MyEnum p){
return p;
}
public decimal DecimalParamMethod(decimal p){
return p;
}
//Use this for initialization
void Start(){
luaenv.DoString(@”
function id(...)
return ...
end
function add(a,b) return a + b end
function array_exchange(arr)
arr[0], arr[1] = arr[1], arr[0]
end
local v3 = CS.UnityEngine.Vector3(7, 8, 9)
local vt = CS.XLuaTest.MyStruct(5, 6)
function lua_access_csharp()
monoBehaviour:FloatParamMethod(123) --primitive
monoBehaviour:Vector3ParamMethod(v3) --vector3
local rnd = math.random(1, 100)
local r = monoBehaviour:Vector3ParamMethod({x=1, y=2, z=rnd})
assert(r.x==1 and r.y==2 and r.z==rnd)
monoBehaviour:StructParamMethod(vt) --custom struct
r = monoBehaviour:StructParamMethod({a=1, b=rnd, e={c=rnd}})
assert(r.b==rnd and r.e.c==rnd)
monoBehaviour:EnumParamMethod(CS.XLuaTest.MyEnum.E2) --enum
monoBehaviour:DecimalParamMethod(monoBehaviour.a5[0])
monoBehaviour.a1[0], monoBehaviour.a1[1]=monoBehaviour.a1[1],monoBehaviour.a1[0] --field
end
exchanger = {
exchange = function(self, arr)
array_exchange(arr)
end
}
A = { B={ C=789}}
GDATA = 1234;
”);
luaenv.Global.Set(“monoBehaviour”, this);
luaenv.Global.Get(“id”, out f1);
luaenv.Global.Get(“id”, out f2);
luaenv.Global.Get(“id”, out f3);
luaenv.Global.Get(“id”, out f4);
luaenv.Global.Get(“id”, out f5);
luaenv.Global.Get(“array_exchange”, out farr);
luaenv.Global.Get(“lua_access_csharp”, out flua);
luaenv.Global.Get(“exchanger”, out ie);
luaenv.Global.Get(“add”, out add);
luaenv.Global.Set(“g_int”, 123);
luaenv.Global.Set(123, 456);
int i;
luaenv.Global.Get(“g_int”, out i);
Debug.Log(“立钻哥哥:g_int: ” + i);
luaenv.Global.Get(123, out i);
Debug.Log(“立钻哥哥:123: ” + i);
} //立钻哥哥:void Start(){}
//Update is called once per frame
void Update(){
//立钻哥哥:C# call lua function with value type but no gc (using delegate)
f1(1); //立钻哥哥:primitive type
f2(new Vector3(1, 2, 3)); //立钻哥哥:vector3
MyStruct mystruct1 = new MyStruct(5, 6);
f3(mystruct1); //立钻哥哥:custom complex value type
f4(MyEnum.E1); //立钻哥哥:enum
decimal dec1 = -32132143143100109.00010001010M;
f5(dec1); //立钻哥哥:decimal
add.Func<int, int, int>(); //立钻哥哥:LuaFunction.Func
//立钻哥哥:lua access C# value type array no gc
farr(a1); //立钻哥哥:primitive value type array
farr(a2); //立钻哥哥:vector3 array
farr(a3); //立钻哥哥:custom struct array
farr(a4); //立钻哥哥:enum array
farr(a5); //立钻哥哥:decimal array
//立钻哥哥:lua call C# no gc with value type
flua();
//立钻哥哥:C# call lua using interface
ie.exchange(a2);
//立钻哥哥:no gc LuaTable use
luaenv.Global.Set(“g_int”, 456);
int i;
luaenv.Global.Get(“g_int”, out i);
luaenv.Global.Set(123.0001, mystruct1);
MyStruct mystruct2;
luaenv.Global.Get(123.0002, out mystruct2);
decimal dec2 = 0.0000001M;
luaenv.Global.Set((byte)12, dec1);
luaenv.Global.Get((byte)12, out dec2);
int gdata = luaenv.Global.Get<int>(“GDATA”);
luaenv.Global.SetInPath(“GDATA”, gdata + 1);
int abc = luaenv.Global.GetInPath<int>(“A.B.C”);
luaenv.Global.SetInPath(“A.B.C”, abc + 1);
luaenv.Tick();
} //立钻哥哥:void Update(){}
void OnDestroy(){
f1 = null;
f2 = null;
f3 = null;
f4 = null;
f5 = null;
farr = null;
flua = null;
ie = null;
add = null;
luaenv.Dispose();
} //立钻哥哥:void OnDestroy(){}
} //立钻哥哥:public class NoGc : MonoBehaviour{}
} //立钻哥哥:namespace XLuaTest{}
++2.8.6、06_Coroutine:展示lua协程怎么和Unity协程相配合
++2.8.6、06_Coroutine:展示lua协程怎么和Unity协程相配合
++2.8.6、06_Coroutine:展示lua协程怎么和Unity协程相配合
++2.8.6、06_Coroutine:展示lua协程怎么和Unity协程相配合
//立钻哥哥:展示lua协程怎么和Unity协程相配合
//Runner(xLua-master\Assets\XLua\Examples\06_Coroutine\Coroutine_Runner.cs)
using UnityEngine;
using XLua;
using System.Collections.Generic;
using System.Collections;
using System;
[LuaCallCSharp]
public class Coroutine_Runner : MonoBehaviour{
public void YieldAndCallback(object to_yield, Action callback){
StartCoroutine(CoBody(to_yield, callback));
}
private IEnumerator CoBody(object to_yield, Action callback){
if(to_yield is IEnumerator){
yield return StartCoroutine((IEnumerator)to_yield);
}else{
yield return to_yield;
}
callback();
}
public static class CoroutineConfig{
[LuaCallCSharp]
public static List<Type> LuaCallCSharp{
get{
return new List<Type>(){
typeof(WaitForSeconds),
typeof(WWW)
};
}
}
}
} //立钻哥哥:public class Coroutine_Runner : MonoBehaviour{}
//立钻哥哥:CoroutineTest(xLua-master\Assets\XLua\Examples\06_Coroutine\CoroutineTest.cs)
using UnityEngine;
using XLua;
public class CoroutineTest : MonoBehaviour{
LuaEnv luaenv = null;
//Use this for initialization
void Start(){
luaenv = new LuaEnv();
luaenv.DoString(“require ‘coroutine_test’”);
}
//Update is called once per frame
void Update(){
if(luaenv != null){
luaenv.Tick();
}
}
void OnDestroy(){
luaenv.Dispose();
}
} //立钻哥哥:public class CoroutineTest:MonoBehaviour{}
//立钻哥哥:coruntine_test.lua(\Examples\06_Coroutine\Resources\coruntine_test.lua.txt)
local util = require ‘xlua.util’
local yield_return = (require ‘cs_coroutine’).yield_return
local co = coroutine.create(function()
print(‘coroutine start!’)
local s = os.time()
yield_return(CS.UnityEngine.WaitForSeconds(3))
print(‘wait interval:’, os.time() - s)
local www = CS.UnityEngine.WWW(‘http://www.lovezuanzuan.com’)
yield_return(www)
if not www.error then
print(www.bytes)
else
print(‘error:’, www.error)
end
end)
assert(coroutine.resume(co))
//立钻哥哥:cs_coroutine.lua(\Examples\06_Coroutine\Resources\cs_coroutine.lua.txt)
local util = require ‘xlua.util’
local gameobject = CS.UnityEngine.GameObject(‘Coroutine_Runner’)
CS.UnityEngine.Object.DontDestroyOnLoad(gameobject)
local cs_coroutine_runner = gameobject:AddComponent(typeof(CS.Coroutine_Runner))
local function async_yield_return(to_yield, cb)
cs_coroutine_runner:YieldAndCallback(to_yield, cb)
end
return{
yield_return = util.async_to_sync(async_yield_return)
}
++2.8.7、07_AsyncTest:展示怎么用lua协程来把异步逻辑同步化
++2.8.7、07_AsyncTest:展示怎么用lua协程来把异步逻辑同步化
//立钻哥哥:展示怎么用lua协程来把异步逻辑同步化
//(xLua-master\Assets\XLua\Examples\07_AsyncTest\AsyncTest.cs)
using UnityEngine;
using XLua;
using System.Collection.Generic;
using System;
public class AsyncTest : MonoBehaviour{
LuaEnv luaenv = null;
void Start(){
luaenv = new LuaEnv();
luaenv.DoString(“require ‘async_test’”);
}
//Update is called once per frame
void Update(){
if(luaenv != null){
luaenv.Tick();
}
}
} //立钻哥哥:public class AsyncTest : MonoBehaviour{}
//立钻哥哥:MessageBox(xLua-master\Assets\XLua\Examples\07_AsyncTest\MessageBox.cs)
using UnityEngine;
using UnityEngine.UI;
using XLua;
using System.Collections.Generic;
using System;
using UnityEngine.Events;
public class MessageBox : MonoBehaviour{
public static void ShowAlertBox(string message, string title, Action onFinished = null){
var alertPanel = GameObject.Find(“Canvas”).transform.Find(“AlertBox”);
if(alertPanel == null){
alertPanel = (Instantiate(Resources.Load(“AlertBox”)) as GameObject).transform;
alertPanel.gameObject.name = “AlertBox”;
alertPanel.SetParent(GameObject.Find(“Canvas”).transform);
alertPanel.localPosition = new Vector3(-6f, -6f, 0f);
}
alertPanel.Find(“title”).GetComponent<Text>().text = title;
alertPanel.Find(“message”).GetComponent<Text>().text = message;
var button = alertPanel.Find(“alertBtn”).GetComponent<Button>();
UnityAction onclick = ()=>{
if(onFinished != null){
onFinished();
}
button.onClick.RemoveAllListeners();
alertPanel.gameObject.SetActive(false);
};
//立钻哥哥:防止消息框未关闭时多次被调用
button.onClick.RemoveAllListeners();
button.onClick.AddListener(onclick);
alertPanel.gameObject.SetActive(true);
} //立钻哥哥:public static void ShowAlertBox(){}
public static void ShowConfirmBox(string message, string title, Action<bool> onFinished = null){
var confirmPanel = GameObject.Find(“Canvas”).transform.Find(“ConfirmBox”);
if(confirmPanel == null){
confirmPanel = (Instantiate(Resources.Load(“ConfirmBox”)) as GameObject).transform;
confirmPanel.gameObject.name = “ConfirmBox”;
confirmPanel.SetParent(GameObject.Find(“Canvas”).transform);
confirmPanel.localPosition = new Vector3(-8f, -18f, 0f);
}
confirmPanel.Find(“confirmTitle”).GetComponent<Text>().text = title;
confirmPanel.Find(“conmessage”).GetComponent<Text>().text = message;
var confirmBtn = confirmPanel.Find(“confirmBtn”).GetComponent<Button>();
var cancelBtn = confirmPanel.Find(“cancleBtn”).GetComponent<Button>();
Action cleanup = ()=>{
confirmBtn.onClick.RemoveAllListeners();
cancelBtn.onClick.RemoveAllListeners();
confirmPanel.gameObject.SetActive(false);
};
UnityAction onconfirm = ()=>{
if(onFinished != null){
onFinished(true);
}
cleanup();
};
UnityAction oncancel = ()=>{
if(onFinished != null){
onFinished(false);
}
cleanup();
};
//立钻哥哥:防止消息框未关闭多次被调用
confirmBtn.onClick.RemoveAllListeners();
confirmBtn.onClick.AddListener(onconfirm);
cancelBtn.onClick.RemoveAllListeners();
cancelBtn.onClick.AddListener(oncancel);
confirmPanel.gameobject.SetActive(true);
} //立钻哥哥:public static void ShowConfirmBox(){}
} //立钻哥哥:public class MessageBox : MonoBehaviour{}
public static class MessageBoxConfig{
[CSharpCallLua]
public static List<Type> CSharpCallLua = new List<Type>(){
typepf(Action),
typeof(Action<bool>),
typeof(UnityAction),
};
} //立钻哥哥:public static class MessageBoxConfig{}
//立钻哥哥:aync_test.lua(\Assets\XLua\Examples\07_AsyncTest\Resources\async_test.lua.txt)
local util = require ‘xlua.util’
local message_box = require ‘message_box’
---------------------------async_recharge--------------------------------------
//立钻哥哥:模拟异步充值
local function async_recharge(num, cb)
print(‘立钻哥哥:request server ....’)
cb(true, num);
end
local recharge = util.async_to_sync(async_recharge)
---------------------------async_recharge end--------------------------------------
local buy = function()
message_box.alert(“立钻哥哥:您余额不足,请充值!”, “余额提醒”)
if message_box.confirm(“确认充值10元吗?”, “确认框”) then
local r1, r2 = recharge(10)
print(‘立钻哥哥:recharge result:’, r1, r2);
message_box.alert(“充值成功”, “提示”)
else
print(‘立钻哥哥:cancel’)
message_box.alert(“取消充值!”, “提示”)
end
print(‘立钻哥哥:recharge finished!’);
end
//立钻哥哥:将按钮监听点击事件,绑定buy方法
CS.UnityEngine.GameObject.Find(“Button”):GetComponent(“Button”).onClick:AddListener(util.coroutine_call(buy))
//立钻哥哥:message_box.lua(\XLua\Examples\07_AsyncTest\Resources\message_box.lua.txt)
local util = require ‘xlua.util’
local sync_alert = util.async_to_sync(CS.MessageBox.ShowAlertBox)
local sync_confirm = util.async_to_sync(CS.MessageBox.ShowConfirmBox)
//立钻哥哥:构造alert和confirm函数
return{
alert = function(message, title)
sync_alert(message, title)
end;
confirm = function(message, title)
local ret = sync_confirm(message, title)
return ret == true
end;
}
++立钻哥哥推荐的拓展学习链接(Link_Url):
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
++++Lua快速入门篇(XLua教程):https://blog.csdn.net/VRunSoftYanlz/article/details/81141502
++++Lua快速入门篇(基础概述):https://blog.csdn.net/VRunSoftYanlz/article/details/81041359
++++框架知识点:https://blog.csdn.net/VRunSoftYanlz/article/details/80862879
++++游戏框架(UI框架夯实篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80781140
++++游戏框架(初探篇):https://blog.csdn.net/VRunSoftYanlz/article/details/80630325
++++设计模式简单整理:https://blog.csdn.net/vrunsoftyanlz/article/details/79839641
++++U3D小项目参考:https://blog.csdn.net/vrunsoftyanlz/article/details/80141811
++++UML类图:https://blog.csdn.net/vrunsoftyanlz/article/details/80289461
++++Unity知识点0001:https://blog.csdn.net/vrunsoftyanlz/article/details/80302012
++++U3D_Shader编程(第一篇:快速入门篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80372071
++++U3D_Shader编程(第二篇:基础夯实篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80372628
++++Unity引擎基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78881685
++++Unity面向组件开发:https://blog.csdn.net/vrunsoftyanlz/article/details/78881752
++++Unity物理系统:https://blog.csdn.net/vrunsoftyanlz/article/details/78881879
++++Unity2D平台开发:https://blog.csdn.net/vrunsoftyanlz/article/details/78882034
++++UGUI基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78884693
++++UGUI进阶:https://blog.csdn.net/vrunsoftyanlz/article/details/78884882
++++UGUI综合:https://blog.csdn.net/vrunsoftyanlz/article/details/78885013
++++Unity动画系统基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78886068
++++Unity动画系统进阶:https://blog.csdn.net/vrunsoftyanlz/article/details/78886198
++++Navigation导航系统:https://blog.csdn.net/vrunsoftyanlz/article/details/78886281
++++Unity特效渲染:https://blog.csdn.net/vrunsoftyanlz/article/details/78886403
++++Unity数据存储:https://blog.csdn.net/vrunsoftyanlz/article/details/79251273
++++Unity中Sqlite数据库:https://blog.csdn.net/vrunsoftyanlz/article/details/79254162
++++WWW类和协程:https://blog.csdn.net/vrunsoftyanlz/article/details/79254559
++++Unity网络:https://blog.csdn.net/vrunsoftyanlz/article/details/79254902
++++C#事件:https://blog.csdn.net/vrunsoftyanlz/article/details/78631267
++++C#委托:https://blog.csdn.net/vrunsoftyanlz/article/details/78631183
++++C#集合:https://blog.csdn.net/vrunsoftyanlz/article/details/78631175
++++C#泛型:https://blog.csdn.net/vrunsoftyanlz/article/details/78631141
++++C#接口:https://blog.csdn.net/vrunsoftyanlz/article/details/78631122
++++C#静态类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630979
++++C#中System.String类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630945
++++C#数据类型:https://blog.csdn.net/vrunsoftyanlz/article/details/78630913
++++Unity3D默认的快捷键:https://blog.csdn.net/vrunsoftyanlz/article/details/78630838
++++游戏相关缩写:https://blog.csdn.net/vrunsoftyanlz/article/details/78630687
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
--_--VRunSoft : lovezuanzuan--_--