找回密码
 立即注册
查看: 461|回复: 0

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

[复制链接]
发表于 2021-8-18 09:08 | 显示全部楼层 |阅读模式
Lua热更新》

##Lua热更新》发布说明:
++++Lua热更新”开始了,立钻哥哥终于开始此部分的探索了。
++++作为游戏发布迭代的重要技术:Lua热更新在网络游戏迭代更新中非常重要,特别是对于AppStore这样的平台,我们只需要定期更新主App,原则上可以随时灵活更新我们的以Lua热更新框架为基础的代码。
++++当然,作为一项新引入的技术,我们通常是以【快速入门】=>【基础夯实】=>【中级进阶】=>【高级实战】=>【立钻哥哥带您学Lua热更新】等几个阶段进行探索。

##Lua热更新》目录:
#第一篇:Lua快速入门篇
#第二篇:Lua基础夯实篇
#第三篇:Lua中级进阶篇
#第四篇:Lua高级实战篇
#第五篇:立钻哥哥带您学Lua热更新

#第一篇:Lua快速入门篇

#第一篇:Lua快速入门篇
++++第一章:Lua基础概述
++++第二章:xLua教程
++++第三章:Lua基础拓展
++++第四章:立钻哥哥带您学Lua热更新

##第一章:Lua基础概述

++第一章:Lua基础概述
++++1.1Lua热更新框架
++++1.2toLua#热更新框架
++++1.3xLua热更新框架
++++1.4、立钻哥哥带您学Lua基础

###1.1Lua热更新框架

++1.1Lua热更新框架
++++toLuahttps://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 codeiOS下是跑不了的,立钻哥哥:IOS下不能动态载入dll或者cs文件,已经编译进App的没有问题
++++立钻哥哥:以lua热更技术为基础的框架有:toLuauLuaxLuaSLuaC#light等。(ulua+ngui)(tolua+gui)(xlua+ngui)(ulua作者已不再维护,转至tolua)(tolua的性能表现好)



++1.1.1toLua
++++toLuagitHub):https://github.com/topameng/tolua
++++toLua是一个工具,将UnityC#代码包装之后导出给Lua,同时提供了一些访问Lua的接口,使得UnityLua可以相互调用。
++++toLua#Unity静态绑定Lua的一个解决方案,它通过C#提供的反射信息分析代码生成包装的类。(它是一个用来简化在C#中集成Lua的插件,可以自动生成用于在Lua中访问Unity的绑定代码,并把C#中的常量、变量、函数、属性、类以及枚举暴露给lua。)
++++toLua#底层库是使用toLuaC语言编写),那么就需要通过C#来调用原生代码。
++++toLua#集成主要分两部分:
--第一部分:运行时需要的代码,包括一些手写的和自动生成的绑定代码;
--第二部分:编辑器相关代码,主要提供代码生成、编译lua文件等操作,具体就是Unity编辑器中提供的功能。


++++ToLua/Assembly-CSharp】:References/Source/ToLua/
--References/】:
--Source/】:Generate/LuaConst.cs
    ----Generate/:这个文件里面是生成的绑定代码;
    ----LuaConst.cs:这个文件是一些lua路径等配置文件;
--ToLua/】:BaseType/Core/Examples/MiscReflectionReadMe.txt
    ----BaseType/:一些基础类型的绑定代码;
    ----Core/:提供的一些核心功能,包括封装的LuaFunctionLuaTableLuaThreadLuaStateLuaEvent、调用tolua原生代码等;
    ----Examples/:示例代码;
    ----Misc/:杂项,包括LuaClientLuaCoroutine(协程)、LuaLooper(用于tick)、LuaResLoader(用于加载Lua文件);
    ----Reflection/:反射相关;
++++ToLua/Assembly-CSharp-Editor】:References/Editor/ToLua/
--Editor/Custom/CustomSettings.cs】:自定义配置文件,用于定义哪些类作为静态类型、哪些类需要导出、哪些附加委托需要导出等。
--ToLua/Editor】:Extend/ToLuaExport.csToLuaMenu.csToLuaTree.cs
    ----Extend/:扩展一些类的方法;
    ----ToLuaExport.cs:真正生成Lua绑定的代码;
