《Lua热更新》

##《Lua热更新》发布说明:

++++“Lua热更新”开始了,立钻哥哥终于开始此部分的探索了。

++++作为游戏发布迭代的重要技术:Lua热更新在网络游戏迭代更新中非常重要,特别是对于AppStore这样的平台,我们只需要定期更新主App,原则上可以随时灵活更新我们的以Lua热更新框架为基础的代码。

++++当然,作为一项新引入的技术,我们通常是以【快速入门】=>【基础夯实】=>【中级进阶】=>【高级实战】=>【立钻哥哥带您学Lua热更新】等几个阶段进行探索。

##《Lua热更新》目录:

#第一篇:Lua快速入门篇

#第二篇:Lua基础夯实篇

#第三篇:Lua中级进阶篇

#第四篇:Lua高级实战篇

#第五篇:立钻哥哥带您学Lua热更新

#第一篇:Lua快速入门篇

#第一篇:Lua快速入门篇

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

++++第二章:xLua教程

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

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

##第一章:Lua基础概述

++第一章:Lua基础概述

++++1.1、Lua热更新框架

++++1.2、toLua#热更新框架

++++1.3、xLua热更新框架

++++1.4、立钻哥哥带您学Lua基础

###1.1、Lua热更新框架

++1.1、Lua热更新框架

++++toLua:https://github.com/topameng/tolua

++++uLua: http://ulua.org/index.html

++++xLua: https://github.com/Tencent/xLua

++++C#在开发效率和运行效率平衡得很好,语言特性也比较全,是很优秀的一门语言。

++++lua被称为游戏脚本之王,在游戏领域应用比较广泛,它设计之初就考虑到嵌入式领域,比如相对它提供的特性来说,它体积非常小,启动占资源也不多,性能是脚本里的佼佼者。

++++lua相对于C#而言,首先是它支持解析执行,进而支持热更新。(免编译对开发效率提升也很大,特别是较大的项目。)(从运行效率上说C#比最快的lua方案也要快50倍左右。)

++++IOS不能热更新,不是因为不能用反射,是因为【System.Reflection.Assembly.Load】、【System.Reflection.Emit】、【System.CodeDom.Compiler】等无法使用,Unity原生的代码逻辑,无论是以前的MonoAOT或者后来的il2cpp,都是编译成native code,iOS下是跑不了的,立钻哥哥:IOS下不能动态载入dll或者cs文件,已经编译进App的没有问题。

++++立钻哥哥:以lua热更技术为基础的框架有:toLua、uLua、xLua、SLua、C#light等。(ulua+ngui)(tolua+gui)(xlua+ngui)(ulua作者已不再维护,转至tolua)(tolua的性能表现好)

++1.1.1、toLua

++++toLua(gitHub):https://github.com/topameng/tolua

++++toLua是一个工具,将Unity的C#代码包装之后导出给Lua,同时提供了一些访问Lua的接口,使得Unity和Lua可以相互调用。

++++toLua#是Unity静态绑定Lua的一个解决方案,它通过C#提供的反射信息分析代码生成包装的类。(它是一个用来简化在C#中集成Lua的插件,可以自动生成用于在Lua中访问Unity的绑定代码,并把C#中的常量、变量、函数、属性、类以及枚举暴露给lua。)

++++toLua#底层库是使用toLua(C语言编写),那么就需要通过C#来调用原生代码。

++++toLua#集成主要分两部分:

--第一部分:运行时需要的代码,包括一些手写的和自动生成的绑定代码;

--第二部分:编辑器相关代码,主要提供代码生成、编译lua文件等操作,具体就是Unity编辑器中提供的功能。

++++【ToLua/Assembly-CSharp】:References/、Source/、ToLua/

--【References/】:

--【Source/】:Generate/、LuaConst.cs

----Generate/:这个文件里面是生成的绑定代码;

----LuaConst.cs:这个文件是一些lua路径等配置文件;

--【ToLua/】:BaseType/、Core/、Examples/、Misc、Reflection、ReadMe.txt

----BaseType/:一些基础类型的绑定代码;

----Core/:提供的一些核心功能,包括封装的LuaFunction、LuaTable、LuaThread、LuaState、LuaEvent、调用tolua原生代码等;

----Examples/:示例代码;

----Misc/:杂项,包括LuaClient、LuaCoroutine(协程)、LuaLooper(用于tick)、LuaResLoader(用于加载Lua文件);

----Reflection/:反射相关;

++++【ToLua/Assembly-CSharp-Editor】:References/、Editor/、ToLua/

--【Editor/Custom/CustomSettings.cs】:自定义配置文件,用于定义哪些类作为静态类型、哪些类需要导出、哪些附加委托需要导出等。

--【ToLua/Editor】:Extend/、ToLuaExport.cs、ToLuaMenu.cs、ToLuaTree.cs

----Extend/:扩展一些类的方法;

----ToLuaExport.cs:真正生成Lua绑定的代码;

----ToLuaMenu.cs:Lua菜单上功能对应的代码;

----ToLuaTree.cs:辅助树结构;

++++Generate All流程:生成绑定代码主要放在ToLuaExport.cs里面:

--【GenLuaDelegates()函数】:生成委托绑定的代码,它会从CustomSettings.customDelegateList里面取出所有自定义导出的委托列表,然后把CustomSettings.customTypeList里面的所有类型中的委托根据一定规则加入到list中,最后调用ToLuaExport.GenDelegates()方法来生成委托绑定的代码,生成的代码在DelegateFactory.cs文件中。(立钻哥哥:该函数的详细实现可查看ToLuaExport.cs中的函数实现。)

--【GenerateClassWraps()函数】:遍历所有需要导出的类,然后调用ToLuaExport.Generate()方法来生成类的绑定代码。

++1.1.2、uLua

++++uLua.org: http://ulua.org/index.html

++++uLua:基于tolua#的Lua热更新UGUI/NGUI框架。(uLua已停止维护,由toLua#代替。)

++++uLua的原理:给GameObject添加上一个C#脚本组件作为中间层,在中间层上绑定上一个Lua脚本,将Unity的所有回调接口通过中间层传递到Lua。(Lua脚本也可以通过中间层操作GameObject。)

++++uLua要使用最新版本,早期的uLua是使用反射机制,脚本的运行效率比较糟糕,新的uLua集成了cstolua,预先生成一批代码把Unity的类和函数导出给lua,然后lua再调用,这样无论是效率还是GC的角度说都是比较完美的。

++1.1.3、xLua

++++xLua官方: https://github.com/Tencent/xLua

++++xLua是Unity3D下Lua编程解决方案,腾讯已将xLua开源到GitHub。

++++xLua是2015年3月完成第一个版本;2016年12月末,xLua实现新的突破:全平台支持用Lua修复C#代码bug。

++++xLua为Unity、.Net、Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便和C#相互调用。(xLua在功能、性能、易用性都有不少突破。)

++++xLua的突破:1、可以运行时把C#实现(方法,操作符,属性,事件等等)替换成lua实现;2、出色的GC优化,自定义struct,枚举在Lua和C#间传递无C# gc alloc;3、编辑器下无需生成代码,开发更轻量。

++++xLua热补丁技术支持在运行时把一个C#实现(函数,操作符,属性,事件,或者整个类)替换成Lua实现,意味着我们可以:1、平时用C#开发;2、运行也是C#,性能秒杀Lua;3、有bug的地方下发个Lua脚本fix了,下次整体更新时可以把Lua的实现换回正确的C#实现,更新时甚至可以做到不重启游戏;

++++xLua热修复框架工程结构参考:

++++【Resource/xLua/Main.lua】:xlua热修复入口。

++++【Resource/xLua/Common/】:提供给lua代码使用的一些工具方法,提供lua逻辑代码到C#调用的一层封装。

++++【Scripts/xLua/XLuaManager.cs】:xLua热修复环境,包括luaState管理,自定义loader。

++++【Scripts/xLua/Util/】:为xLua的lua脚本提供的C#侧代码支持,被Resources/xLua/Common/所使用。

++++【Scripts/HotfixTest】:需要热修复的C#脚本。

++++【Resource/xLua/HotFix】:热修复脚本。

###1.2、toLua#热更新框架

++1.2、toLua#热更新框架

++++toLua:https://github.com/topameng/tolua

