LuaFramework熱更新過程(及可更新的loading界面實現)

 
 
 
1.名詞解釋:
資源包:點擊 LuaFramework  |  Build XXX(平臺名) Resource,框架會自動將自定義指定的資源打包到StreamingAssets文件夾,這個文件夾下的unity3d文件就是資源包,它是一種u3d本身的壓縮格式,也被稱爲AssetBundle包。
資源:資源通過打包成爲資源包,若是在遊戲裏想用資源包裏的內容的話,就須要先加載資源包到內存,而後解壓這個資源包,獲取資源包裏包含的部分資源。

2.熱更新涉及文件夾

 

資源目錄:apk文件(安卓安裝文件)其實也是一種壓縮格式,安裝apk以後,安卓系統會爲遊戲自動建立一個資源目錄,用來存放apk解壓出來的東西。須要特別注意的一點是,資源目錄裏的東西是隻能讀不能寫的,也就是說,咱們不能隨意地從網上下載文件替換資源目錄裏的資源,因此咱們須要一個可讀可寫的目錄以便咱們用下載的資源替換原資源實現熱更新。因爲lua文件能夠看成一種資源,因此lua裏的代碼也是能夠熱更新的。資源目錄路徑經過Util.AppContentPath()獲取,安卓上大概是這樣的:jar:file///data/app.com/com.XXX.XXX-1/base.apk!/assets/(root以後才能看到),用編輯器的話,是工程所在文件夾下的StreamingAssets。
數據目錄:安卓建立的用來存放遊戲數據用的,能夠隨意讀寫。第一次打開遊戲的時候,把資源包從資源目錄複製到這裏,此後每次啓動遊戲都會比較這裏的文件和網絡上的資源包,若是不同的話,就下載下來替換這裏的資源包。數據目錄路徑經過Util.DataPath獲取,安卓上大概這樣data/user/0/com.XXX.XXX-1/files/mygamename/(root以後才能看到),用編輯器的話,會建立在C盤。

網絡資源目錄:存放遊戲資源的網址,遊戲開啓後,程序會從網絡資源地址下載一些更新的文件到數據目錄。版本控制是經過files.txt實現的,裏面存放着資源文件的名稱和md5碼。程序會先下載「網絡資源地址」上的files.txt,而後與「數據目錄」中文件的md5碼作比較,更新有變化的文件。
 

3.熱更新的三個過程:
複製資源:將「遊戲資源目錄」的內容複製到「數據目錄中」(只有在第一次打開遊戲的時候會有此過程)
GameManager.OnExtractResource
 1 IEnumerator OnExtractResource() {
 2             string dataPath = Util.DataPath;  //數據目錄
 3             string resPath = Util.AppContentPath(); //遊戲包資源目錄
 4 
 5             if (Directory.Exists(dataPath)) Directory.Delete(dataPath, true);
 6             Directory.CreateDirectory(dataPath);
 7 
 8             string infile = resPath + "files.txt";
 9             string outfile = dataPath + "files.txt";
10             if (File.Exists(outfile)) File.Delete(outfile);
11 
12             string message = "正在解包文件:>files.txt";
13             Debug.Log(infile);
14             Debug.Log(outfile);
15             if (Application.platform == RuntimePlatform.Android) {
16                 WWW www = new WWW(infile);
17                 yield return www;
18 
19                 if (www.isDone) {
20                     File.WriteAllBytes(outfile, www.bytes);
21                 }
22                 yield return 0;
23             } else File.Copy(infile, outfile, true);
24             yield return new WaitForEndOfFrame();
25 
26             //釋放全部文件到數據目錄
27             string[] files = File.ReadAllLines(outfile);
28             foreach (var file in files) {
29                 string[] fs = file.Split('|');
30                 infile = resPath + fs[0];  //
31                 outfile = dataPath + fs[0];
32 
33                 message = "正在解包文件:>" + fs[0];
34                 Debug.Log("正在解包文件:>" + infile);
35                 facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
36 
37                 string dir = Path.GetDirectoryName(outfile);
38                 if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
39 
40                 if (Application.platform == RuntimePlatform.Android) {
41                     WWW www = new WWW(infile);
42                     yield return www;
43 
44                     if (www.isDone) {
45                         File.WriteAllBytes(outfile, www.bytes);
46                     }
47                     yield return 0;
48                 } else {
49                     if (File.Exists(outfile)) {
50                         File.Delete(outfile);
51                     }
52                     File.Copy(infile, outfile, true);
53                 }
54                 yield return new WaitForEndOfFrame();
55             }
56             message = "解包完成!!!";
57             facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
58             yield return new WaitForSeconds(0.1f);
59 
60             message = string.Empty;
61             //釋放完成,開始啓動更新資源
62             StartCoroutine(OnUpdateResource());
63         }

 

