Ulua热更新提高 Ulua_SimpleFramework框架流程详解

以前写过几篇关于热更新的文章,但是我一直没有深入研究,就是公司用什么技术,我就根据公司的框架写代码。这回刚好在家闲着,我打算写一个系列的文章,深入研究一下Uua的热更新。最近几天有2家公司挖我去做游戏,开门问我第一句都是热更新框架你能不能搭建起来,cocos做2D有LUA是不是比U3D强,搞的我腰板也是不太硬,都是因为热更新懂的不是很彻底,工资都没有到20K,虽然自信自己的学习能力,很快就能研究完,但是还是要从头搞一遍才行。


目前主流的ULua有两套框架,一个是SimpleFramework_UGUI ,16年初以后不维护了。一个是LuaFramework_UGUI。本来是当算学一下LuaFramework_UGUI毕竟是原作者的新东西。但是看一下,竟然打包资源还要手动加代码,再加上商用估计都还是老的比较多,我决定还是学习SimpleFramework_UGUI ,官方教程:http://doc.ulua.org/default.asp?cateID=4 因为接下来的项目需要短时间出东西,当然是用最成熟最方便的,以后可能会用LuaFramework_UGUI,如果需要学LuaFramework_UGUI,可以看这系列博客:https://www.zhihu.com/people/pyluo/posts


首先:需要了解这些知识点,编辑器扩展,Lua脚本语言,Unity5的AsstBundle打包,Lua和C#通信原理。我打算先上手直接做一遍流程再深入研究。我个人不喜欢一上来就是原理,这个那个的,咱们学东西就是要快速上手能用,用顺手了再去深挖!


我这里都是以热更新UI界面或者2D游戏为例子。我理解的ULUA热更新是这样的顺序:先导入SimpleFramework_UGUI框架到项目,登录界面一个Assetbundle包,大厅一个包,用户信息一个包,排行榜一个包,类型这种模块化的包,需要更新那个地方就更新哪一块,StreamingAssets文件夹下有个files文件是包的版本信息。我们打包了一个APK出去以后,如果有更新的模块,就放到服务器上,通过服务器下载远端的files和本地的files做比对,哪些资源包不一样,就会自动下载下来,覆盖原有的包,第一次打开游戏都是需要解包的,就是把StreamingAssets包的资源拷贝到你真机的持久化储存的地方,第二次就不需要了,直接读取。


我的U3D版本是Unity 5.3.1,SimpleFramework_UGUI版本是16年1月份的,Lua开发工具sublime。


首先,需要注意的是:

1.如果在Lua里创建的界面没有指定父物体,将会创建到Tag为GuiCamera的物体下。具体看PanelManager.cs这个类。