++++toLua#是Unity静态绑定lua的一个解决方案,它通过C#提供的反射信息分析代码并生成包装的类。它是一个用来简化在C#中集成lua的插件,可以自动生成用于在lua中访问Unity的绑定代码,并把C#中的常量、变量、函数、属性、类以及枚举暴露给lua。

++++【Unity C#】<==>【Tolua#】<==>【Tolua(c)】

++++toLua特性:

--自动生成绑定代码文件,非反射调用;

--大量内建基础类型支持,如枚举,委托,事件,Type,数组,迭代器等;

--支持多种协同形式;

--支持所有unity内部类导出,支持委托类型导出;

--支持导出自定义,跳过某个空的基类,修改导出名称等;

--支持扩展函数自定义导出,比如DoTween;

--支持值类型Nullable导出,包括Nullable<Vector3>等;

--支持Lua中function转委托,可以区分需要不同委托的参数的重载函数;

--支持C# LuaFunction对象转委托,简化调用方式。支持无GC的多参数调用形式;

--支持重载函数自动排序,如:参数个数相同,object参数执行级最低,不会出现错误匹配情况;

--支持导出函数重命名,可以分离导出某个重载函数(可以导出被折叠掉的函数);

--支持使用编辑器类改写导出规则;

--支持this数组访问,索引为int可以通过[]访问,其他可使用.get_Item或者.this.get()访问数组成员;

--支持委托(事件)+-lua function。支持通过函数接口的Add和Remove委托操作;

--支持静态反射操作,形式同C#;

--支持peer表,可在lua端扩展导出的userdata;

--支持自定义struct压入和读取,做到无GC,并且结构成员无类型限制;

--支持preloading,可以通过require后绑定wrap文件;

--支持int64,unit64;

--大量的lua数学类型,如Quaternion, Vector3, Mathf等;

--包含第三方lua扩展,包括luasocket, struct, lpeg, utf8, pb等库;

--当lua出现异常,能够同时捕获C#端和lua端堆栈,便于调试;

--print信息,在编辑器点击日志,能自动打开对应lua文件;

--支持unity所有版本;

--支持Lua hook C#代码实现,一定程度上支持利用Lua代码修改C#端代码的bug;

++++tolua#集成主要两部分:1、运行时需要的代码包括一些手写和自动生成的绑定代码,2、编辑器相关代码,主要提供代码生成、编译lua文件等操作,具体就是Unity编辑器中提供的功能。

++1.2.1、【MenuItem(“Lua/Generate All”)】流程

++++ToLuaMenu.cs/GenLuaAll():GenLuaDelegates()、GenerateClassWraps()、GenLuaBinder():

--【GenLuaDelegate()】:生成委托绑定代码,它会从CustomSettings.customDelegateList里面取出所有自定义导出的委托列表,然后把CustomSettings.customTypeList里面的所有类型中的委托根据一定规则加入到list中,最后调用ToLuaExport.GenDelegates()方法来生成委托绑定的代码,生成的代码在DelegateFactory.cs文件中。

--【GenerateClassWraps()】:遍历所有需要导出的类,然后调用ToLuaExport.Generate()方法来生成类的绑定代码。

--【GenLuaBinder()】:生成向lua注册C#类的绑定代码,这个代码存放在LuaBinder.cs文件中,这部分代码中不包含BaseType(Array、Enum、Object、String等等)的注册。

//立钻哥哥:GenLuaAll(tolua-master-\Assets\ToLua\Editor\ToLuaMenu.cs)

using UnityEngine;

using UnityEditor;

using System;

[InitializeOnLoad]

public static class ToLuaMenu{

[MenuItem(“Lua/Generate All”, false, 5)]

    static void GenLuaAll(){

if(EditorApplication.isCompiling){

EditorUtility.DisplayDialog(“立钻哥哥警告”, “请等待编辑器完成编译再执行此功能”, “确定”);

return;

}

beAutoGen = true;

        GenLuaDelegates();

        AssetDatabase.Refresh();

        GenerateClassWraps();

        GenLuaBinder();

beAutoGen = false;

}

[MenuItem(“Lua/Gen Lua Delegates”, false, 2)]

    static void GenLuaDelegates(){

if(!beAutoGen && EditorApplication.isCompiling){

EditorUtility.DisplayDialog(“立钻哥哥警告”, ”请等待编辑器完成编译再执行此功能”, “确定”);

return;

}

        ToLuaExport.Clear();

        List<DelegateType> list = new List<DelegateType>();

list.AddRange(CustomSettings.customDelegateList);

        HashSet<Type> set = GetCustomTypeDelegates();

        foreach(Typein set){

if(null == list.Find((p)=>{  return p.type == t;  })){

list.Add(new DelegateType(t));

}

}

        ToLuaExport.GenDelegates(list.ToArray());

set.Clear();

        ToLuaExport.Clear();

        AssetDatabase.Refresh();

Debug.Log(“立钻哥哥:Create lua delegate over!”);

}

[MenuItem(“Lua/Gen Lua Wrap Files”, false, 1)]

    public static void GenerateClassWraps(){

if(!bAutoGen && EditorApplication.isCompiling){

EditorUtility.DisplayDialog(“立钻哥哥警告”, “请等待编辑器完成编译再执行此功能”, “确定”);

            return;

}

        if(!File.Exists(CustomSettings.saveDir)){

Directory.CreateDirectory(CustomSettings.saveDir);

}

allTypes.Clear();

        BindType[] typeList = CustomSettings.customTypeList;

        BindType[] list = GenBindTypes(typeList);

        ToLuaExport.allTypes.AddRange(baseType);

        for(int i = 0;  i < list.Length;  i++){

ToLuaExport.allTypes.Add(list[i].type);

}

        for(int i = 0;  i < list.Length;  i++){

ToLuaExport.Clear();

ToLuaExport.className = list[i].name;

ToLuaExport.type = list[i].type;

ToLuaExport.isStaticClass = list[i].IsStatic;

ToLuaExport.baseType = list[i].baseType;

ToLuaExport.wrapClassName = list[i].wrapName;

ToLuaExport.libClassName = list[i].libName;

ToLuaExport.extendList = list[i].extendList;

ToLuaExport.Generate(CustomSettings.saveDir);

}

Debug.Log(“立钻哥哥:Generic lua binding files over!”);

        ToLuaExport.allTypes.Clear();

allTypes.Clear();

        AssetDatabase.Refresh();

}

[MenuItem(“Lua/Gen LuaBinder File”, false, 4)]