下載資源:從網絡比對下載資源
GameManager.OnUpdateResource
 1 IEnumerator OnUpdateResource() {
 2             if (!AppConst.UpdateMode) {
 3                 OnResourceInited();
 4                 yield break;
 5             }
 6             string dataPath = Util.DataPath;  //數據目錄
 7             string url = AppConst.WebUrl;
 8             string message = string.Empty;
 9             string random = DateTime.Now.ToString("yyyymmddhhmmss");
10             string listUrl = url + "files.txt?v=" + random;
11             Debug.LogWarning("LoadUpdate---->>>" + listUrl);
12 
13             WWW www = new WWW(listUrl); yield return www;
14             if (www.error != null) {
15                 OnUpdateFailed(string.Empty);
16                 yield break;
17             }
18             if (!Directory.Exists(dataPath)) {
19                 Directory.CreateDirectory(dataPath);
20             }
21             File.WriteAllBytes(dataPath + "files.txt", www.bytes);
22             string filesText = www.text;
23             string[] files = filesText.Split('\n');
24 
25             for (int i = 0; i < files.Length; i++) {
26                 if (string.IsNullOrEmpty(files[i])) continue;
27                 string[] keyValue = files[i].Split('|');
28                 string f = keyValue[0];
29                 string localfile = (dataPath + f).Trim();
30                 string path = Path.GetDirectoryName(localfile);
31                 if (!Directory.Exists(path)) {
32                     Directory.CreateDirectory(path);
33                 }
34                 string fileUrl = url + f + "?v=" + random;
35                 bool canUpdate = !File.Exists(localfile);
36                 if (!canUpdate) {
37                     string remoteMd5 = keyValue[1].Trim();
38                     string localMd5 = Util.md5file(localfile);
39                     canUpdate = !remoteMd5.Equals(localMd5);
40                     if (canUpdate) File.Delete(localfile);
41                 }
42                 if (canUpdate) {   //本地缺乏文件
43                     Debug.Log(fileUrl);
44                     message = "downloading>>" + fileUrl;
45                     facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
46                     /*
47                     www = new WWW(fileUrl); yield return www;
48                     if (www.error != null) {
49                         OnUpdateFailed(path);   //
50                         yield break;
51                     }
52                     File.WriteAllBytes(localfile, www.bytes);
53                      */
54                     //這裏都是資源文件,用線程下載
55                     BeginDownload(fileUrl, localfile);
56                     while (!(IsDownOK(localfile))) { yield return new WaitForEndOfFrame(); }
57                 }
58             }
59             yield return new WaitForEndOfFrame();
60 
61             message = "更新完成!!";
62             facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
63 
64             OnResourceInited();
65         }

 

