《Lua热更新》

##《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快速入门篇

++++第一章:Lua基础概述

++++第二章:xLua教程

++++第三章:Lua基础拓展

++++第四章:立钻哥哥带您学Lua热更新

##第二章:xLua教程

++第二章: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入门教程

###2.1、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的配置

###2.2、xLua的配置

++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<Type>就可以了。

[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<Type, List>的field或者property。(key指明的是生效的类,value是一个列表,配置的是不生成代码的函数、字段、属性的名字。)

++++[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<T>:ForEach;

--另外一类场景是通过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<Type, List<string>>类型,Dictionary的Key是要生效的类型,Value是属性名列表。

++++可以参考XLua对几个UnityEngine下值类型的配置,SysGCOptimize类等。

++2.2.10、XLua.BlackList

++++如果不要生成一个类型的一些成员的适配代码,可以通过这个配置来实现。

++++标签方式比较简单,对应的成员上加上就可以了。

++++由于考虑到有可能需要把重载函数的其中一个重载列入黑名单,配置方式比较复杂,类型是List<List<string>>,对于每个成员,

--在第一层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、热补丁操作指南

###2.3、热补丁操作指南

++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库

###2.4、XLua增加删除第三方lua库

++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

###2.5、xLua API

++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<TKey, TValue>()、Set<TKey, TValue>、Cast()、SetMetaTable()、

++++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<int>(“a.b.c”)相当于在lua里头执行i = tbl.a.b.c,避免仅为了获取中间变量而多次调用Get,执行效率更高。

++2.5.1.2.3、void SetInPath(string path, T val);

++++描述:和GetInPath<T>对应的setter;

++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<TKey, TValue>的setter;

++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.5uint64.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_ENABLENOT_GEN_WARNINGGEN_CODE_MINIMIZE

++++【HOTFIX_ENABLE】:打开hotfix功能。

++++【NOT_GEN_WARNING】:反射时打印warning。

++++【GEN_CODE_MINIMIZE】:以偏向减少代码段的方式生成代码。

###2.6、生成引擎二次开发指南

###2.6、生成引擎二次开发指南

++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”

%>

<linker>

<%ForEachCsList(assembly_infos, function(assembly_info)%>

<assembly fullname=”<%=assembly_info.FullName%>”>

<%ForEachCsList(assembly_info.Types, function(type)

%><type fullname=”<%=type:ToString()%>” preserve=”all”/>

<%end)%>

</assembly>

<%end)%>

</linker>

++++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常见问题

###2.7、xLua常见问题

++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示例参考

###2.8、xLua示例参考

++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<T1, T2, TResult>

//立钻哥哥: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;

}

###2.9、xLua拓展总结

###2.9、xLua拓展总结

++立钻哥哥推荐的拓展学习链接(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知识点0001https://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--_--

Lua快速入门篇(XLua教程)(Yanlz+热更新+xLua+配置+热补丁+第三方库+API+二次开发+常见问题+示例参考)相关推荐

  1. Lua快速入门篇(基础概述)(Yanlz+toLua+xLua)

    <Lua热更新> ##<Lua热更新>发布说明: ++++"Lua热更新"开始了,立钻哥哥终于开始此部分的探索了. ++++作为游戏发布迭代的重要技术:Lu ...

  2. JSON数据结构(A、快速入门篇)(Yanlz+Data+JsonArray+JsonObject+JsonValue+JsonMapper.ToJson+JsonMapper.ToObject+)

    <JSON数据结构> 版本 作者 参与者 完成日期 备注 Data_JSON_V01_1.0 严立钻 2018.08.24 ++++一个好用的JSON在线编辑:http://www.kjs ...

  3. VIVE开发基础(A、快速入门篇)(Yanlz+HTC+VIVE+VR+AR+MR+XR+SteamVR+CameraRig+LightHouse+HeadSet+Teleport+立钻哥哥+)

    <HTC_VIVE开发基础> 版本 作者 参与者 完成日期 备注 HTC_VIVE_V01_1.0 严立钻 2018.08.23 <HTC_VIVE开发基础>发布说明: +++ ...

  4. Git快速入门篇—— Windows版本淘宝镜像快速下载安装详细步骤及简单入门教程(附带图文教程)

    Git快速入门篇-- Windows版本淘宝镜像快速下载安装详细步骤及简单入门教程(附带图文教程) 前言:我们平时在整理代码的时候,尤其是与别人一起开发项目的时候,常常涉及到代码的更新,因此代码版本问 ...

  5. Lua 快速入门(一)——基础语法

    前言 Lua是由标准C实现的解释型语言,它的执行效率比较高,通常作为脚本嵌入到应用程序中. 由于支持热更新,Lua在游戏开发领域应用普遍.作为从业人员,不得不来填这个大坑. 本文是Lua入门学习笔记, ...

  6. OpenCV快速入门篇(Python实现)

    OpenCV快速入门篇(Python实现) 转载自:https://blog.csdn.net/feilong_csdn/article/details/82750029 本系列python版本:py ...

  7. HTML5+app开发学习之快速入门篇

    HTML5+app开发学习之快速入门篇 5+app开发概念理解相关 开发环境与支持 快速入门实战 5+app开发概念理解相关 见博文:学习跨平台移动应用开发必须理解的一些概念 开发环境与支持 开发环境 ...

  8. 穿越 java | 快速入门篇 - 第1节 计算机基础知识

    主题:计算机基础知识 开发环境 更多干货 定义 作用 组成元件 CPU 内存 cpu里的高速缓存 BIOS软件(基础输入输出系统) CMOS芯片 机械硬盘 组成 数据存取过程 文件编码 ASCII G ...

  9. 运动控制器编程_快速入门 | 篇二十一:运动控制器ZHMI组态编程简介一

    点击上方"正运动小助手",随时关注新动态! 运动控制器ZHMI组态编程简介一  今天我们来学习一下,运动控制器的ZHMI组态编程简介.本文主要从产品概述.控制器连接触摸屏使用.HM ...

最新文章

  1. 邬贺铨:工业物联网的技术与前景
  2. virtio驱动_0020 virtio-blk简易驱动
  3. 女神节快乐!世界上第一位程序员就是女神
  4. 程序员最想得到的十大证件
  5. 随身助手271个可用api接口网站php源码(随身助手API)
  6. 决策树算法(ID3算法)
  7. laravel whereDoesntHave 查询不等于条件的数据
  8. 使用三防漆的安全说明
  9. 垃圾分类小程序,识别和站点查询,答题,用户后台管理集一体的小程序
  10. 认识PV、UV、UV价值
  11. PS 2019 Mac版 自学入门系列(三)——插入对象
  12. 字节跳动启动大规模校招
  13. 人间清醒,内容为王 - 技术er究竟该如何写博客?1024上海嘉年华之敖丙演讲观后感。
  14. pygame简单伪3d赛车赛道
  15. 仿淘宝详情页 直接上代码
  16. 医疗建筑智能化工程项目能耗监测系统的研究与应用
  17. PrestoOnYarn搭建及其问题解决方案总结
  18. 苹果手机怎么备份所有数据,2个步骤轻松解决(免费)
  19. 【CSDN软件工程师能力认证学习精选】 什么是前端工程化?
  20. HDMI拼接、分割、无缝切换、视频矩阵方案

热门文章

  1. 婚礼上新郎经典发言稿!搞笑!
  2. handsontable下拉数字递增效果vue
  3. ArcGIS应用基础4 专题图的制作
  4. 3.4softmax回归
  5. php 去掉excel导入字符串所有空格
  6. Java四舍五入及保留小数点位数
  7. 从头搭建VUE环境,使用IDEA 2021创建VUE项目
  8. 现货黄金入门:资产配置技巧
  9. Opencv中的透视变换
  10. 王者荣耀攻略——典韦玩法