    static void GenLuaBinder(){

if(!beAutoGen && EditorApplication.isCompiling){

EditorUtility.DisplayDialog(“立钻哥哥警告”, “请等待编辑器完成编译再执行此功能”, “确定”);

return;

}

allTypes.Clear();

        ToLuaTree<string> tree = InitTree();

        StringBuilder sb = new StringBuilder();

        List<DelegateType> dtList = new List<DelegateType>();

        List<DelegateType> list = new List<DelegateType>();

list.AddRange(CustomSetting.customDelegateType);

        HashSet<Type> set = GetCustomTypeDelegates();

        List<BindType> backupList = new List<BindType>();

backupList.AddRange(allTypes);

        ToLuaNode<string> root = tree.GetRoot();

        string libname = null;

        foreach(Typein set){

if(null == list.Find(p) => {  return p.type == t;  }){

DelegateType dt = new DelegateType(t);

AddSpaceNameToTree(tree, root, ToLuaExport.GetNameSpace(t, out libname));

list.Add(dt);

}

}

sb.AppendLineEx(“//立钻哥哥:this source code was auto-generated by tolua#, do not modify it”);

sb.AppendLineEx(“using System”);

sb.AppendLineEx(“using UnityEngine”);

sb.AppendLineEx(“using LuaInterface”);

sb.AppendLineEx();

sb.AppendLineEx(“public static class LuaBinder”);

sb.AppendLineEx(“{”);

sb.AppendLineEx(“\tpublic static void Bind(LuaState L)”);

sb.AppendLineEx(“\t{”);

sb.AppendLineEx(“\t\tfloat t = Time.realtimeSinceStartup;”);

sb.AppendLineEx(“\t\tL.BeginModule(null);”);

        GenRegisterInfo(null, sb, list, dtList);

        Action<ToLuaNode<string>> begin = (node)=>{

if(node.value == null){

return;

}

sb.AppendFormat(“\t\tL.BeginModule(\”{0}\”);\r\n”, node.value);

            string space = GetSpaceNameFromTree(node);

            GenRegisterInfo(space, sb, list, dtList);

};

        Action<ToLuaNode<string>> end = (node) =>{

if(node.value != null){

sb.AppendLineEx(“\t\tL.EndModule();”);

}

};

tree.DepthFirstTraversal(begin, end, tree.GetRoot());

sb.AppendLineEx(“\t\tL.EndModule()”);

        if(CustomSettings.dynamicList.Count > 0){

sb.AppendLineEx(“\t\tL.BeginPreLoad();”);

for(int i = 0;  i < CustomSettings.dynamicList.Count;  i++){

Type t1 = CustomSettings.dynamicList[i];

BindType bt = backupList.Find((p)=>{  return p.type == t1;  });

                if(bt != null){

sb.AppendFormat(“\t\tL.AppPreLoad(\”{0}\”, LuaOpen_{1}, typeof({0}));\r\n”, bt.name, bt.wrapName);

}

}

sb.AppendLineEx(“\t\tL.EndPreLoad();”);

}

sb.AppendLineEx(“\t\tDebugger.Log(\”立钻哥哥:Register lua type cost time: {0}\”, Time.realtimeSinceStartup - t);”);

sb.AppendLineEx(“\t}”);

        for(int i = 0; i < dtList.Count;  i++){

ToLuaExport.GenEventFunction(dtList[i].type, sb);

}

        if(CustomSettings.dynamicList.Count > 0){

for(int i = 0;  i < CustomSettings.dynamicList.Count;  i++){

Type t = CustomSettings.dynamicList[i];

BindType bt = backupList.Find((p)=>{  return p.type == t;  });

if(bt != null){

GenPreLoadFunction(bt, sb);

}

}

}

sb.AppendLineEx(“}\r\n”);

allTypes.Clear();

        string file = CustomSettings.saveDir + “LuaBinder.cs”;

        using(StreamWriter textWriter = new StreamWriter(file, false, Encoding.UTF8)){

textWriter.Write(sb.ToString());

textWriter.Flush();

textWriter.Close();

}

        AssetDatabase.Refresh();

Debugger.Log(“立钻哥哥:Generate LuaBinder over!”);

}

}    //立钻哥哥:public static class ToLuaMenu{}

++1.2.2、【ToLuaExport.cs/Generate()】流程

++++ToLuaExport.Generate()基本流程:如果这个类是枚举类型,那么会调用枚举导出的接口,如果这个类型是一个普通的类,那么就会调用相应的流程将代码导出。

//立钻哥哥:Generate()流程(tolua-master-\Assets\ToLua\Editor\ToLuaExport.cs)

using UnityEngine;

using System;

public static class ToLuaExport{

    public static void Generate(string dir){

#if !EXPORT_INTERFACE

Type iterType = typeof(System.Collections.IEnumerator);

if(type.IsInterface && type != iterType){

return;

}

#endif

Debugger.Log(“立钻哥哥:Begin Generate lua Wrap for class {0}”, className);

sb = new StringBuilder();

usingList.Add(“System”);

if(wrapClassName == “”){

wrapClassName = className;

}

        if(type.IsEnum){

BeginCodeGen();

GenEnum();

EndCodeGen(dir);

return;

}

        InitMethods();

        InitPropertyList();

        InitCtorList();

        BeginCodeGen();

        GenRegisterFunction();

        GenConstructFunction();

        GenItemPropertyFunction();

        GenFunctions();

//GenToStringFunction();

        GenIndexFunc();

        GenNewIndexFunc();

        GenOutFunction();

        GenEventFunctions();

        EndCodeGen(dir);

}

    static void BeginCodeGen(){

sb.AppendFormat(“public class {0}Wrap\r\n”, wrapClassName);

sb.AppendLineEx(“{”);

}

    static void GenEnum(){

fields = type.GetFields(BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static);

List<FieldInfo> list = new List<FieldInfo>(fields);

for(int i = list.Count - 1;  i > 0;  i--){

if(IsObsolete(list[i])){

list.RemoveAt(i);

}

}

fields = list.ToArray();

....  //立钻哥哥:此处省略一万字

}

    static void EndCodeGen(string dir){

sb.AppendLineEx(“}\r\n”);

SaveFile(dir + wrapClassName + “Wrap.cs”);

}

    static void InitMethods(){

bool flag = false;

if(baseType != null || isStaticClass){

binding |= BindingFlags.DeclaredOnly;

flag = true;

}

        List<_MethodBase> list = new List<_MethodBase>();

        MethodInfo[] infos = type.GetMethods(BindingFlags.Instance | binding);

        for(int i = 0;  i < infos.Length;  i++){

list.Add(new _MethodBase(infos[i]));

}

....    //立钻哥哥:此处省略一万字

}

    static void InitPropertyList(){

props = type.GetProperties(BindingFlags.GetProperty | BindingFlags.SetProperty | BindingFlags.Instance | binding);

propList.AddRange(type.GetProperties(BindingFlags.GetProperty | BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase));

fields = type.GetFields(BindingFlags.GetField | BindingFlags.SetField | BindingFlags.Instance | binding);

events = type.GetEvents(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);

eventList.AddRange(type.GetEvents(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public));

List<FieldInfo> fieldList = new List<FieldInfo>();

fieldList.AddRange(fields);

....    //立钻哥哥:此处省略一万字

}

    static void InitCtorList(){

if(isStaticClass || type.IsAbstract || typeof(MonoBehaviour).IsAssignableFrom(type)){

return;

}

        ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Instance | binding);

....    //立钻哥哥:此处省略一万字

}