加載資源:加載資源包內的資源到內存
PanelManager.CreatePanel
 1     public void CreatePanel(string name, LuaFunction func = null) {
 2             string assetName = name + "Panel";
 3             string abName = name.ToLower() + AppConst.ExtName;
 4 
 5             ResManager.LoadPrefab(abName, assetName, delegate(UnityEngine.Object[] objs) {
 6                 if (objs.Length == 0) return;
 7                 // Get the asset.
 8                 GameObject prefab = objs[0] as GameObject;
 9 
10                 if (Parent.FindChild(name) != null || prefab == null) {
11                     return;
12                 }
13                 GameObject go = Instantiate(prefab) as GameObject;
14                 go.name = assetName;
15                 go.layer = LayerMask.NameToLayer("Default");
16                 go.transform.SetParent(Parent);
17                 go.transform.localScale = Vector3.one;
18                 go.transform.localPosition = Vector3.zero;
19                 go.AddComponent<LuaBehaviour>();
20 
21                 if (func != null) func.Call(go);
22                 Debug.LogWarning("CreatePanel::>> " + name + " " + prefab);
23             });
24         }

 

 
4.可更新的loading界面
前提:
1)若是想更新,則必須把loading界面打包進資源包
2)在第一次打開遊戲的時候,下載資源階段以前,尚未loading界面的資源包,可是此刻無疑是必須顯示loading的
3)生成loading界面的兩種過程:
使用Resource.Load(不可熱更新)
 1  public void CreateLoadingPanelFromResource()
 2         {
 3             _MemoryPoolManager_ memMgr = AppFacade.Instance.GetManager<_MemoryPoolManager_>(ManagerName._Pool_);
 4 
 5             Debug.Log("--------------------Resources加載loading--------------------------------");
 6 
 7             memMgr.AddPrefabPoolRS("LoadingPanel", 1, 1, true, null);
 8             //            memMgr.AddPrefabPoolAB ("loading", "LoadingPanel", 1, 1, true, null);
 9 
10             Transform loadingPanel = _MemoryPoolManager_.Spawn("LoadingPanel", true);
11             loadingPanel.SetParent(GameObject.Find("2DERoot/Resolution").transform);
12             loadingPanel.localPosition = Vector3.zero;
13             loadingPanel.localScale = new Vector3(1, 1, 1);
14 
15             _slider = loadingPanel.FindChild("LoadingSlider").GetComponent<Slider>();
16             _text = loadingPanel.FindChild("LoadingSlider/Text").GetComponent<Text>();
17             _text.gameObject.SetActive(true);
18             _text.transform.localPosition = Vector3.zero;
19             _slider.value = 5;
20             _text.text = "準備啓動遊戲";
21             _isStartupLoading = true;
22 
23             KeyFrameWork.MVC.SendEvent(FWConst.E_ShowLog, "Resources load finish");

 

使用加載AssetBundle資源包的方式
 1 public IEnumerator CreateLoadingPanelFromAssetBundle()
 2         {
 3             string url = Util.GetRelativePath() + "loading.unity3d";
 4             Debug.Log("--------------------AssetBundle加載Loading--------------------------------" + url);
 5 
 6             
 7             WWW www = WWW.LoadFromCacheOrDownload(url, 0, 0);
 8 
 9             yield return www;
10 
11             AssetBundle bundle = www.assetBundle;
12             GameObject asset = bundle.LoadAsset<GameObject>("LoadingPanel");
13             Transform loadingPanel = Instantiate<GameObject>(asset).GetComponent<Transform>();
14 
15             //memMgr.AddPrefabPoolRS("LoadingPanel", 1, 1, true, null);
16             //Transform loadingPanel = _MemoryPoolManager_.Spawn ("LoadingPanel", true);
17 
18             
19 
20             loadingPanel.SetParent(GameObject.Find("2DERoot/Resolution").transform);
21             loadingPanel.localPosition = Vector3.zero;
22             loadingPanel.localScale = new Vector3(1, 1, 1);
23             loadingPanel.name = "LoadingPanel(Clone)001";
24 
25 
26             _slider = loadingPanel.FindChild("LoadingSlider").GetComponent<Slider>();
27             _text = loadingPanel.FindChild("LoadingSlider/Text").GetComponent<Text>();
28             _text.gameObject.SetActive(true);
29             _slider.value = 5;
30 
31             _isStartupLoading = true;
32 
33             //加載loading完成後再開始lua,保證lua能順利接管loading
34             AppFacade.Instance.StartUp();            //啓動遊戲
35 
36             KeyFrameWork.MVC.SendEvent(FWConst.E_ShowLog, "AssetBundle load finish");
37 
38         }

 

在遊戲開始的時候使用判斷(Directory.Exists(Util.DataPath) && Directory.Exists(Util.DataPath + "lua/") && File.Exists(Util.DataPath + "files.txt")來肯定當前是不是第一次開啓遊戲,若是是,則使用Resource.Load,若是不是則使用加載AssetBundle資源包的方式。這裏須要着重說下加載AssetBundle資源包時的順序問題,因爲ResourceManager須要Manifest關聯文件(用以肯定各個資源包之間的引用關係)進行初始化,因此ResourceManager初始化必在下載資源以後,而下載資源時無疑是須要顯示loading的,故順序是:加載AssetBundle資源包-》下載資源-》ResourceManager初始化。在這個順序下,雖然ResourceManager有加載AssetBundle資源包的接口,可是不能用。而加載AssetBundle資源包是須要異步加載的,也就是說,顯示loading須要1秒左右的時間(根據具體loading資源和手機硬件而定),如果須要在lua裏接管loading(經過Find函數找到loading界面而後在lua裏作一個引用),則必須在loading資源包加載完畢後才能執行lua接管loading的代碼
相關文章
相關標籤/搜索