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
首先:需要了解這些知識點,編輯器擴充套件,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檔案的程式碼應該這麼寫(負責點選事件,建立關閉面板等):
LoginCtrl = {};
local this = LoginCtrl;
local Login;
local transform;
local gameObject;
--構建函式--
function LoginCtrl.New()
return this;
end
function LoginCtrl.Awake()
PanelManager:CreatePanel('Login', this.OnCreate);
end
--啟動事件--
function LoginCtrl.OnCreate(obj)
gameObject = obj;
transform = obj.transform;
Login = transform:GetComponent('LuaBehaviour');
Login:AddClick(LoginPanel.gameObject, this.OnClick); --新增點選事件
end
--單擊事件--
function LoginCtrl.OnClick(go)
logError("你點選了!"); this.Close();
end
Panel裡負責面板的引用
--啟動事件--
function GameFruitPanel.Awake(obj)
gameObject = obj;
transform = obj.transform;
transform:SetParent(LayerPanel.Layer_B); --設定父物體
local Rect = gameObject:GetComponent("RectTransform"); --設定位置,大小
Rect.localPosition=Vector3.zero; --Vector3是框架給我寫好的,在Lua / System裡,其實就是一個表。
Rect.sizeDelta=Vector3.one;
this.InitPanel();
end
另外,呼叫Update()
新增UpdateBeat:Add(PromptCtrl.Update) 移除UpdateBeat:Remove(PromptCtrl.Update) 具體還沒研究,只知道這麼用,用完要移除掉,很耗效能。
為什麼要分開寫,為什麼要一個UI介面寫2個Lua, 是因為Ulua的框架結構是PureMVC,基於模型、檢視和控制器的三層MVC模式,這裡xxxxxPanel.lua是介面展示和引用UI元素,也就是檢視層,xxxxxCtrl.lua就是業務邏輯層,像define.lua就是資料層。當然你可以每個面板搞一個數據層存一下,我感覺沒什麼必要,只存全域性資料就行了。
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
--關閉事件--
function LoginCtrl.Close()
PanelManager:ClosePanel(CtrlName.Login);
end
Ctrl檔案裡的,我發現PanelManager.cs裡沒有ClosePanel這個方法,需要我們自己寫
/// <summary>
/// 關閉面板
/// </summary>
/// <param name="name"></param>
public void ClosePanel(string name){
var panelName = name + "Panel";
// var panelObj = Parent.FindChild (panelName);
var panelObj = GameObject.Find(panelName);
if (panelObj == null) return;
Destroy(panelObj.gameObject);
}