ULUA简介:
ULUA:Unity AssetStore 中一款热更新插件Lua Framework 的简称,2015年发布v1.1版本并且停止了更新,但是由于国内手游这几年的飞速发展和对热更新的大量需求,LuaFramework由国内一个社群
(http://www.ulua.org/index.html) 在继续进行维护。并且在原版基础上添加了cstolua去反射机制,大大的提上了ulua的效率,并且给我们提供了简单易学的框架demo。真的感谢这些大牛们。
最新下载地址可以去社区群主的GitHub下载;
LuaFramework_UGUI:(https://github.com/jarjin/LuaFramework_UGUI)
LuaFramework_NGUI:(https://github.com/jarjin/LuaFramework_NGUI)
ulua和slua框架的性能测试对比
slua成绩:
属性的读取和设置 cost time:0.27200000000001
成员方法的调用 cost time:0.099999999999994
静态方法调用 cost time: 0.179
纯属性设置 cost time: 0.09899999999999
构造valuetype i,i,i 20万次 cost time: 0.13799999999999
构造valuetype i,i,i 200万次 cost time: 1.387
构造valuetype 1,1,1 20万次 cost time: 0.131
构造valuetype 1,1,1 200万次 cost time: 1.313
cstolua成绩:
属性的读取和设置20万次 cost time:0.17700000000002
成员方法的调用20万次 cost time:0
静态方法调用20万次 cost time: 0
纯属性设置20万次 cost time: 0.08499999999998
构造valuetype i,i,i 20万次 cost time: 0.027999999999963
构造valuetype i,i,i 200万次 cost time: 0.26600000000002
构造valuetype 1,1,1 20万次 cost time: 0
构造valuetype 1,1,1 200万次 cost time: 0.0020000000000095
ulua/cstolua全面领先slua,这才是真实数据!
LuaFramework文件结构
Editor:生成_Wrap文件和Build xxxResource的编辑器工具。
Examples :框架自带的Demo例子,如果只需要框架的同学,里面的资源可以删除掉。
Builds:里面是需要热更的Prefab资源。用于生成assetbundle而准备的资源。
Editor:里面是例子用到的一个新手引导步骤演示的编辑器脚本。
Editor Default Resource:目录是新手引导步骤对话框用的的图片资源。
Textures:里面是Buidls目录里面图集的原图文件。Lua:框架自带的Lua源码目录,用户自定义的Lua脚本也就是放在这里面,最后打包的时候,打包脚本会将其按目录结构生成到StreaminAssets目录里面去,然后在将其上传到游戏的Web服务器上面,用于准备被每个游戏客户端下载更新他们本地的Lua脚本。达到热更目的。
3rd:里面是第三方的一些插件lua、实例源码文件,比如:cjson、pbc、pblua、sproto等。
Common:公用的lua文件目录,如define.lua文件,一些变量声明,全局配置等,functions.lua常用函数库,通讯的protocal.lua协议文件。
Controller:控制器目录,它不依赖于某一个Lua面板,它是独立存活在Luavm中的一个操作类,操作数据、控制面板显示而已。
Logic:目录里面存放的是一些管理器类,比如GameManager游戏管理器、NetworkManager网络管理器,如果你有新的管理器可以放到里面。
View:这是面板的视图层,里面都是一些被Unity调用的面板的变量,走的是Unity GameObject的生命周期的事件调用。Plugins:ulua底层库所在的目录,里面存放的是不同平台的底层库,之所以ulua效率高,就是它是纯c的lua虚拟机,而不是c#解释型的。
Andriod:安卓lua虚拟机底层库,里面分为armv7-a与Intel x86平台。
iOS:里面就是苹果lua虚拟机底层库。
ulua.bundle:里面是Mac机器的底层库。
x86:里面是Win32/Linux32位机器的lua虚拟机底层库。
x86_64:里面是Win64/Linux64位机器的lua虚拟机底层库。Scripts:框架的C#脚本层,之所以这个目录跟lua目录都放在最外层,为了让用户一眼都能找到,明白是什么。
Common:框架的公用定义类。BehaviourBase(行为基类)、GlobalGenerator全局构造器、与luavm通知unity游戏对象的“LuaBehaviour”桥类。
ConstDefine:常量定义目录,AppConst(应用常量)ManagerName(管理器名称)NotiConst(通知常量,用于mvc消息通知)。
Controller:控制器目录,分为Boostraps启动控制器,跟Command常用逻辑控制器。
Framework:经过修改过的PureMVC的框架文件。
Manager:Unity提供基础功能的管理器类,音乐、面板、线程、资源等众多管理器。
Network:网络的常用辅助类,ByteBuffer字节操作封装类,网络协议类,转换器类。
ObjectPool:对象池工具。
Utility:常用工具类。
View:C#用的PureMVC的视图层。Scenes:框架自带demo的场景。
uLua:ulua/cstolua的核心目录,里面还有经过我们修缮后ulua的基础使用例子,用户初学者最佳。
Core:顾名思义,ulua的核心目录,所有c#与lua的交互都是通过它进行调度的。
Docs:ulua自带的文档目录,因为ulua基于LuaInterface,所以这里面就是它的文档。
Editor:这是供cstolua去反射定义Wrap文件列表的工具类目录。
Examples:经过我们修改增加后ulua自带的例子。
Source:这个是cstolua的核心目录,里面有Base核心目录,与动态生成用于存放LuaWrap类的缓存目录。
框架demo运行流程
1.打开LuaFramework/Scenes下的main场景。
2.找到GameManager游戏对象,身上运行着一个Main.cs脚本,通过
AppFacade.Instance.StartUp() ,启动PureMVC框架。“框架”主要指以AppFacade为核心的一个消息分发控制体系。3.在StartUp方法中发送了一个命令给Model层的StartUpCommand,在GameManager游戏对象身上有添加了N多个管理器,供C#和Lua层去调用,包括LuaManager(Lua管理器),NetworkManager(网络管理器),PanelManager(页面管理器),ResourcesManager(资源管理器),SoundManager声音管理器,GameManager(热更新/游戏管理器)等管理器类。
4.GameManager组件在运行后,进行了资源的热更新检测逻辑,并加载lua脚本。
解压:根据需求,从streamingAssetsPath目录(游戏数据,可读)拷贝到persistentDataPath目录(游戏沙盒,可读可写)。
更新:根据files.txt文件,从服务器WebUrl下载文件更新到persistentDataPath目录。
加载lua:从persistentDataPath目录中加载lua脚本,并进入Game.lua的逻辑。5.GameManager在解压和更新完后,执行OnResourceInited,执行初始化资源管理器ResourceManager,加载AssetBundle的环境,因为所有的prefab资源和lua资源都在bundle中。
6.GameManager加载完资源管理器后,加载OnInitialize方法,初始LuaManager,然后加载第一个lua的脚本Game.lua,和Network.lua,并且执行Game的初始化函数OnInitOK。
7.Game.lua脚本执行OnInitOK函数,加载例子的messagePanel.lua和promptPanel.lua脚本,这两个UI界面,分别被拆分成两个脚本,一个Panel和一个Ctrl共同控制页面,实现了MVC的分层。
8.创建页面使用PanelManager页面管理器,
public void CreatePanel(string name, LuaFunction func = null)9.加载预制体使用ResourcesManager资源管理器,
public void LoadPrefab(string abName, string[] assetNames, LuaFunction func)10.播放声音使用SoundManager声音管理器,
public void PlayBacksound(string name, bool canPlay)11.点击button按钮发送网络事件,使用了NetworkManager网络管理器,
public void SendMessage(ByteBuffer buffer)
PureMVC框架
框架配置常量
AppConst
1.DebugMode
对于LuaManager来说,如果值为true,那么lua代码的加载路径就为"LuaFramework/lua/"和"LuaFramework/Tolua/Lua/"这两个路径(也就是pc上的本地路径),否则为Util.DataPath + "lua"(也就是手机上的沙盒路径)2.ExampleMode
如果AppConst.ExampleMode为true,那么就会对"Assets/LuaFramework/Examples/Builds/"这个路径下的素材进行打包,打包到StreamingAssets这个目录下。这里我建议不要修改这个值(保持为true),通过系列二的打包工具将素材的路径改了就可以了。3.UpdateMode
如果为true,则会访问服务端进行下载更新4.LuaBundleMode
如果AppConst.LuaBundleMode为true,那么就会对"LuaFramework/lua/"和"LuaFramework/Tolua/Lua/"这两个路径下的lua代码进行打包成 .Unity3D,打包到StreamingAssets/lua/这个目录下;否则,就是将这两个路径的文件复制到目标目录下。如果要导出apk,那么要将这个值改为true;如果只是在编辑器上测试,可以改为false。5.LuaEncode
我们打开ulua/Editor/Packager.cs打包脚本。这里面根据不同的平台打包相应的资源。HandleExampleBundle函数就是将Examples/Builds下面的素材统一按照Unity的规则进行打包成assetbundle文件。 我们主要是看HandleLuaFile函数的操作流程。
(1)在StreamingAssets目录下面新建lua目录,用于存放编码后的lua文件。
(2)遍历Lua目录下面所有的lua文件,并且根据目录结构创建相应目录树。
(3)如果AppConst.LuaEncode 设置了编码开关,就启动编码操作,否则直接复制源码过去。
(4)然后将前面的图片素材assetbundle跟相对应的lua文件目录,统一遍历计算出MD5/CRC,生成到files.txt里面。
ulua脚本的声明周期
每一个panel页面默认都会被绑定一个LuaBehaviour脚本,用于直接调用panel同名的一个lua脚本,这样每个panel页面的lua脚本就会拥有Awake,Start,OnDestroy等相对应的生命周期。并且还可以通过LuaBehaviour帮助页面里面的按钮进行绑定按钮等的点击事件。
function xxxPanel.Awake(go)
this.gameObject=go;
this.transform=go.transform;
this.behavior=this.transform:GetComponent('LuaBehaviour');
--Button绑定事件
this.loginBtn = this.transform:Find("LoginBtn").gameObject;
this.behavior:AddClick(this.loginBtn,this.OnClick);
end
function xxxPanel.OnClick()
log('点击事件');
end
function xxxPanel.Start()
end
function xxxPanel.OnDestroy()
end
ulua的Update方法
刚才我们说了LuaBehaviour可以给同名的lua脚本添加一个生命周期,但是不建议添加Update,因为大量的C#调用Lua脚本会造成性能瓶颈。出于效率的考虑,tolua提供了名为UpdateBeat的对象,在LuaFramework中,只需给UpdateBeat添加回调函数,该函数便会每帧执行,相当于Monobehaviour的Update方法。
function xxxPanel.Start()
--开启Lua的Update
UpdateBeat:Add(this.Update)
end
--Update方式
function xxxPanel.Update()
this.image.transform:Rotate(Vector3.forward,20);
end
function xxxPanel.OnDestroy()
UpdateBeat:Remove(this.Update);
end
ulua的协程方法
lua语法本身有协程,但是我们希望在lua中使用像unity的协程,可以方便的延迟,下载等,于是tolua为我们提供了ulua协程。
function xxxPanel.Start()
--开启协程
this.co = coroutine.start(this.test_coroutine);
end
function xxxPanel.test_coroutine()
coroutine.wait(1);--延迟一秒
coroutine.step();--延迟一帧
local www = WWW("http://bbs.ulua.org/readme.txt");
coroutine.www(www);
logWarn(www.text);
end
function xxxPanel.OnDestroy()
--销毁协程
coroutine.stop(this.co);
end