由于网上关于Unity Lua学习的资料较少,本人也是刚入门U3D不久,现在项目准备基于LuaFramework用Lua做热更新开发,最近几天看了ToLua官网的文档说明,研究了一下C#与Lua的交互并做了一些尝试,发现实际入手还是遇到了很多配置文件,方法调用的坑,并根据学习资料做了一些整理,方便初学者参考,好了 废话不多说,进入正题,由于比较基础大神绕过,不喜勿喷!
基础部分:
解决的问题
更新频繁,而IOS审核长
IOS无法用DLL反射机制去做代码更新
如果无热更新,客户端每次都需要重新下载一次安装包。用户体验不好
解决发布包的容量问题,一切都可以增量下载
原理
首先要清楚Unity的打包原理,也就是AssetBundle的打包机制,他会把prefab打包成.asset格式作为传输的数据。
通过校验文件的MD5值来判断是否需要更新,如果需要更新则下载差异文件。
lua属于解释性文件所以能通过www直接下载到本地,通过C#与lua交互,把逻辑写在lua里,从而实现代码热更新。注意记得加密lua。
实现方案
这里我推荐LuaFramework 这个框架(下简称tolua),以上内容他已经完全封装好,包括上述没提到的CG的一些操作。
我们只需要在tolua里面写属于自己模块部分的逻辑就能简单的实现热更新。
Tolua学习
简介
tolua#是Unity静态绑定lua的一个解决方案,它通过C#提供的反射信息分析代码并生成包装的类。它是一个用来简化在C#中集成lua的插件,可以自动生成用于在lua中访问Unity的绑定代码,并把C#中的常量、变量、函数、属性、类以及枚举暴露给lua。它是从cstolua衍变而来。从它的名字可以看出,它是集成了原来的tolua代码通过二次封装写了一个C#与tolua(c)的一个中间层。
基础
要想了解tolua#是如何集成的我们需要对C#的一些特性有一些了解,比如了解C#与原生代码交互的方式等。,我们假设读者已经对lua和tolua++有一个比较熟悉,我们略过lua与c或者C++的交互方式,主要介绍一些C#的特性,来帮助我们接下来分析tolua#的集成原理。
C#特性
Attribute
Attribute 是一种可由用户自由定义的修饰符(Modifier),可以用来修饰各种需要被修饰的目标。特性Attribute 的作用是添加元数据。元数据可以被工具支持,比如:编译器用元数据来辅助编译,调试器用元数据来调试程序。Unity以及tolua#中就会用Attribute来辅助做一些事情。
值类型与引用类型
只所以要提这两个概念,是因为很好得理解这两个概念有助于我们写出比较高效的C#代码。
我们知道,C#中的每一种类型要么是值类型,要么是引用类型。所以每个对象要么是值类型的实例,要么是引用类型的实例。
引用类型和值类型都继承自System.Object类。不同的是,几乎所有的引用类型都直接从System.Object继承,而值类型则继承其子类,即直接继承System.ValueType。
作为所有类型的基类,System.Object提供了一组方法,这些方法在所有类型中都能找到,其中包含toString方法及clone等方法。
System.ValueType直接继承System.Object,即System.ValueType本身是一个类类型,而不是值类型;System.ValueType没有添加任何成员,但覆盖了所继承的一些方法,使其更适合于值类型。例如,ValueType重写了Equals()方法,从而对值类型按照实例的值来比较,而不是引用地址来比较。
![](https://i-blog.csdnimg.cn/blog_migrate/1015389922bc8c0f2bbc0ee2738110ea.png)
简单了解了值类型与引用类型那么我们下面来看下C#中的装箱和拆箱的概念。
装箱和拆箱
装箱和拆箱是值类型和引用类型之间相互转换是要执行的操作。
1. 装箱在值类型向引用类型转换时发生
2. 拆箱在引用类型向值类型转换时发生
```
object objValue = 4;
int value = (int)objValue;
```
上面的两行代码会执行一次装箱操作将整形数字常量4装箱成引用类型object变量objValue;然后又执行一次拆箱操作,将存储到堆上的引用变量objValue存储到局部整形值类型变量value中。
同样我们需要看下IL代码:
```
locals init (
[0] object objValue,
[1] int32 'value'
//上面IL声明两个局部变量object类型的objValue和int32类型的value变量
IL_0000: nop
IL_0001: ldc.i4.4 //将整型数字4压入栈
IL_0002: box [mscorlib]System.Int32 //执行IL box指令,在内存堆中申请System.Int32类型需要的堆空间
IL_0007: stloc.0 //弹出堆栈上的变量,将它存储到索引为0的局部变量中
IL_0008: ldloc.0//将索引为0的局部变量(即objValue变量)压入栈
IL_0009: unbox.any [mscorlib]System.Int32 //执行IL 拆箱指令unbox.any 将引用类型object转换成System.Int32类型
IL_000e: stloc.1 //将栈上的数据存储到索引为1的局部变量即value
```
拆箱操作的执行过程和装箱操作过程正好相反,是将存储在堆上的引用类型值转换为值类型并给值类型变量。
C#调用原生代码
因为tolua#底层库是使用的tolua(c语言编写),那么就需要通过C#来调用原生代码,我们从LuaDll.cs中摘取一段代码来演示如何从C#中调用原生代码
```
Public class LuaDll
{
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void lua_close(IntPtr luaState);
}
```
其中LUADLL对应的字符串就是tolua,在不同的平台上mono会去加载对应的tolua.dll或者tolua.so等文件并调用对应的函数。具体可以参考mono官方的教程。
tolua#集成
tolua#集成主要分为两部分,一部分是运行时需要的代码包括一些手写的和自动生成的绑定代码,另一部分是编辑器相关代码,主要提供代码生成、编译lua文件等操作,具体就是Unity编辑器中提供的功能。用mono打开整个tolua#的工程,
Runtime
Source
Generate 这个文件里面是生成的绑定代码
LuaConst.cs 这个文件是一些lua路径等配置文件。
ToLua
BaseLua 一些基础类型的绑定代码
Core 提供的一些核心功能,包括封装的LuaFunction LuaTable LuaThread LuaState LuaEvent、调用tolua原生代码等等。
Examples 示例
Misc 杂项,目前有LuaClient LuaCoroutine(协程) LuaLooper(用于tick) LuaResLoader(用于加载lua文件)
Reflection 反射相关
Editor
Editor
Custom
CustomSettings.cs 自定义配置文件,用于定义哪些类作为静态类型、哪些类需要导出、哪些附加委托需要导出等。
ToLua
Editor
Extend 扩展一些类的方法。
ToLuaExport.cs 真正生成lua绑定的代码
ToLuaMenu.cs Lua菜单上功能对应的代码
ToLuaTree.cs 辅助树结构
Generate All 流程
了解了tolua#的大致文件结构,下面我们来看下tolua#的Generate All 这个功能来分析下它的生成过程。生成绑定代码主要放在ToLuaExport.cs里面,我们并不会对每一个函数进行细致的讲解,如果有什么不了解的地方,可以直接看它的代码。
![](https://i-blog.csdnimg.cn/blog_migrate/6066b35c123e7ff5cd652889729fd770.png)
GenLuaDelegates函数
生成委托绑定的代码,它会从CustomSettings.customDelegateList里面取出所有自定义导出的委托列表,然后把CustomSettings.customTypeList里面的所有类型中的委托根据一定规则加入到list中,最后调用ToLuaExport.GenDelegates()方法来生成委托绑定的代码,生成的代码在DelegateFactory.cs文件中。
由于该函数比较简单,我们这里不做展开,可以直接查看ToLuaExport.cs中的GenDelegates()并配合DelegateFactory.cs来查看。
GenerateClassWraps 函数
遍历所有需要导出的类,然后调用ToLuaExport.Generate()方法来生成类的绑定代码。
下面我们来看下ToLuaExport.Generate()方法,其基本流程如下所示:
![](https://i-blog.csdnimg.cn/blog_migrate/85353ed6f74b0c971bf94284ee5c97bb.png)
从上面的流程图我们可以看到,整个过程还是比较清楚的,如果这个类是枚举类型,那么它会调用枚举导出的接口,而如果这个类型是一个普通的类,那么它就会调用上图所示的相应的流程将代码导出。至于结构体类型,目前应该是只支持一些特定的结构体,需要在lua中对应一份实现(Assets\ToLua\Lua目录中),当然它生成的代码也有一些依赖于tolua#的核心运行时,我们前面简单的讲解了如何在编辑器中生成绑定代码,接下来我们讲一下它的核心运行时。
tolua#的核心运行时
tolua#的运行代码包含SourceàGenerate下面的绑定代码以及ToLuaàBaseType代码以及Core下面的核心代码。接下来我们着重讲一下Core下面的几个主要类。
LuaAttribute.cs
我们前面基础知识部分已经讲过,它在tolua#生成绑定代码时做一些标示使用。
LuaBaseRef.cs
Lua中对象对应C#中对象的一个基类,主要作用是有一个reference指向lua里面的对象,引用计数判断两个对象是否相等等。