2.如果你修改了CS类,你要Lua菜单---Gen Lua Wrap Files,生成的文件在uLua/Source/LuaWrap文件夹,Lua菜单----Clean Wrap...可以清空这个文件夹。(生成的文件可以让Lua脚本去调用C#类)

3.修改和创建了LUA脚本就要Build Resource  (如果把AppConst类中的 DebugMode = true; 设置为调试模式,就不会读取本地保存的Lua,而且是读取工程中的Lua。也就是可以一边修改一边看到修改后的内容,而不用Build,但是Prefab资源修改了就一定要Build)


制作Lua资源:就是修改3个Lua脚本(define,GameManager,CtrlManager,)和添加2个Lua脚本(Ctrl和Panle)和资源制作(比如名字叫LoginPanel,资源包叫Login.assetBundle)


第一步,打包成AssetBundle

1.比如你做好了一个登录界面,先把该Panel界面做成prefab,注意prefab命名为  xxxxxPanel (原因是PanelManager.cs 这个类里OnCreatePanel函数要求这么写,当然Scripts/Manager还有其他的管理器,有其他资源的加载方式。)

2.在右下角把该Panel的AssetBundle 命名为  xxxxx (不要Panle),后缀为 AssetBundle

3.Game菜单----build  Windows Resource     会在StreamingAssets文件夹下生成的lua代码和资源包,正式项目打包最好先删除这个文件夹再Build


注意:如果你有Json,或者其他文本形式的文件想打包,直接放在StreamingAssets下,会打包进文件目录files.txt


第二步,用lua创建UI面板

1.新建一个物体叫GlobalGenerator,给他一个GlobalGenerator脚本,这个是Lua入口,再往下看GameManager类的OnResourceInited()方法,他会去调用GameManager.lua脚本里的LuaScriptPanel函数(CallMethod("LuaScriptPanel")  ),如果返回 xxxxx,就回去执行 Lua/view/xxxxxPanel脚本。


2.接着会执行GameManager.Lua里的GameManager.OnInitOK()方法,会执行CtrlManager.Init(); 在CtrlManager.Lua脚本   require "Controller/xxxxxCtrl"然后再Init函数中加上  ctrlList[CtrlName.xxxxx] = xxxxxCtrl.New(); 

3.在Define.Lua脚本中的CtrlName表中加入  xxxxx="xxxxxCtrl"


4.添加一个UI模块,需要添加上面说到的有两个Lua脚本,一个是Lua \ View \ xxxxxPanel  ,一个是Lua \ Controller \ xxxxxCtrl。 这两个脚本等会再说,可以先仿照官方的例子来写。


5.GameManager.Lua脚本在CtrlManager.Init();执行完后,会通过得到的Ctr脚本,执行ctrl:Awake();  就是一句创建面板  PanelManager:CreatePanel('xxxxx', this.OnCreate);  前面一个参数是AssetBundle的包名(其实也是Prefab的名字,会自动加一个"Panel",在PanelManager.cs的CreatePanel函数中可以去看),后面一个参数是回调。  例子是是回调到LogCtrl.OnCreate(obj)这个函数,Obj是什么东西呢,其实就是创建的物体(在PanelManager.cs类里的CreatePanel函数,func.Call(Go);)  创建的这个物体会先加载本地持久化目录的资源,然后创建一个UI物体,并给它挂载一个LuaBehaviour组件。LuaBehaviour里面就可以让xxxxxPanel.Lua脚本执行U3D里的Awake,Start函数,以及AddClick添加点击事件等。


6. 编写Ctrl和Panel的Lua文件

正式项目中,应该写一个LayerPanel,下面有前中后三层,就可以给生产的Panel指定父物体来,来改变显示的前后顺序。


Panel文件只负责UI界面的引用。

his.Btn_Ok = transform:FindChild("Btn_Ok").gameObject;


Ctrl文件的代码应该这么写(负责点击事件,创建关闭面板等):

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. LoginCtrl = {};  
  2. local this = LoginCtrl;  
  3.   
  4. local Login;  
  5. local transform;  
  6. local gameObject;  
  7.   
  8. --构建函数--  
  9. function LoginCtrl.New()  
  10.     return this;  
  11. end  
  12.   
  13. function LoginCtrl.Awake()  
  14.     PanelManager:CreatePanel('Login', this.OnCreate);  
  15. end  
  16.   
  17. --启动事件--  
  18. function LoginCtrl.OnCreate(obj)  
  19.     gameObject = obj;  
  20.     transform = obj.transform;  
  21.   
  22.     Login = transform:GetComponent('LuaBehaviour');  
  23.     Login:AddClick(LoginPanel.gameObject, this.OnClick);  --添加点击事件  
  24. end  
  25.   
  26. --单击事件--  
  27. function LoginCtrl.OnClick(go)   
  28.     logError("你点击了!"); this.Close();  
  29. end  


Panel里负责面板的引用

[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. --启动事件--  
  2. function GameFruitPanel.Awake(obj)  
  3.     gameObject = obj;  
  4.     transform = obj.transform;  
  5.   
  6.     transform:SetParent(LayerPanel.Layer_B);  --设置父物体  
  7.   
  8.     local  Rect = gameObject:GetComponent("RectTransform"); --设置位置,大小  
  9.     Rect.localPosition=Vector3.zero;        --Vector3是框架给我写好的,在Lua / System里,其实就是一个表。  
  10.     Rect.sizeDelta=Vector3.one;  
  11.   
  12.     this.InitPanel();  
  13.   
  14. end  



另外,调用Update()  

添加UpdateBeat:Add(PromptCtrl.Update)      移除UpdateBeat:Remove(PromptCtrl.Update)  具体还没研究,只知道这么用,用完要移除掉,很耗性能。

为什么要分开写,为什么要一个UI界面写2个Lua, 是因为Ulua的框架结构是PureMVC基于模型、视图和控制器的三层MVC模式,我理解的是:这里xxxxxPanel.lua是界面展示和引用UI元素,xxxxxCtrl.lua就是转发业务请求(这里就是点击事件,开关面板) LuaBehaviour就是业务处理层。


7.添加自定义组件。

打开 Ulua  /  Editor / WrapFile.cs文件, 添加: _GT(  typeof(类名)  ) , 然后打开菜单Lua---Gen  lua Wrap Files,生成该C#类对应的Warp文件,有了这个文件才能给Lua脚本使用,生成的Wrap文件在uLua  / Source/  LuaWrap里。



8.生成EXE或者APK。需要注意一下几点:

1.生成哪个平台,Game菜单下选择哪个平台的资源

2.选择生成的平台,PC要选择PC--- Architecture:  X86_64 ,如果选择X86会找不到Ulua.DLL

3.生成选项:Development build ,会在游戏中打印日志信息,并且保存在一个 log.txt里。

   生成选项:Autoconnect profiler,自动连接分析器,开启U3D性能分析工具的功能。(点Build And Run)

   生成选项:Script debugging,开启脚本调试


在生成导出的时候,还报错: The nested type `AdvertisingIdentifierCallback' does not exist in the type `UnityEngine.Application'

这个问题我搞了好久,问了蛮多人,最后的解决方法:直接用U3D打开SimpleFramework_UGUI解压的工程,就不会报错,如果是Uua导入你原先的项目就会报错,有可能是我导入的时候少放了东西。

如果,报错:Push table failed  就是GameManager.LuaScriptPanel忘了添加了。

如果,编辑器没问题,但是到真机上报错:Loader lua filed System.Global,在AppConst设置调试模式DebugMode=false


[plain] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. --关闭事件--  
  2. function LoginCtrl.Close()  
  3.     PanelManager:ClosePanel(CtrlName.Login);  
  4. end  

Ctrl文件里的,我发现PanelManager.cs里没有ClosePanel这个方法,需要我们自己写

[csharp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /// <summary>  
  2. /// 关闭面板  
  3. /// </summary>  
  4. /// <param name="name"></param>  
  5. public void ClosePanel(string name){  
  6.     var panelName = name + "Panel";  
  7.  // var panelObj = Parent.FindChild  (panelName);    
  8.     var panelObj =  GameObject.Find(panelName);  
  9.     if (panelObj == nullreturn;  
  10.     Destroy(panelObj.gameObject);  
  11. }  
展开阅读全文

没有更多推荐了,返回首页