|
前言
在介绍 ToLua 前,我们首先来了解一下在游戏开发中,热更新的概念。 热更新
热更新是一种手游及App常用的更新方式,举例来说,游戏上线后,玩家需要通过应用商店及其他渠道下载第一个版本。在运营的过程中,如游戏需要更换UI、修改逻辑、开放功能等,此时若不使用热更新技术,就需要重新打包,那么玩家也就需要通过应用商店或其他渠道重新下载游戏。 热更新可以在不重新下载客户端的情况下,更新游戏的内容。
然而c#是一门编译型语言,其运行之前需要进行编译,而编译的过程在移动平台无法完成,所以当我们游戏的逻辑更改,代码发生变化时,我们就需要重新在开发环境下编译,然后重新打包,让玩家下载最新版本。这个过程中,会下载很多不需要更新的资源,便会增加玩家的时间及流量消耗,造成不好的用户体验。因此在移动平台中便就出现了热更新技术。 现在主流的热更新方式主要有Lua和ILRuntime
在unity的lua热更新中,有ulua、slua、xlua、tolua等多种热更新方案,这些方案提供了C#与Lua的互相调用机制。而我们项目用的是tolua,今天我们就来学习一下tolua的源码。
Tolua
Tolua是Unity静态绑定lua的一个解决方案,它通过C#中集成lua的插件,可以自动生成用于在lua中访问Unity的绑定代码,并把C#中的常量、变量、函数、属性、类以及枚举暴露给lua。其从cstolua衍变而来。 在学习之前,先明确一下我们想学到的一些知识点:
目录
wrap文件是如何生成,为什么要生成wrap?lua是怎么获取、调用c#的静态方法、成员方法?c#对象在lua栈里是以什么形式存在的?c#如何调用到lua的方法的?tolua是怎么把lua的table、function转成c#的table、function实例的?tolua把对象存在objects里,而值类型的Struct如果存在objects了,会发生封箱、拆箱的操作,tolua是如何避免的?objects里的对象是什么时候会被移除?lua怎样才算正确释放了c#对象?利用tolua如何实现热更?针对lua和c#的交互有什么优化手段?
细节
每个wrap文件都是对一个c#类的包装,在lua中,通过对wrap类中的函数调用,间接的对c#实例进行操作。
tolua之wrap文件的原理与使用 - blueberryzzz - 博客园
2. lua是怎么获取、调用c#的静态方法、成员方法?c#对象在lua栈里是以什么形式存在的?
【unity游戏开发】tolua学习-C#和Lua的交互细节
c#对象在lua栈里是以数组下标存储的,需要调用的时候,可以通过这个索引,通过ObjectTranslator.GetObject(int udata)访问到C#层对象 3. c#如何调用到lua的方法的?tolua是怎么把lua的table、function转成c#的table、function实例的?
【unity游戏开发】tolua学习-C#和Lua的交互细节
4. tolua把对象存在objects里,而值类型的Struct如果存在objects了,会发生封箱、拆箱的操作,tolua是如何避免的?
Unity中xLua与toLua对Vector3的优化
5. objects里的对象是什么时候会被移除?lua怎样才算正确释放了c#对象?
Lua和C#对象释放图文详解:用Unity+Lua开发游戏,有什么好的办法进行性能检测?
①c#内存泄露
c# object返回给lua,是通过dictionary将lua的userdata和c# object关联起来,只要lua中的userdata没回收,c# object也就会被这个dictionary拿着引用,导致无法回收。
最常见的就是gameobject和component,如果lua里头引用了他们,即使你进行了Destroy,也会发现他们还残留在mono堆里。
不过,因为这个dictionary是lua跟c#的唯一关联,所以要发现这个问题也并不难,遍历一下这个dictionary就很容易发现。ulua和tolua下这个dictionary在ObjectTranslator类、slua则在ObjectCache类。
那么,lua是什么时候移除了对c#对象的引用的?
tolua在Update里调用了luaState.Collect(); 然后调用了ObjectTranslator.Collect();用了脏标记模式,gcList长度大于0的时候进行回收,调用DestroyUnityObject() //ObjectTranslator.cs
//延迟删除处理
void DestroyUnityObject(int udata, UnityEngine.Object obj)
{
object o = objects.TryGetValue(udata);
if (object.ReferenceEquals(o, obj))
{
RemoveObject(o, udata);
//一定不能Remove, 因为GC还可能再来一次
objects.Destroy(udata);
if (LogGC)
{
Debugger.Log("destroy object {0}, id {1}", o, udata);
}
}
UnityEngine.Object.Destroy(obj);
}
Lua GC
Lua 提供了以下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:
collectgarbage("collect"): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:collectgarbage("count"): 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。collectgarbage("restart"): 重启垃圾收集器的自动运行。collectgarbage("setpause"): 将 arg 设为收集器的 间歇率。 返回 间歇率 的前一个值。collectgarbage("setstepmul"): 返回 步进倍率 的前一个值。collectgarbage("step"): 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。collectgarbage("stop"): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。
如何监测Lua的编程产生内存泄露:
方法:①:
1. 针对会产生泄露的函数,先调用collectgarbage("collect")和collectgarbage("count"),取得最初的内存使用情况。
2. 函数调用后, collectgarbage("collect")进行收集, 并使用collectgarbage("count")再取得当前内存, 最后记录两次的使用差。
3.可以保存函数调用前后的_G到本地文件,然后使用软件比较前后两次的_G的内容差,可以获取到泄漏的具体内容。
方法:②:游戏开发:Unity中Lua造成的堆内存泄露问题
6. 利用tolua如何实现热更?
编辑器运行时热更:【Unity游戏开发】Lua更新运行时代码
线上包热更:【unity游戏开发】AB学习(三)—热更学习
7. 针对lua和c#的交互有什么优化手段?
Lua配置表导出优化
Lua配置表导入优化:延迟导入 参考:
朔宇:Unity3D热更新技术点——ToLua(上)
Xlua源码学习 |
|