----ToLuaMenu.csLua菜单上功能对应的代码;
----ToLuaTree.cs:辅助树结构;
++++Generate All流程:生成绑定代码主要放在ToLuaExport.cs里面:
--GenLuaDelegates()函数】:生成委托绑定的代码,它会从CustomSettings.customDelegateList里面取出所有自定义导出的委托列表,然后把CustomSettings.customTypeList里面的所有类型中的委托根据一定规则加入到list中,最后调用ToLuaExport.GenDelegates()方法来生成委托绑定的代码,生成的代码在DelegateFactory.cs文件中。(立钻哥哥:该函数的详细实现可查看ToLuaExport.cs中的函数实现。)
--GenerateClassWraps()函数】:遍历所有需要导出的类,然后调用ToLuaExport.Generate()方法来生成类的绑定代码。



++1.1.2uLua
++++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.3xLua
++++xLua官方: https://github.com/Tencent/xLua
++++xLuaUnity3DLua编程解决方案,腾讯已将xLua开源到GitHub
++++xLua20153月完成第一个版本;201612月末,xLua实现新的突破:全平台支持用Lua修复C#代码bug
++++xLuaUnity.NetMonoC#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便和C#相互调用。(xLua在功能、性能、易用性都有不少突破。)
++++xLua的突破:1、可以运行时把C#实现(方法,操作符,属性,事件等等)替换成lua实现;2、出色的GC优化,自定义struct,枚举在LuaC#间传递无C# gc alloc3、编辑器下无需生成代码,开发更轻量。
++++xLua热补丁技术支持在运行时把一个C#实现(函数,操作符,属性,事件,或者整个类)替换成Lua实现,意味着我们可以:1、平时用C#开发;2、运行也是C#,性能秒杀Lua3、有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/】:为xLualua脚本提供的C#侧代码支持,被Resources/xLua/Common/所使用。
++++Scripts/HotfixTest】:需要热修复的C#脚本。
++++Resource/xLua/HotFix】:热修复脚本。
###1.2toLua#热更新框架

++1.2toLua#热更新框架
++++toLuahttps://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>等;
--支持Luafunction转委托,可以区分需要不同委托的参数的重载函数;
--支持C# LuaFunction对象转委托,简化调用方式。支持无GC的多参数调用形式;
--支持重载函数自动排序,如:参数个数相同,object参数执行级最低,不会出现错误匹配情况;
--支持导出函数重命名,可以分离导出某个重载函数(可以导出被折叠掉的函数);
--支持使用编辑器类改写导出规则;
--支持this数组访问,索引为int可以通过[]访问,其他可使用.get_Item或者.this.get()访问数组成员;
--支持委托(事件)+-lua function。支持通过函数接口的AddRemove委托操作;
--支持静态反射操作,形式同C#
--支持peer表,可在lua端扩展导出的userdata
--支持自定义struct压入和读取,做到无GC,并且结构成员无类型限制;
--支持preloading,可以通过require后绑定wrap文件;
--支持int64unit64
--大量的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文件中,这部分代码中不包含BaseTypeArrayEnumObjectString等等)的注册。
//立钻哥哥:GenLuaAlltolua-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(Type tin 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(Type tin 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.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.3toLua#核心运行时
++++toLua#的运行代码包含【Source/Generate/】下的绑定代码,以及【ToLua/BaseType/】代码,以及【ToLua/Core/】下的核心代码。
++++LuaAttribute.cs】:在toLua#生成绑定代码时做一些标示使用。
++++LuaBaseRef.cs】:Lua中对象对应C#中对象的一个基类,主要作用是用一个reference指向lua里面的对象,引用计数判断两个对象是否相等。(比如:LuaFunction里面的reference是指向lua里面的一个闭包的,而LuaTablereference是指向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.ToObjectObjectTranslator获取真正的C#对象。)

++1.2.4、关于反射
++++toLua#不支持动态反射。(动态反射对于重载函数有参数匹配问题,函数排序问题,ref, out参数问题等等。)
++++toLua#提供的替换方法是:
--1preloading,把未来可能需要的类型添加到导出列表customTypeList,同时也添加到dynamicList列表中,这样导出后该类型不会随binder注册到lua中,可以通过require “namespace.classname”动态注册到lua中,对于非枚举类型toLua#系统也可以在第一次push该类型时动态载入,当然也可在过场动画、资源下载、登录、场景加载或者某个的函数中require这个类型。
--2、静态反射。通过静态反射支持精确的函数参数匹配和类型检查。不会存在重载函数参数混乱匹配错误问题。(注意iOS必须配置好link.xml
++++link.xmltolua-master-\Assets\link.xml
//立钻哥哥:link.xmliOS必须配置好link.xml
<?xml version=”1.0”encoding=”utf-8”?>
<linker>
    <assembly fullname=”mscorlib”>
        <namespace fullname=”System.Collections.Generic”perserve=”all”/>
    </assembly>
</linker>

++1.2.5toLua简单示例: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.6toLua简单示例:ScriptsFromFileDoFileRequire的区别)
//立钻哥哥:ScriptsFromFile示例(\ToLua\Examples\02_ScriptsFromFile\ScriptsFromFile.cs
using UnityEngine;
using System.Collections;
using LuaInterface;
using System;
using System.IO;