    static void BeginCodeGen(){

sb.AppendFormat(“public class {0}Wrap\r\n”, wrapClassName);

sb.AppendLineEx(“{”);

}

    static void GenRegisterFunction(){

sb.AppendLineEx(“\tpublic static void Register(LuaState L)”);

sb.AppendLineEx(“\t{”);

....

GenRegisterFuncItems();

GenRegisterOpItems();

GenRegisterVariables();

GenRegisterEventTypes();    //立钻哥哥:注册事件类型

....

}

    static void GenConstructFunction(){

....

}

//立钻哥哥:this[] 非静态函数

    static void GenItemPropertyFunction(){

....

}

    static void GenFunctions(){

HashSet<string> set = new HashSet<string>();

....

}

    static void GenIndexFunc(){

....

}

    static void GenNewIndexFunc(){

....

}

    static void GenOutFunction(){

if(isStaticClass || CustomSettings.outList.IndexOf(type) < 0){

return;

}

sb.AppendLineEx(“\r\n\r[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]”);

sb.AppendLineEx(“\tstatic int get_out(IntPtr L)”);

sb.AppendLineEx(“\t{”);

sb.AppendFormat(“\t\tToLua.PushOut<{0}>(L, new LuaOut<{0}>());\r\n”, className);

sb.AppendLineEx(“\t\treturn 1;”);

sb.AppendLineEx(“\t}”);

}

    static void GenEventFunctions(){

foreach(Type t in eventSet){

GetEventFunction(t, sb);

}

}

    static void EndCodeGen(string dir){

sb.AppendLineEx(“}\r\n”);

SaveFile(dir + wrapClassName + “Wrap.cs”);

}

}    //立钻哥哥:public static class ToLuaExport{}

++1.2.3、toLua#核心运行时

++++toLua#的运行代码包含【Source/Generate/】下的绑定代码,以及【ToLua/BaseType/】代码,以及【ToLua/Core/】下的核心代码。

++++【LuaAttribute.cs】:在toLua#生成绑定代码时做一些标示使用。

++++【LuaBaseRef.cs】:Lua中对象对应C#中对象的一个基类,主要作用是用一个reference指向lua里面的对象,引用计数判断两个对象是否相等。(比如:LuaFunction里面的reference是指向lua里面的一个闭包的,而LuaTable的reference是指向lua中的一个table的。)

++++【LuaDll.cs】:这个类的主要作用就是实现了C#调用原生代码的功能。

++++【LuaState.cs】:对真正的lus_State的封装,包括初始化lua路径,加载相应的lua文件,注册生成的绑定代码以及各种辅助函数。

++++【ObjectTranslator.cs】:给lua中对C#对象的交互提供了基础,简单来说就是C#中的对象在传给lua时并不是直接把对象暴露给lua,而是在这个ObjectTranslator里面注册并返回一个索引(句柄),并把这个索引包装成一个userdata传递给lua,并且设置元表。(在lua需要通过传到lua里面的对象调用C#的方法时,它会调用ToLua.CheckObject或者ToLua.ToObject从ObjectTranslator获取真正的C#对象。)

++1.2.4、关于反射

++++toLua#不支持动态反射。(动态反射对于重载函数有参数匹配问题,函数排序问题,ref, out参数问题等等。)

++++toLua#提供的替换方法是:

--1、preloading,把未来可能需要的类型添加到导出列表customTypeList,同时也添加到dynamicList列表中,这样导出后该类型不会随binder注册到lua中,可以通过require “namespace.classname”动态注册到lua中,对于非枚举类型toLua#系统也可以在第一次push该类型时动态载入,当然也可在过场动画、资源下载、登录、场景加载或者某个的函数中require这个类型。

--2、静态反射。通过静态反射支持精确的函数参数匹配和类型检查。不会存在重载函数参数混乱匹配错误问题。(注意iOS必须配置好link.xml)

++++link.xml(tolua-master-\Assets\link.xml)

//立钻哥哥:link.xml(iOS必须配置好link.xml)

<?xml version=”1.0” encoding=”utf-8”?>

<linker>

<assembly fullname=”mscorlib”>

<namespace fullname=”System.Collections.Generic” perserve=”all”/>

</assembly>

</linker>

++1.2.5、toLua简单示例:HelloWorld(最小的toLua#环境)

//立钻哥哥:HelloWorld示例(\Assets\ToLua\Examples\01_HelloWorld\HelloWorld.cs)

using UnityEngine;

using LuaInterface;

using System;

public class HelloWorld : MonoBehaviour{

    void Awake(){

LuaState myLua = new LuaState();

myLua.Start();

string myHello = @”print(‘立钻哥哥:hello tolua#’)”;

myLua.DoString(myHello, “HelloWorld.cs”);

myLua.CheckTop();

myLua.Dispose();

myLua = null;

}

}    //立钻哥哥:public class HelloWorld : MonoBehaviour{}

++++立钻哥哥:该简单示例展示了最小的toLua#环境。

++++【LuaState】封装了对lua主要数据结构lua_State指针的各种堆栈操作。

++++一般对于客户端,推荐只创建一个LuaState对象。(如果要使用多State需要在Unity中设置全局宏MULTI_STATE)

++++【LuaState.Start()】:需要在toLua代码加载到内存后调用。(如果使用assetbundle加载lua文件,调用Start()之前assetbundle必须加载好)

++++【LuaState.DoString()】:执行一段lua代码。(比较少用这种方式加载代码,无法避免代码重复加载覆盖等情况,需调用者自己保证。第二个参数用于调试信息,或者error消息(用于提示出错代码所在文件名称。))

++++【LuaState.CheckTop()】:检查堆栈是否平衡,一般放在update中,C#中任何使用lua堆栈操作,都需要调用者自己平衡堆栈,当CheckTop出现警告时其实早已经离开了堆栈操作范围,这是需自行review代码。

++++【LuaState.Dispose()】:释放LuaState以及其资源。

++1.2.6、toLua简单示例:ScriptsFromFile(DoFile和Require的区别)

//立钻哥哥:ScriptsFromFile示例(\ToLua\Examples\02_ScriptsFromFile\ScriptsFromFile.cs)

using UnityEngine;

using System.Collections;

using LuaInterface;

using System;

using System.IO;

//立钻哥哥:展示searchpath使用,require与dofile区别

public class ScriptsFromFile : MonoBehaviour{

    LuaState myLua = null;

    private string strLog = “”;

    void Start(){

#if UNITY_5 || UNITY_2017 || UNITY_2018

Application.logMessageReceived += Log;

#else

Application.RegisterLogCallback(Log);

#endif

myLua = new LuaState();

myLua.Start();

//立钻哥哥:如果移动了ToLua目录,手动修复配置

string fullPath = Application.dataPath + “\\ToLua/Examples/Yanlz_ScriptsFromFile”;

myLua.AddSearchPath(fullPath);

}

    void Log(string msg, string stackTrace, LogType type){

strLog += msg;

strLog += “\r\n”;

}

    void OnGUI(){

GUI.Label(new Rect(100, Screen.height/2 - 100, 600, 400), strLog);

if(GUI.Button(new Rect(50, 50, 120, 45), “DoFile”)){

strLog = “”;

myLua.DoFile(“ScriptsFromFile.lua”);

}else if(GUI.Button(new Rect(50, 150, 120, 45), “Require”)){

strLog = “”;

myLua.Require(“ScriptsFromFile”);

}

myLua.Collect();

myLua.CheckTop();

}

    void OnApplicationQuit(){

myLua.Dispose();

myLua = null;

#if UNITY_5 || UNITY_2017 || UNITY_2018

Application.logMessageReceived -= Log;

#else

Application.RegisterLogCallback(null);

#endif

}

}    //立钻哥哥:public class ScriptsFromFile : MonoBehaviour{}

++++立钻哥哥:该示例展示了dofile和require区别。

++++toLua#中DoFile函数,和Lua保持一致行为,能多次执行一个文件。

++++toLua#中加入了新的Require函数,无论C#和lua谁先require一个lua文件,都能保证加载唯一性。

++++【LuaState.AddSearchPath()】:增加搜索目录,这样DoFile和Require函数可以只用文件名,无需写全路径。

++++【LuaState.DoFile()】:加载一个lua文件,注意dofile需要扩展名,可反复执行,后面的变量会覆盖之前的DoFile加载的变量。

++++【LuaState.Require()】:同lua require(modname)操作,加载指定模块并且把结果写入到package.loaded中,如果modname存在,则直接返回package.loaded[modname]。

++++【LuaState.Collect()】:垃圾回收,对于被自动gc的LuaFunction,LuaTable,以及委托减掉的LuaFunction,延迟删除的Object类等需要延迟处理的回收,都在这里自动执行。

++1.2.7、toLua#示例:CallLuaFunction(调用Lua函数)

//立钻哥哥:CallLuaFunction示例(\ToLua\Examples\03_CallLuaFunction\CallLuaFunction.cs)

using UnityEngine;

using System.Collections;

using LuaInterface;

using System;

public class CallLuaFunction : MonoBehaviour{

    private string script =

@”

function luaFunc(num)

return num + 1;

end

Test = {};

Test.luaFunc = luaFunc;

”;

    LuaFunction luaFunc = null;

    LuaState myLua = null;

    string myTips = null;

    void Start(){

#if UNITY_5 || UNITY_2017 || UNITY_2018

Application.logMessageReceived += ShowTips;

#else

Application.RegisterLogCallback(ShowTips);

#endif

new LuaResLoader();

myLua = new LuaState();

myLua.Start();

DelegateFactory.Init();

myLua.DoString(script);

//立钻哥哥:Get the fucntion object

luaFunc = myLua.GetFunction(“test.luaFunc”);

if(luaFunc != null){

int myNum = luaFunc.Invoke<int, int>(123456);

Debugger.Log(“立钻哥哥:generic call return: {0}”, myNum);

myNum = CallFunc();

Debugger.Log(“立钻哥哥:expansion call return: {0}”, myNum);

Func<int, int> myFunc = luaFunc.ToDelegate<Func<int, int>>();

myNum = myFunc(123456);

Debugger.Log(“立钻哥哥:Delegate call return: {0}”, myNum);

myNum = myLua.Invoke<int, int>(“test.luaFunc”, 123456, true);

Debugger.Log(“立钻哥哥:luastate call return: {0}”, myNum);

}

myLua.CheckTop();

}

    void ShowTips(string msg, string stackTrace, LogType type){

myTips += msg;

myTips += “\r\n”;

}

#if ! TEST_GC

void OnGUI(){

GUI.Label(new Rect(Screen.width/2 - 200, Screen.height/2 - 150, 400, 300), myTips);

}

#endif

    void OnDestroy(){

if(luaFunc != null){

luaFunc.Dispose();

luaFunc = null;

}

myLua.Dispose();

myLua = null;

#if UNITY_5 || UNITY_2017 || UNITY_2018

Application.logMessageReceived -= ShowTips;

#else

Application.RegisterLogCallback(null);

#endif

}

    int CallFunc(){

luaFunc.BeginPCall();

luaFunc.Push(123456);

luaFunc.PCall();

int myNum = (int)luaFunc.CheckNumber();

luaFunc.EndPCall();

return myNum;

}

}    //立钻哥哥:public class CallLuaFunction : MonoBehaviour{}

++++立钻哥哥:toLua#简化了lua函数的操作,通过LuaFunction封装(并缓存)一个lua函数,并提供各种操作,建议频繁调用函数使用无GC方式。

++++【LuaState.GetLuaFunction()】:获取并缓存一个lua函数,此函数支持串式操作,如“test.luaFunc”代表test表中的luaFunc函数。

++++【LuaState.Invoke()】:临时调用一个lua function并返回一个值,这个操作并不缓存lua function,适合频率非常低的函数调用。

++++【LuaFunction.Call()】:不需要返回值的函数调用操作。

++++【LuaFunction.Invoke()】:有一个返回值的函数调用操作。

++++【LuaFunction.BeginPCall()】:开始函数调用。

++++【LuaFunction.Push()】:压入函数调用需要的参数,通过众多的重载函数来解决参数转换gc问题。

++++【LuaFunction.PCall()】:调用lua函数。

++++【LuaFunction.CheckNumber()】:提取函数返回值,并检查返回值为lua number类型。

++++【LuaFunction.EndPCall()】:结束lua函数调用,清除函数调用造成的堆栈变化。

++++【LuaFunction.Dispose()】释放LuaFunction,递减引用计数,如果引用计数为0,则从_R表删除该函数。

++++立钻哥哥:无论Call还是PCall只相当于lua中的函数“.”调用。(self:call(...) == self.call(self, ...),C#中需要按self.call(self, ...)方式调用,即必须主动传入第一个参数self)

++1.2.8、toLua#示例:AccessingLuaVariables(访问lua变量)

//立钻哥哥:AccessingLuaVariables示例(\04_AccessingLuaVariables\AccessingLuaVariables.cs)

using UnityEngine;

using System.Collections.Generic;

using LuaInterface;

public class AccessingLuaVariables : MonoBehaviour{

    private string myScript = @”

print(‘立钻哥哥:Objs2Spawn is: ’ .. Objs2Spawn)

var2read = 42

varTable = { 1, 2, 3, 4, 5 }

varTable.default = 1

varTable.map = {}

varTable.map.name = ‘map’

meta = { name = ‘meta’ }

setmetatable(varTable, meta)

function TestFunc(strs)

print(‘立钻哥哥:func by variable’);

end

”;

    void Start(){

#if UNITY_5 || UNITY_2017 || UNITY_2018

Application.logMessageReveived += MyShowTips;

#else

Application.RegisterLogCallback(MyShowTips);

#endif

new LuaResLoader();

LuaState myLua = new LuaState();

myLua.Start();

myLua[“Objs2Spawn”] = 5;

myLua.DoString(myScript);

//立钻哥哥:通过LuaState访问

Debugger.Log(“立钻哥哥:Read var from lua:{0}”, lua[“var2read”]);

Debugger.Log(“立钻哥哥:Read table var from lua:{0}”, lua[“varTable.default”]);

LuaFunction myFunc = myLua[“TestFunc”] as LuaFunction;

myFunc.Call();

myFunc.Dispose();

//立钻哥哥:cache成LuaTable进行访问

LuaTable myTable = myLua.GetTable(“varTable”);

Debugger.Log(“立钻哥哥:Read varTable from lua, default:{0} name:{1}”, myTable[”default”], myTable[“map.name”]);

myTable[“map.name”] = “new”;    //立钻哥哥:table字符串只能是key

Debugger.Log(“立钻哥哥:Modify varTable name:{0}”, table[“map.name”]);

myTable.AddTable(“newmap”);

LuaTable myTable1 = (LuaTable)myTable[“newmap”];

myTable1[“name”] = “table1”;

Debugger.Log(“立钻哥哥:varTable.newmap name:{0}”, myTable1[“name”]);

myTable1.Dispose();

myTable1 = myTable.GetMetaTable();

if(myTable1 != null){

Debugger.Log(“立钻哥哥:varTable metatable name: {0}”, myTable1[“name”]);

}

        object[] myList = myTable.ToArray();

        for(int i = 0;  i < myList.Length;  i++){

Debugger.Log(“立钻哥哥:varTable[{0}], is {1}”, i, myList[i]);

}

myTable.Dispose();

myLua.CheckTop();

myLua.Dispose();

}

    private void OnApplicationQuit(){

#if UNITY_5 || UNITY_2017 || UNITY_2018

Application.logMessageReceived -= MyShowTips;

#else

Application.RegisterLogCallback(null);

#endif

}

    string myTips = null;

    void MyShowTips(string msg, string stackTrace, LogType type){

myTips += msg;

myTips += “\r\n”;

}

    void OnGUI(){

GUI.Label(new Rect(Screen.width/2 - 300, Screen.height/2 - 200, 600, 400), tips);

}

}    //立钻哥哥:public class AccessingLuaVariables : MonoBehaviour{}

++++立钻哥哥:该示例展示了如何访问lua中的变量,table的操作。

++++【luaState[“Objs2Spawn”]】:LuaState通过重载this操作符,访问lua_G表中的变量Objs2Spawn。

++++【LuaState.GetTable()】:从lua中获取一个lua table,可以串式访问,如:lua.GetTable(“varTable.map.name”)等同:varTable->map->name。

++++LuaTable支持this操作符,但此this不支持串式访问。比如table[“map.name”]中,“map.name”只是一个key,不是table->map->name。

++++【LuaTable.GetMetaTable()】:可以获取当前table的metatable。

++++【LuaTable.ToArray()】:获取数组表中的所有对象存入到object[]表中。

++++【LuaTable.AddTable(name)】:在当前的table表中添加一个名字为name的表。

++++【LuaTable.GetTable(key)】:获取t[key]值到C#,类似于lua_gettable。

++++【LuaTable.SetTable(key, value)】:等价于t[k]=v的操作,类似于lua_settable。

++++【LuaTable.RawGet(key)】:获取t[key]值到C#,类似于lua_rawget。

++++【LuaTable.RawSet(key, value)】:等价于t[k]=v的操作,类似于lua_rawset。

++1.2.9、toLua#示例:TestCoroutine(使用lua协同)

//立钻哥哥:TestCoroutine示例(\Assets\ToLua\Examples\05_LuaCoroutine\TestCoroutine.cs)

using UnityEngine;

using System;

using System.Collections;

using LuaInterface;

public class TestCoroutine : MonoBehaviour{

    public TextAsset luaFile = null;

    private LuaState myLua = null;

    private LuaLooper myLooper = null;

    void Awake(){

#if UNITY_5 || UNITY_2017 || UNITY_2018

Application.logMessageReceived += MyShowTips;

#else

Application.RegisterLogCallback(MyShowTips);

#endif

new LuaResLoader();

myLua = new LuaState();

myLua.Start();

LuaBinder.Bind(myLua);

DelegateFactory.Init();

myLooper = gameObject.AddComponent<LuaLooper>();

myLooper.luaState = myLua;

myLua.DoString(luaFile.text, “TestLuaCoroutine.lua”);

LuaFunction myF = myLua.GetFunction(“TestMyCoroutine”);

myF.Call();

myF.Dispose();

myF = null;

}

    void OnApplicationQuit(){

myLooper.Destroy();

myLua.Dispose();

myLua = null;

#if UNITY_% || UNITY_2017 || UNITY_2018

Applicaiton.logMessageReceived -= MyShowTips;

#else

Application.RegisterLogCallback(null);

#endif

}

    string myTips = null;

    void MyShowTips(string msg, string stackTrace, LogType type){

myTips += msg;

myTips += “\r\n”;

}

    void OnGUI(){

GUI.Label(new Rect(Screen.width/2 - 300, Screen.height/2 - 200, 600, 400), myTips);

if(GUI.Button(new Rect(50, 50, 120, 45), “Start Counter”)){

myTips = null;

LuaFunction myFunc = myLua.GetFunction(“StartDelay”);

myFunc.Call();

myFunc.Dispose();

}else if(GUI.Button(new Rect(50, 150, 120, 45), “Stop Counter”)){

LuaFunction myFunc = myLua.GetFunction(“StopDelay”);

myFunc.Call();

myFunc.Dispose();

}else if(GUI.Button(new Rect(50, 250, 120, 45), “立钻哥哥:GC”)){

myLua.DoString(“collectgarbage(‘collect’)”, “TestCoroutine.cs”);

Resources.UnloadUnusedAssets();

}

}

}    //立钻哥哥:public class TestCoroutine : MonoBehaviour{}

//立钻哥哥:TestLuaCoroutine.lua(使用lua协同,lua代码部分)

//(tolua-master-\Assets\ToLua\Examples\Resources\Lua\TestLuaCoroutine.lua.bytes)

//fib函数负责计算一个斐波那契n

function fib(n)

    local a, b = 0, 1

    while n > 0 do

a, b = b, a + b

n = n - 1

    end

    return a

end

function CoFunc()

print(‘立钻哥哥:Coroutine started!’);

    for i = 0, 10, 1 do

print(fib(i))

coroutine.wait(0.1)

    end

print(“立钻哥哥:current framewCount: ” .. Time.frameCount)

    coroutine.step()

print(“立钻哥哥:yield frameCount: ” .. Time.frameCount);

    local myWww = UnityEngine.WWW(“http://www.baidu.com”);

coroutine.www(myWww);

    local s = tolua.tolstring(myWww.bytes)

print(s:sub(1, 128));

print(‘立钻哥哥:Coroutine ended’)

end

function TestMyCoroutine()

coroutine.start(CoFunc)

end

local coDelay = nil

function Delay()

    local c = 1

    while true do

coroutine.wait(1)

print(“立钻哥哥:Count:” .. c)

c = c + 1

    end

end

function StartDelay()

coDelay = coroutine.start(Delay)

end

function StopDelay()

coroutine.stop(coDelay)

end

++++立钻哥哥:该示例展示了如何使用lua协同。(必须启动LuaLooper驱动协同,这里将一个lua的半双工协同转换为类似unity的全双工协同。)

++++【coroutine.start()】:启动一个lua协同。

++++【coroutine.wait()】:协同中等待一段时间,单位:秒。

++++【coroutine.step()】:协同中等待一帧。

++++【coroutine.www()】:等待一个WWW完成。

++++【tolua.tolstring()】:转换byte数组为lua字符串缓冲。

++++【coroutine.stop()】:停止一个协同。

###1.3、xLua热更新框架

++1.3、xLua热更新框架

++++xLua:https://github.com/Tencent/xLua

++++xLua为Unity、.Net、Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便和C#相互调用。

++1.3.1、xLua的突破

++++xLua在功能、性能、易用性都有不少突破,最具代表性的是:

--1、可以运行时把C#实现(方法,操作符,属性,事件等等)替换成Lua实现;

--2、出色的GC优化,自定义struct,枚举在Lua和C#间传递无C# gc alloc;

--3、编辑器下无需生成代码,开发更轻量;

++++【总体特性】

--1、Lua虚拟机支持:Lua5.3、Luajit2.1;

--2、Unity3D版本支持:各版本支持;

--3、平台支持:windows64/32、android、ios 64/32/bitcode、osx、uwp、webgl;

--4、互访技术:生成适配代码、反射;

--5、易用性:解压即可用;开发期无需生成代码;生成代码和反射间可无缝切换;更简单的无GC api;菜单简单易懂;配置可以多份,按模块划分,也可以直接在目标类型上打Attribute标签;自动生成link.xml防止代码剪裁;Plugins部分采用cmake编译,更简单;核心代码不依赖生成代码,可以随时删除生成目录;

--6、性能:lazyload技术,避免用不上的类型的开销;lua函数映射到C# delegate,lua table映射到interface,可实现接口层面无C# gc alloc开销;所有基本值类型,所有枚举,字段都是值类型的struct,在Lua和C#间传递无C# gc alloc;LuaTable,LuaFunction提供无gc访问接口;通过代码生成期的静态分析,生成最优代码;支持C#和Lua间指针传递;自动解除已经Destroy的UnityEngine.Object的引用;

--7、扩展性:不用改代码就可以加入Lua第三方扩展;生成引擎供接口做二次开发;

++++【支持C#实现打补丁】:构造函数、析构函数、成员函数、静态函数、泛化函数、操作符重载、成员属性;静态属性;事件;

++++【Lua代码加载】:加载字符串;支持加载后立即执行;支持加载后返回一个delegate或者LuaFunction,调用delegate或者LuaFunction后可传脚本参数;Resources目录的文件;直接require;自定义loader;Lua里头require时触发;require参数透传给loader,loader读取Lua代码返回;Lua原有的方式;Lua原有的方式都保留;

++++【Lua调用C#】

--1、创建C#对象:C#静态属性,字段;C#静态方法;C#成员属性,字段;C#成员方法;C#继承;子类对象可以直接调用父类的方法,访问父类属性;子类模块可以直接调用父类的静态方法,静态属性;

--2、扩展方法(Extension methods):就像普通成员方法一样使用;

--3、参数的输入输出属性(out, ref):out对应一个lua返回值;ref对应一个lua参数以及一个lua返回值;

--4、函数重载:支持重载;由于lua数据类型远比C#要少,会出现无法判断的情况,可通过扩展方法来调用;操作符重载;

--5、支持的操作符:+,-,*,/,==,<,<=,%,[];其他操作符可以借助扩展方法调用;

--6、参数默认值:C#参数有默认值,在Lua可以不传;

--7、可变参数:在对应可变参数部分,直接输入一个参数即可,不需要把这些参数扩到一个数组里头;

--8、泛化方法调用:静态方法可以自行封装使用;成员函数可通过扩展方法封装使用;

--9、枚举类型:数字或字符串到枚举的转换;

--10、delegate:调用一个C# delegate;+操作符;-操作符;把一个lua函数作为一个c# delegate传递给C#;

--11、evnet:增加事件回调;移除事件回调;

--12、64位整数:传递无gc而且无精度损失;lua53下使用原生64位支持;可以和number运算;以java的方式支持无符号64位整数;

--13、table的自动转换到C#复杂类型;

--14、obj.complexField = { a=1, b = { c = 1}},obj是一个C#对象,complexField是两层嵌套的struct或者class;

--15、typeof:对应C#的typeof操作符,返回Type对象;

--16、lua侧直接clone;

--17、decimal:传递无gc而且无精度损失;

++++【C#调用Lua】:调用lua函数;以delegate方式调用Lua函数;以LuaFunction调用Lua函数;访问Lua的table;LuaTable的泛化Get/Set接口,调用无gc,可指明Key,Value的类型;用标注了CSharpCallLua的interface访问;值拷贝到struct,class;

++++【Lua虚拟机】:虚拟机gc参数读取及设置;

++++【工具链】:Lua Profiler;可根据函数调用总时长,平均每次调用时长,调用次数排序;显示lua函数名及其所在文件的名字及行号;如果C#函数,会显示这个C#函数;支持真机调试;

++1.3.2、xLua常见问题(立钻哥哥:初学者FAQ)

++++1、xLua发布包怎么用:xLua目前已zip包形式发布,在工程目录下解压即可。

++++2、xLua可以放别的目录吗?

--可以,但生成代码目录需要配置一下(默认放Assets/XLua/Gen目录);

--更改目录要注意的是:生成代码和xLua核心代码必须在同一程序集。如果要用热补丁特性,xLua核心代码必须在Assembly-CSharp程序集;

++++3、lua源码只能以txt后缀?

--什么后缀都可以。

--如果想以TextAsset打包到安装包(比如放到Resources目录),Unity不认lua后缀,这是Unity的规则;

--如果不打包到安装包,就没有后缀的限制:比如自行下载到某个目录(这也是热更的正确姿势),然后通过CustomLoader或者设置package.path去读这个目录;

--为啥xLua本身带的lua源码(包括示例)为什么都是txt结尾呢?因为xLua本身就一个库,不含下载功能,也不方便运行时去某个地方下载代码,通过TextAsset是较简单的方式;

++++4、编辑器(或非il2cpp的android)下运行正常,ios下运行调用某函数报“attempt to call a nil value”:

--il2cpp默认会对诸如引擎、C#系统api,第三方dll等等进行代码裁剪。(简单来说就是这些地方的函数如果C#代码没访问到的就不编译到最终发布包。)

--解决办法:增加引用(比如配置到LuaCallCSharp,或者自己C#代码增加那函数的访问),或者通过link.xml配置(当配置了ReflectionUse后,xlua会自动帮我们配置到link.xml)告诉il2cpp别剪裁某类型;

++++5、Plugins源码在哪里可以找到,怎么使用?

--Plugins源码位于xLua_Project_Root/build下。

--源码编译依赖cmake,按照cmake后执行make_xxxx_yyyy.zz即可。(xxxx代表平台,比如ios,android等)(yyyy是要集成的虚拟机,有lua53和luajit)(zz是后缀,window下是bat,其他平台是sh)

--windows编译依赖Visual Studio 2015;

--android编译在linux下执行,依赖NDK,并且需要把脚本中ANDROID_NDK指向NDK的安装目录。

--ios和osx需要在mac下编译。

++++6、报类似“xlua.access, no field _Hitfix0_Update”的错误怎么解决?

--按Hotfix操作指南一步步操作。

--https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/hotfix.md

--使用方式:

----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!”)

--约束:不支持静态构造函数。(目前只支持Assets下代码的热补丁,不支持引擎,C#系统库的热补丁。)

++++7、报“please install the Tools”:没有把Tools安装到Assets平级目录,安装包,或者master下都能找到这个目录。

++++8、报“This delegate/interface must add to CSharpCallLua: XXX”异常怎么解决?

--在编辑器下xLua不生成代码都可以运行,出现这种提示,要么是该类型没加CSharpCallLua,要么是加之前生成过代码,没重新执行生成。

--解决方法,确认XXX(类型)加上CSharpCallLua后,清除代码后运行。

--如果编辑器下没有问题,发布到手机报这错,表示你发布前没有生成代码(执行“XLua/Generate Code”)。

++++9、Unity5.5以上执行“XLua/Hotfix Inject In Editor”菜单会提示“WARNING: The runtime version supported by this application is unavailable.”

--这是因为注入工具是用.net3.5编译,而Unity5.5中MonoBleedingEdge的mono环境并没3.5支持导致的,不过一般而言都向下兼容,目前为止也没发现该warning带来什么问题。

--可能有人发现定义INJECT_WITHOUT_TOOL用内嵌模式会没有该warning,但问题是这模式是调试问题用的,不建议使用,因为可能会有一些库冲突问题。

++++10、hotfix下怎么触发一个event。

--首先通过xlua.private_accessible开启私有成员访问。

--跟着通过对象的“&事件名”字段调用delegate。(例如:self[‘&MyEvent’](),其中MyEvent是事件名。)

++++11、怎么对Unity Coroutine的实现函数打补丁?

--见Hotfix操作指南。

--https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/hotfix.md

--xLua可以用lua函数替换C#的构造函数,函数,属性,事件的替换。Lua实现都是函数,比如属性对于一个getter函数和一个setter函数,事件对应一个add函数和一个remove函数。

--Unity协程:通过util.cs_generator可以用一个function模拟一个IEnumerator,在里头用coroutine.yield,就类似C#里头的yield return。

++++12、支持NGUI(后者UGUI/DOTween等)么?

--支持,xLua最主要的特性是让原来用C#写的地方可以换成用lua写,C#能用的插件,基本都能用。

++++13、如果需要调试,CustomLoader的filepath参数该如何处理?

--lua里头调用require ‘a.b’时,CustomLoader会被调用,并传入字符串“a.b”,需要理解这字符串,(从文件/内存/网络等)加载好lua文件,返回两个东西,第一个是调试器可以理解的路径,比如:a/b.lua,这个通过设置ref类型的filepath参数返回,第二个是UTF8格式的源码的字节流(byte[]),通过返回值返回。

++++14、什么是生成代码?

--xLua支持的lua和C#间交互技术之一,这种技术通过生成两者间的适配代码来实现交互,性能较好,是推荐的方式。

--另一种交互技术是反射,这种方式对安装包的影响更少,可以在性能要求不高或者对安装包大小很敏感的场景下使用。

++++15、改了接口后,之前生成的代码出现错误怎么办?

--清除掉生成代码(执行“Clear Generated Code”菜单,如果重启过,会找不到这个菜单,这是可以手动删除整个生成代码目录),等编译完成后重新生成。

++++16、应该什么时候生成代码?

--开发期不建议生成代码,可以避免很多由于不一致导致的编译失败,以及生成代码本身的编译等待。

--build手机版本前必须执行生成代码,建议做成自动化的。

--做性能调优,性能测试前必须执行生成代码,因为生成和不生成性能的区别还是很大的。

++++17、CS命名空间下有所有C# API是不是很占内存?

--由于用了lazyload,这个“有”只是个虚拟的概念,比如UnityEngine.GameObject,是访问第一次CS.UnityEngine.GameObject或者第一个实例往lua传送才加载该类型方法,属性等。

++++18、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即可。

++++19、值类型传递会有gc alloc么?

--如果使用的是delegate调用lua函数,或者用LuaTable、LuaFunction的无gc接口,或者数组的话,已下值类型都没有gc的:

----1、所有的基本值类型(所有整数,所有浮点数,decimal);

----2、所有的枚举类型;

----3、字段只包含值类型的struct,可嵌套其它只包含值类型struct;

----立钻哥哥:其中2、3需要把该类型加到GCOptimize。

++++20、反射在ios下可用吗?

--ios下的限制有两个:1、没有jit;2、代码剪裁(stripping);

--对于C#通过delegate或者interface调用lua,如果不生成代码是反射的emit,这依赖jit,所以这目前只在编辑器可用。

--对于lua调用C#,主要会被代码剪裁影响,这时可以配置ReflectionUse(不要配LuaCallSharp),执行“Generate Code”,这时不会对该类生成封装代码,而是生成link.xml把该类配置为不剪裁。

--简而言之,除了CSharpCallLua是必须的(这类生成代码往往不多),LuaCallSharp生成都可以改为用反射。

++++21、支持泛型方法的调用么?

--部分支持,支持的程度可以看示例9。

--其它情况也有办法调用到。如果是静态方法,可以自己写个封装来实例化泛型方法。

--如果是成员方法,xLua支持扩展方法,可以添加一个扩展方法来实例化泛型方法。(该扩展方法使用起来就和普通成员方法一样。)

//立钻哥哥:扩展方法实例化泛型方法

//C#

public static Button GetButton(this GameObject go){

return go.GetComponent<Button>();

}

//--lua

local go = CS.UnityEngine.GameObject.Find(“button”)

go.GetButton().onClick:AddListener(

function() print(‘onClick’) end

)

++++22、支持lua调用C#重载函数吗?

--支持,但没有C#端支持的那么完善,比如重载方法void Foo(int a)和void Foo(short a),由于int和short都对应lua的number,是没法根据参数判断调用的是哪个重载。这是可以借助扩展方法来为其中一个起一个别名。

++++23、编辑器下运行正常,打包的时候生成代码报“没有某方法/属性/字段定义”怎么办?

--往往是由于该方法/属性/字段是扩在条件编译里头,只在UNITY_EDITOR下有效,这是可以通过这方法/属性/字段加到黑名单来解决,加了之后要等编译完成后重新执行代码生成。

++++24、this[string field]或者this[object field]操作符重载为什么在lua无法访问?(比如Dictionary<string, xxx>,Dictionary<object, xxx>在lua中无法通过dic[‘abc’]或者dic.abc检索值)

--因为1:这个特性会导致基类定义的方法、属性、字段等无法访问(比如Animation无法访问到GetComponent方法);

--因为2:key为当前类某方法、属性、字段的名字的数据无法检索,比如Dictionary类型,dic[‘TryGetValue’]返回的是一个函数,指向Dictionary的TryGetValue方法。

--如果版本大于2.1.11,可以用get_Item来获取值,用set_Item来设置值。要注意只有this[string field]或者this[object field]才有这两个替代api,其它类型的key是没有的。

--如果版本小于或等于2.1.11,建议直接方法该操作符的等效方法,比如Dictionary的TryGetValue,如果该方法没有提供,可以在C#那通过Extension method封装一个使用。

++++25、有的Unity对象,在C#为null,在lua为啥不为nil呢?比如一个已经Destroy的GameObject。

--其实那C#对象并不为null,是UnityEngine.Object重载的==操作符,当一个对象被Destroy,未初始化等情况,obj==null返回true,但这C#对象并不为null,可以通过System.Object.ReferenceEquals(null,obj)来验证下。

--对这种情况,可以为UnityEngine.Object写一个扩展方法:

[LuaCallCSharp]

[ReflectionUse]

public static class UnityEngineObjectExtention{

public static bool IsNull(this UnityEngine.Object o){

return o == null;

}

}

--然后在lua那对所有UnityEngine.Object实例都使用IsNull判断:

print(go.GetComponent(‘Animator’):IsNull())

++++26、泛型实例怎么构造?

--涉及的类型都在mscorlib,Assembly-CSharp程序集的话,泛型实例的构造和普通类型时一样的,都是CS.namespace.typename(),可能比较特殊的是typename的表达,泛型实例的typename的表达包含了标识符非法符号,最后一部分要换成[“typename”],以List为例:

----local lst = CS.System.Collections.Generic[‘List`1[System.String]’]();

--如果某个泛型实例的typename不确定,可以在C#测打印下typeof(不确定的类型).ToString()。

--如果涉及mscorlib,Assembly-CSharp程序集之外的类型的话,可以用C#的反射来做:

----local dic = CS.System.Activator.CreateInstance(CS.System.Type.GetType(‘System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[UnityEngine.Vector3, UnityEngine]], mscorlib’))

----dic:Add(‘a’, CS.UnityEngine.Vector3(1, 2, 3))

----print(dic:TryGetValue(‘a’))

++++27、调用LuaEnv.Dispose时,报“try to dispose a LuaEnv with C# callback!”错是什么原因?

--这是由于C#还存在指向lua虚拟机里头某个函数delegate,为了防止业务在虚拟机释放后调用这些无效(因为其引用的lua函数所在虚拟机都释放了)delegate导致的异常甚至崩溃,做了这个检查。

--怎么解决?释放这些delegate即可,所谓释放,在C#中,就是没有引用:

----是在C#通过LuaTable.Get获取并保存到对象成员,赋值该成员为null;

----是在lua那把lua函数注册到一些事件事件回调,反注册这些回调;

----是通过xlua.hotfix(class, method, func)注入到C#,则通过xlua.hotfix(class, method, nil)删除;

--立钻哥哥:注意以上操作在Dispose之前完成。

++++28、调用LuaEnv.Dispose崩溃?

--很可能是这个Dispose操作是由lua那驱动执行,相当于在lua执行的过程中把lua虚拟机给释放了,改为只由C#执行即可。

++++29、C#参数(或字段)类型是object时,传递整数默认是以long类型传递,如何指明其它类型?比如int。

//立钻哥哥:示例参考

using UnityEngine;

using XLua;

namespace YanlzXLuaTest{

    public class RawObjectTest : MonoBehaviour{

public static void PrintType(object o){

Debug.Log(“立钻哥哥:type:” + o.GetType() + “ , value: ” + o);

}

//Use this for initialization

        void Start(){

LuaEnv luaenv = new LuaEnv();

//直接传1234到一个object参数,xLua将选择能保留最大精度的long来传递

luaenv.DoString(“CS.YanlzXLuaTest.RawObjectTest.PrintType(1234)”);

//立钻哥哥:通过一个继承RawObject的类,能实现指明以一个int来传递

luaenv.DoString(“CS.YanlzXLuaTest.RawObjectTest.PrintType(CS.XLua.Cast.Int32(1234))”);

luaenv.Dispose();

}

}    //立钻哥哥:public class RawObjectTest : MonoBehaviour{}

}    //立钻哥哥:namespace YanlzXLuaTest

++++30、如何做到先执行原来的C#逻辑,然后再执行补丁?

--用util.hotfix_ex,可以调用原先的C#逻辑。

local util = require ‘xlua.util’

util.hotfix_ex(CS.HotfixTest, ‘Add’, function(self, a, b)

local org_sum = self:Add(a, b)

print(‘org_sum’, org_sum)

return a + b

end)

++++31、怎么把C#的函数赋值给一个委托字段?

--2.1.8及之前版本,把C#函数当成一个lua函数即可,性能会略低,因为委托调用时先通过Bridge适配代码调用lua,然后lua再调用回C#。

--2.1.9 xlua.util新增createdelegate函数。

++++32、为什么有时Lua错误直接中断了而没错误信息?

--情况1:错误代码用协程跑,而标准的lua,协程出错是通过resume返回值来表示。如果希望协程出错直接抛异常,可以在resume调用那加个assert。(【coroutine.resume(co, ...)】改为:【assert(coroutine.resume(co, ...))】)

--情况2:上层catch后,不打印。(比如某些sdk,在回调业务时,try-catch后把异常吃了。)

++++33、重载含糊如何处理?

--比如由于忽略out参数导致的Physics.Raycast其中的一个重载调用不了,比如short,int无法区分的问题。

--首先out参数导致重载含糊比较少见,比如Physics.Raycast,建议通过自行封装来解决(short, int这种情况也适用):静态函数的直接封装个另外名字的,如果是成员方法则通过Extension method来封装。

++++34、支持interface扩展方法么?

--考虑到生成代码量,不支持通过obj:ExtensionMethod()的方式去调用,支持通过静态方法的方式去调用CS.ExtensionClass.ExtensionMethod(obj)

 

++立钻哥哥推荐的拓展学习链接(Link_Url):

++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/

++++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快速入门篇(基础概述)(Yanlz+toLua+xLua)相关推荐

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

                            <Lua热更新> ##<Lua热更新>发布说明: ++++"Lua热更新"开始了,立钻哥哥终于开始此部分的探 ...

  2. 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开发基础>发布说明: +++ ...

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

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

  4. Java基础-SSM之mybatis快速入门篇

    Java基础-SSM之mybatis快速入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 其实你可能会问什么是SSM,简单的说就是spring mvc + Spring + m ...

  5. web快速入门之基础篇-js:3_3、简易购物车

    目录 一.前言 二.js代码实例演示 1.简易购物车01_数量变化 (1)实例代码 (2)效果演示 2.简易购物车02_数量价格同步 (1)实例代码 (2)效果演示 一.前言 上一篇介绍了DHTML对 ...

  6. web快速入门之基础篇-html:2、基本标签之标题标签、段落标签、换行标签

    目录 一.前言 二.简单实例介绍 实例代码 运行效果 三.要点说明 1.h1到h6 标题标签 2.p 段落标签 3.br 换行标签[单标记] 一.前言 上一篇文章我整理以前上学的笔记是初见 html ...

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

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

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

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

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

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

最新文章

  1. linux sed命令的用法
  2. 关闭 Adobe Flash 沙箱(保护模式)解决Flash崩溃及卡顿问题
  3. TCP文件下载器(Python)
  4. springboot事物注解不生效_springboot事务不生效的几种解决方案
  5. [转]:xmake工程描述编写之选择性编译
  6. 大篆汉字对照表_甲骨文汉字对照表大全_甲骨文数字象形字对照图
  7. 遥感解译结果(多要素)用Arcgis做拓扑检查
  8. 实现图片懒加载的方法
  9. 爬虫抓取新浪微博数据
  10. pygame之mouse模块
  11. pytest文档71-pytest+yaml实现接口自动化框架
  12. leapFTP 使用笔记
  13. Spark核心之top、take和takeOrdered
  14. 英语单词在计算机中怎么存放,电脑怎么学英语单词
  15. 【我的OpenGL学习进阶之旅】EGL简介
  16. 仿163邮箱上传多附件,有点酷
  17. 阿里云视频云正式支持AV1编码格式 为视频编码服务降本提效
  18. 【Jquery】jquery-qrcode把域名地址生成二维码图片,扫码即可访问
  19. MySQL 亿级数据的迁移、清洗、与审计分析
  20. 联想平板android版本升级,联想乐Pad A1平板安卓4.0升级操作详细教程

热门文章

  1. Anaconda3 安装教程
  2. 面向对象程序设计课程设计:利用决策树方法判定西瓜质量
  3. 解决pnpm 不是内部或外部命令的问题(pnpm安装教程)
  4. IF函数多个条件判断及嵌套
  5. LeetCode 309. 最佳买卖股票时机含冷冻期 | Python
  6. 关于win7 32bit连接win10共享打印机0x0000011b解决办法
  7. Ubuntu安装X265+FFMPEG
  8. Macbook Pro 外接显卡实现Tensorflow GPU运行之环境配置(重点)
  9. ORACLE存储过程RECORD数据类型的使用
  10. 怎样批量查询中通快运物流并分析出提前签收的单号