[Unity 热更新]tolua原理及实践
来源:互联网 发布:彩虹六号软件 编辑:程序博客网 时间:2024/05/29 03:05
一、概论
1、tolua相比ulua的优势:
①效率更高。
②更加稳定。
③不支持使用反射,只支持wrap方式。
二、基础
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
其中LUADLL对应的字符串就是tolua,在不同的平台上mono会去加载对应的tolua.dll或者tolua.so等文件并调用对应的函数。
①LuaAttribute.cs
在tolua#生成绑定代码时做一些标示使用。
②LuaBaseRef.cs
Lua中对象对应C#中对象的一个基类,主要作用是有一个reference指向lua里面的对象,引用计数判断两个对象是否相等等。
比如LuaFunction里面的reference是指向lua里面的一个闭包的,而LuaTable的reference是指向lua中的一个table的。
③LuaDll.cs
这个类的主要作用就是实现了C#调用原生代码的功能。
④LuaState.cs
这里面是对真正的lua_State的封装,包括初始化lua路径,加载相应的lua文件,注册我们前面生成的绑定代码以及各种辅助函数。
⑤ObjectTranslator.cs
主要意义就是给lua中对C#对象的交互提供了基础,简单来说就是C#中的对象在传给lua时并不是直接把对象暴露给了lua,而是在这个OjbectTranslator里面注册并返回一个索引(可以理解为windows编程中的句柄),并把这个索引包装成一个userdata传递给lua,并且设置元表。具体可以查看tolua_pushnewudata代码。
参考一
弱引用可以让您保持对对象的引用,同时允许GC在必要时释放对象,回收内存。对于那些创建便宜但耗费大量内存的对象,即希望保持该对象,又要在应用程序需要时使用,同时希望GC必要时回收时,可以考虑使用弱引用。
Object obj = new Object();WeakReference wref = new WeakReference( obj );obj = null;
第一行代码新建了一个新的对象,这里叫它对象A,obj是对对象A的强引用。接着第二行代码新建了一个弱引用对象,参数就是对象A的强引用,第三行代码释放掉对对象A的强引用。这时如果GC进行回收,对象A就会被回收。
怎样在取得对象A的强引用呢?很简单,请看代码2:
Object obj2 = wref.Target;if( obj2 != null ){ // 做你想做的事}else{// 对象已经被回收,如果要用必须新建一个}
1、提供Lua-c#值类型、对象类型转化操作交互层。(ObjectTranslator.cs、LuaFunction.cs、LuaTable.cs、ToLua.cs等)。
2、提供Lua虚拟机创建、启动、销毁,Require、DoFile、DoString、Traceback等相关支持。(LuaState.cs、LuaStatic.cs)。
3、提供导出工具,利用c#反射,对指定的c#类生成对应的wrap文件,启动后将所有wrap文件注册到lua虚拟机中。(ToLuaMenu.cs、ToLuaExport.cs、ToLuaTree.cs、LuaBinder.cs、CustomSetting.cs等)。
4、提供c#对象和lua userdata对应关系,使该userdata能访问对应c#对象属性,调用对应c#对象函数。lua支持一定的面向对象(类、继承)。管理这些对象的内存分配与生命周期、GC。(LuaState.cs)。
5、提供支持功能Lua Coroutine、反射等,Lua层重写部分性能有问题对象如Vector系列。(Vector3.lua等)。
LuaState继承自LuaStatePtr,该类包含一个System.IntPtr L指针,即lua虚拟机栈,并提供了一系列LuaDLL API的封装,可以认为是LuaDLL的升级版。而成员属性中比较重要的有ObjectTranslator和LuaReflection,暂时我们只用关注ObjectTranslator。
1、new LuaState()总结
①初始化了LuaState成员属性ObjectTranslator和LuaReflection。
②LuaState构造开始。
③构造中先初始化了LuaException用于异常时抛出,提供了出错信息堆栈的格式化显示。
④以标准的LuaDLL.luaL_newstate语句正式启动lua虚拟机。
⑤luaL_openlibs开启lua基本库,额外地在c层初始了tolua的一些相关内容。
⑥OpenToLuaLibs向lua虚拟机注册ToLua.Print、ToLua.DoFile、Panic等基础c函数。
⑦OpenBaseLibs先向虚拟机注册了一些System和Unity命名空间下的基础类,接着初始化了Mathf、Layer和一些反射相关。
⑧InitLuaPath在LuaFileUtils中存储了lua中package.path、LuaConst中一些路径搜索路径。用于查找文件。
⑨LuaState构造结束。
LuaState.Start()先是DoFile(“tolua.lua”),而tolua.lua中require了所有tolua重写的一些Unity性能有问题的类型,例如Vector3、Vector2、Bound等。接着在C#缓存了这些类型的一些lua方法。
LuaBinder.cs这个文件是ToLua导出自动生成的,而Bind函数是向lua虚拟机注册所有的wrap类。
lua调用c#比c#调用lua效率要高。Lua 调用 Wrap , Wrap调用C#的模式,实现了Lua调用C#。
尽最多可能使用lua中的容器table取代c#中的所有容器,因为lua的table是c语言实现,效率比c#最快的容器Dictionary还要快。
安卓平台如果使用luajit的话,记得在lua最开始执行的地方请开启 jit.off(),性能会提升N倍。
loadassembly 反射
require 非反射
参考二
三、示例
public class HelloWorld : MonoBehaviour{ void Awake() { LuaState lua = new LuaState(); //创建Lua虚拟机; lua.Start(); //初始化; string hello = //Lua代码; @" print('hello tolua#') "; lua.DoString(hello, "HelloWorld.cs"); //执行; lua.CheckTop(); lua.Dispose(); //回收; lua = null; }}
loadfile,加载文件,编译文件,并且返回一个函数,不运行。
dofile,其实就是包装了Loadfile,根据loadfile的返回函数运行一遍。
require,加载文件的时候,不用带目录,有lua自己的搜索加载目录的路径,并且会判断文件是否加载过,加载过则不加载。
dofile与require都是会执行里面的代码,区别是require只加载一次,dofile每次加载,loadfile只加载文件而不执行。
在lua文件里面,推荐的是requrie加载文件。
四、LuaFramework
启动游戏:
加载Manager;
AppFacade.Instance.AddManager<LuaManager>(ManagerName.Lua);AppFacade.Instance.AddManager<PanelManager>(ManagerName.Panel);AppFacade.Instance.AddManager<SoundManager>(ManagerName.Sound);AppFacade.Instance.AddManager<TimerManager>(ManagerName.Timer);AppFacade.Instance.AddManager<NetworkManager>(ManagerName.Network);AppFacade.Instance.AddManager<ResourceManager>(ManagerName.Resource);AppFacade.Instance.AddManager<ThreadManager>(ManagerName.Thread);AppFacade.Instance.AddManager<ObjectPoolManager>(ManagerName.ObjectPool);AppFacade.Instance.AddManager<GameManager>(ManagerName.Game);
LuaManager:
--主入口函数。从这里开始lua逻辑function Main() print("logic start")--[LuaManager 开始执行Main.Lua--]end--场景切换通知function OnLevelWasLoaded(level) collectgarbage("collect") Time.timeSinceLevelLoad = 0end
collectgarbage(“collect”):运行一个完整的垃圾回收周期。
collectgarbage(“count”):返回当前程序使用的内存总量,以 KB 为单位。
collectgarbage(“restart”):如果垃圾回收器停止,则重新运行它。
collectgarbage(“setpause”):设置垃圾收集暂停时间变量的值,值由第二个参数指出(第二参数的值除以 100 后赋予变量)。稍后,我们将详细讨论它的用法。
collectgarbage(“setsetmul”):设置垃圾收集器步长倍增器的值,第二个参数的含义与上同。
collectgarbage(“step”):进行一次垃圾回收迭代。第二个参数值越大,一次迭代的时间越长;如果本次迭代是垃圾回收的最后一次迭代则此函数返回 true。
collectgarbage(“stop”):停止垃圾收集器运行。
GameManager:
LuaManager.DoFile("Logic/Game"); //加载游戏LuaManager.DoFile("Logic/Network"); //加载网络NetManager.OnInit(); //初始化网络Util.CallMethod("Game", "OnInitOK"); //初始化完成
Game.Lua
--初始化完成,发送链接服务器信息--function Game.OnInitOK() AppConst.SocketPort = 2012; AppConst.SocketAddress = "127.0.0.1"; networkMgr:SendConnect(); --注册LuaView-- this.InitViewPanels(); --测试第三方库功能-- this.test_class_func(); this.test_pblua_func(); this.test_cjson_func(); this.test_pbc_func(); this.test_lpeg_func(); this.test_sproto_func(); coroutine.start(this.test_coroutine); CtrlManager.Init(); --初始化CtrlManager-- local ctrl = CtrlManager.GetCtrl(CtrlNames.Prompt); --获取Prompt-- if ctrl ~= nil and AppConst.ExampleMode == 1 then ctrl:Awake(); --调用ctrl的Awake方法-- end logWarn('LuaFramework InitOK--->>>');end
相关引用在define.lua中:
CtrlNames = { Prompt = "PromptCtrl", Message = "MessageCtrl"}PanelNames = { "PromptPanel", "MessagePanel",}--协议类型--ProtocalType = { BINARY = 0, PB_LUA = 1, PBC = 2, SPROTO = 3,}--当前使用的协议类型--TestProtoType = ProtocalType.BINARY;Util = LuaFramework.Util;AppConst = LuaFramework.AppConst;LuaHelper = LuaFramework.LuaHelper;ByteBuffer = LuaFramework.ByteBuffer;resMgr = LuaHelper.GetResManager();panelMgr = LuaHelper.GetPanelManager();soundMgr = LuaHelper.GetSoundManager();networkMgr = LuaHelper.GetNetManager();WWW = UnityEngine.WWW;GameObject = UnityEngine.GameObject;
我们可以看到,networkMgr是通过LuaHelper注册的wrap文件执行LuaHelper中的方法获得的。
PanelManager:
IEnumerator StartCreatePanel(string name, LuaFunction func = null) { AssetBundle bundle = ResManager.LoadBundle(name); name += "Panel"; GameObject prefab = null;#if UNITY_5 prefab = bundle.LoadAsset(name, typeof(GameObject)) as GameObject;#else prefab = bundle.Load(name, typeof(GameObject)) as GameObject;#endif yield return new WaitForEndOfFrame(); if (Parent.FindChild(name) != null || prefab == null) { yield break; } GameObject go = Instantiate(prefab) as GameObject; go.name = name; go.layer = LayerMask.NameToLayer("Default"); go.transform.parent = Parent; go.transform.localScale = Vector3.one; go.transform.localPosition = Vector3.zero; yield return new WaitForEndOfFrame(); go.AddComponent<LuaBehaviour>().OnInit(bundle); //加上LuaBehaviour组件,执行对于模块的Awake,Start... if (func != null) func.Call(go); //完成加载并传递回Lua Debug.Log("StartCreatePanel------>>>>" + name); }
- [Unity 热更新]tolua原理及实践
- [Unity热更新]tolua# & LuaFramework(十一):实践
- [Unity 热更新]tolua集成
- [Unity热更新]tolua# & LuaFramework(一):基础
- [Unity热更新]tolua# & LuaFramework(一):基础
- [Unity热更新]tolua# & LuaFramework(一):基础
- [Unity热更新]tolua# & LuaFramework(八):更新下载(上)
- [Unity热更新]tolua# & LuaFramework(十四):更新下载(中)
- [Unity热更新]tolua# & LuaFramework(十五):更新下载(下)
- [Unity热更新]tolua# & LuaFramework(二):打包工具
- [Unity热更新]tolua# & LuaFramework(四):读取数据
- [Unity热更新]tolua# & LuaFramework(五):.proto转换为.lua
- [Unity热更新]tolua# & LuaFramework(六):网络通信
- [Unity热更新]tolua# & LuaFramework(七):lua使用DOTween
- [Unity热更新]tolua# & LuaFramework(九):网络通信实例
- [Unity热更新]tolua# & LuaFramework(十):扩展工具包
- [Unity热更新]tolua# & LuaFramework(十二):基础补充
- [Unity热更新]tolua# & LuaFramework(十三):导出apk
- Tomcat9.0安装
- 11.4(周六)
- C++ 运算符重载
- hello world
- JAVA项目中发布WebService服务——调用方式
- [Unity 热更新]tolua原理及实践
- 堆栈与动态分配内存空间
- java基础之IO(重点)
- tensorflow(4)
- instr函数
- 【NOIP模拟】 (11.3) T2 排列
- “小微”企业的集体“大”商标
- Alibaba开源Fastjson讲解和应用
- Rxjava和retorfit的混合使用