//立钻哥哥:展示searchpath使用,requiredofile区别
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{}
++++立钻哥哥:该示例展示了dofilerequire区别。
++++toLua#DoFile函数,和Lua保持一致行为,能多次执行一个文件。
++++toLua#中加入了新的Require函数,无论C#lua谁先require一个lua文件,都能保证加载唯一性。
++++LuaState.AddSearchPath()】:增加搜索目录,这样DoFileRequire函数可以只用文件名,无需写全路径。
++++LuaState.DoFile()】:加载一个lua文件,注意dofile需要扩展名,可反复执行,后面的变量会覆盖之前的DoFile加载的变量。
++++LuaState.Require()】:同lua require(modname)操作,加载指定模块并且把结果写入到package.loaded中,如果modname存在,则直接返回package.loaded[modname]
++++LuaState.Collect()】:垃圾回收,对于被自动gcLuaFunctionLuaTable,以及委托减掉的LuaFunction,延迟删除的Object类等需要延迟处理的回收,都在这里自动执行。

++1.2.7toLua#示例: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.8toLua#示例: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();

      //立钻哥哥:cacheLuaTable进行访问
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);
        }

        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()】:可以获取当前tablemetatable
++++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.9toLua#示例: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, 1do
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.3xLua热更新框架

++1.3xLua热更新框架
++++xLuahttps://github.com/Tencent/xLua
++++xLuaUnity.NetMonoC#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便和C#相互调用。

++1.3.1xLua的突破
++++xLua在功能、性能、易用性都有不少突破,最具代表性的是:
--1、可以运行时把C#实现(方法,操作符,属性,事件等等)替换成Lua实现;
--2、出色的GC优化,自定义struct,枚举在LuaC#间传递无C# gc alloc
--3、编辑器下无需生成代码,开发更轻量;
++++【总体特性】
--1Lua虚拟机支持Lua5.3Luajit2.1
--2Unity3D版本支持:各版本支持;
--3、平台支持windows64/32androidios 64/32/bitcodeosxuwpwebgl
--4、互访技术:生成适配代码、反射;
--5、易用性:解压即可用;开发期无需生成代码;生成代码和反射间可无缝切换;更简单的无GC api;菜单简单易懂;配置可以多份,按模块划分,也可以直接在目标类型上打Attribute标签;自动生成link.xml防止代码剪裁;Plugins部分采用cmake编译,更简单;核心代码不依赖生成代码,可以随时删除生成目录;
--6、性能lazyload技术,避免用不上的类型的开销;lua函数映射到C# delegatelua table映射到interface,可实现接口层面无C# gc alloc开销;所有基本值类型,所有枚举,字段都是值类型的struct,在LuaC#间传递无C# gc allocLuaTable,LuaFunction提供无gc访问接口;通过代码生成期的静态分析,生成最优代码;支持C#Lua间指针传递;自动解除已经DestroyUnityEngine.Object的引用;
--7、扩展性:不用改代码就可以加入Lua第三方扩展;生成引擎供接口做二次开发;
++++【支持C#实现打补丁】:构造函数、析构函数、成员函数、静态函数、泛化函数、操作符重载、成员属性;静态属性;事件;
++++Lua代码加载】:加载字符串;支持加载后立即执行;支持加载后返回一个delegate或者LuaFunction,调用delegate或者LuaFunction后可传脚本参数;Resources目录的文件;直接require;自定义loaderLua里头require时触发;require参数透传给loaderloader读取Lua代码返回;Lua原有的方式;Lua原有的方式都保留;
++++Lua调用C#
--1、创建C#对象C#静态属性,字段;C#静态方法;C#成员属性,字段;C#成员方法;C#继承;子类对象可以直接调用父类的方法,访问父类属性;子类模块可以直接调用父类的静态方法,静态属性;
--2、扩展方法(Extension methods:就像普通成员方法一样使用;
--3、参数的输入输出属性(out, refout对应一个lua返回值;ref对应一个lua参数以及一个lua返回值;
--4、函数重载:支持重载;由于lua数据类型远比C#要少,会出现无法判断的情况,可通过扩展方法来调用;操作符重载;
--5、支持的操作符+-*/==<<=%[];其他操作符可以借助扩展方法调用;
--6、参数默认值C#参数有默认值,在Lua可以不传;
--7、可变参数:在对应可变参数部分,直接输入一个参数即可,不需要把这些参数扩到一个数组里头;
--8、泛化方法调用:静态方法可以自行封装使用;成员函数可通过扩展方法封装使用;
--9、枚举类型:数字或字符串到枚举的转换;
--10delegate:调用一个C# delegate+操作符;-操作符;把一个lua函数作为一个c# delegate传递给C#
--11evnet:增加事件回调;移除事件回调;
--1264位整数:传递无gc而且无精度损失;lua53下使用原生64位支持;可以和number运算;以java的方式支持无符号64位整数;
--13table的自动转换到C#复杂类型
--14obj.complexField = { a=1, b = { c = 1}}obj是一个C#对象,complexField是两层嵌套的struct或者class
--15typeof:对应C#typeof操作符,返回Type对象;
--16lua侧直接clone
--17decimal:传递无gc而且无精度损失;
++++C#调用Lua:调用lua函数;以delegate方式调用Lua函数;以LuaFunction调用Lua函数;访问LuatableLuaTable的泛化Get/Set接口,调用无gc,可指明KeyValue的类型;用标注了CSharpCallLuainterface访问;值拷贝到structclass
++++Lua虚拟机】:虚拟机gc参数读取及设置;
++++【工具链】Lua Profiler;可根据函数调用总时长,平均每次调用时长,调用次数排序;显示lua函数名及其所在文件的名字及行号;如果C#函数,会显示这个C#函数;支持真机调试;

++1.3.2xLua常见问题(立钻哥哥:初学者FAQ
++++1xLua发布包怎么用xLua目前已zip包形式发布,在工程目录下解压即可。
++++2xLua可以放别的目录吗?
--可以,但生成代码目录需要配置一下(默认放Assets/XLua/Gen目录);
--更改目录要注意的是:生成代码和xLua核心代码必须在同一程序集。如果要用热补丁特性,xLua核心代码必须在Assembly-CSharp程序集;
++++3lua源码只能以txt后缀?
--什么后缀都可以。
--如果想以TextAsset打包到安装包(比如放到Resources目录),Unity不认lua后缀,这是Unity的规则;
--如果不打包到安装包,就没有后缀的限制:比如自行下载到某个目录(这也是热更的正确姿势),然后通过CustomLoader或者设置package.path去读这个目录;
--为啥xLua本身带的lua源码(包括示例)为什么都是txt结尾呢?因为xLua本身就一个库,不含下载功能,也不方便运行时去某个地方下载代码,通过TextAsset是较简单的方式;
++++4、编辑器(或非il2cppandroid)下运行正常,ios下运行调用某函数报“attempt to call a nil value
--il2cpp默认会对诸如引擎、C#系统api,第三方dll等等进行代码裁剪。(简单来说就是这些地方的函数如果C#代码没访问到的就不编译到最终发布包。)
--解决办法:增加引用(比如配置到LuaCallCSharp,或者自己C#代码增加那函数的访问),或者通过link.xml配置(当配置了ReflectionUse后,xlua会自动帮我们配置到link.xml)告诉il2cpp别剪裁某类型;
++++5Plugins源码在哪里可以找到,怎么使用?
--Plugins源码位于xLua_Project_Root/build下。
--源码编译依赖cmake,按照cmake后执行make_xxxx_yyyy.zz即可。(xxxx代表平台,比如iosandroid等)(yyyy是要集成的虚拟机,有lua53luajit)(zz是后缀,window下是bat,其他平台是sh
--windows编译依赖Visual Studio 2015
--android编译在linux下执行,依赖NDK,并且需要把脚本中ANDROID_NDK指向NDK的安装目录。
--iososx需要在mac下编译。
++++6、报类似“xlua.access, no field _Hitfix0_Update”的错误怎么解决?
--Hotfix操作指南一步步操作。
--https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/hotfix.md
--使用方式:
    ----1、添加HOTFIX_ENABLE宏打开该特性(在Unity3DFile->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”)。
++++9Unity5.5以上执行“XLua/Hotfix Inject In Editor”菜单会提示“WARNING: The runtime version supported by this application is unavailable.
--这是因为注入工具是用.net3.5编译,而Unity5.5MonoBleedingEdgemono环境并没3.5支持导致的,不过一般而言都向下兼容,目前为止也没发现该warning带来什么问题。
--可能有人发现定义INJECT_WITHOUT_TOOL用内嵌模式会没有该warning,但问题是这模式是调试问题用的,不建议使用,因为可能会有一些库冲突问题。
++++10hotfix下怎么触发一个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、如果需要调试,CustomLoaderfilepath参数该如何处理?
--lua里头调用require ‘a.b’时,CustomLoader会被调用,并传入字符串“a.b”,需要理解这字符串,(从文件/内存/网络等)加载好lua文件,返回两个东西,第一个是调试器可以理解的路径,比如:a/b.lua,这个通过设置ref类型的filepath参数返回,第二个是UTF8格式的源码的字节流(byte[]),通过返回值返回。
++++14、什么是生成代码?
--xLua支持的luaC#间交互技术之一,这种技术通过生成两者间的适配代码来实现交互,性能较好,是推荐的方式。
--另一种交互技术是反射,这种方式对安装包的影响更少,可以在性能要求不高或者对安装包大小很敏感的场景下使用。
++++15、改了接口后,之前生成的代码出现错误怎么办?
--清除掉生成代码(执行“Clear Generated Code”菜单,如果重启过,会找不到这个菜单,这是可以手动删除整个生成代码目录),等编译完成后重新生成。
++++16、应该什么时候生成代码?
--开发期不建议生成代码,可以避免很多由于不一致导致的编译失败,以及生成代码本身的编译等待。
--build手机版本前必须执行生成代码,建议做成自动化的。
--做性能调优,性能测试前必须执行生成代码,因为生成和不生成性能的区别还是很大的。
++++17CS命名空间下有所有C# API是不是很占内存?
--由于用了lazyload,这个“有”只是个虚拟的概念,比如UnityEngine.GameObject,是访问第一次CS.UnityEngine.GameObject或者第一个实例往lua传送才加载该类型方法,属性等。
++++18LuaCallSharp以及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函数,或者用LuaTableLuaFunction的无gc接口,或者数组的话,已下值类型都没有gc的:
----1、所有的基本值类型(所有整数,所有浮点数,decimal);
----2、所有的枚举类型;
----3、字段只包含值类型的struct,可嵌套其它只包含值类型struct
----立钻哥哥:其中23需要把该类型加到GCOptimize
++++20、反射在ios下可用吗?
--ios下的限制有两个:1、没有jit2、代码剪裁(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),由于intshort都对应luanumber,是没法根据参数判断调用的是哪个重载。这是可以借助扩展方法来为其中一个起一个别名。
++++23、编辑器下运行正常,打包的时候生成代码报“没有某方法/属性/字段定义”怎么办?
--往往是由于该方法/属性/字段是扩在条件编译里头,只在UNITY_EDITOR下有效,这是可以通过这方法/属性/字段加到黑名单来解决,加了之后要等编译完成后重新执行代码生成。
++++24this[string field]或者this[object field]操作符重载为什么在lua无法访问?(比如Dictionary<string, xxx>Dictionary<object, xxx>lua中无法通过dic[‘abc’]或者dic.abc检索值)
--因为1:这个特性会导致基类定义的方法、属性、字段等无法访问(比如Animation无法访问到GetComponent方法);
--因为2key为当前类某方法、属性、字段的名字的数据无法检索,比如Dictionary类型,dic[‘TryGetValue’]返回的是一个函数,指向DictionaryTryGetValue方法。
--如果版本大于2.1.11,可以用get_Item来获取值,用set_Item来设置值。要注意只有this[string field]或者this[object field]才有这两个替代api,其它类型的key是没有的。
--如果版本小于或等于2.1.11,建议直接方法该操作符的等效方法,比如DictionaryTryGetValue,如果该方法没有提供,可以在C#那通过Extension method封装一个使用。
++++25、有的Unity对象,在C#null,在lua为啥不为nil呢?比如一个已经DestroyGameObject
--其实那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、泛型实例怎么构造?
--涉及的类型都在mscorlibAssembly-CSharp程序集的话,泛型实例的构造和普通类型时一样的,都是CS.namespace.typename(),可能比较特殊的是typename的表达,泛型实例的typename的表达包含了标识符非法符号,最后一部分要换成[“typename”],以List为例:
    ----local lst = CS.System.Collections.Generic[‘List`1[System.String]’]();
--如果某个泛型实例的typename不确定,可以在C#测打印下typeof(不确定的类型).ToString()
--如果涉及mscorlibAssembly-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#执行即可。
++++29C#参数(或字段)类型是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其中的一个重载调用不了,比如shortint无法区分的问题。
--首先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--_--

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 11:31 , Processed in 0.118397 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表