最近在研究Unity中關於資源打包的東西,網上看了一堆資料,這里做個整合,說整合,其實也就是Ctrl-C + Ctrl-V,不是原創
首先為了尊重原創,先貼出原創者的文章地址:
http://blog.csdn.net/kenkao/article/details/24290063
http://blog.csdn.net/janeky/article/details/17652021
http://blog.csdn.net/janeky/article/details/17666409
http://blog.csdn.net/janeky/article/details/25923151
本文原創版權歸 csdn ken老 所有,轉載請詳細注明原創作者及出處,以示尊重!
作者:ken老
原文:http://blog.csdn.net/janeky/article/details/17652021
如果這篇文章對你有幫助,敬請關注作者《Unity手游之路》系列教程。
在手游的運營過程中,更新資源是比不可少的。資源管理第一步是資源打包。傳統的打包可以將所有物件制成預設Prefab,打包成場景。今天我們來一起學習官方推薦的Assetbundle,它是Unity(Pro)提供的資源打包策略。利用AssetBundle,可以將幾乎所有的資源都打包封裝,便於客戶端更新下載新的資源。
(轉載請注明原文出處http://blog.csdn.net/janeky/article/details/17652021)
- 創建AssetBundle
1.創建一個空的Prefab,命名Cube,然后創建一個Cube,將其拉到剛創建好的Prefab
2.新建一個腳本ExportAssetBundles.cs(代碼來自官方文檔),保存在Asset/Editor目錄下
5.選中該場景,在之前的ExportAssetBundles.cs腳本中添加打包場景的函數,運行Assets->Build Scene,保存為scene1.unity3d(這里為了測試方便,也放在c盤)
注意事項
a.AssetBundle的保存后綴名可以是assetbundle或者unity3d
b.BuildAssetBundle要根據不同的平台單獨打包,BuildTarget參數指定平台,如果不指定,默認的webplayer
- 加載AssetBundle
我們通過一個簡單的代碼來演示如何加載assetbundle,包括加載普通asset和場景。
注意事項
a.LoadFromCacheOrDownload 可以指定版本,如果本地版本是新的,將不會從服務器讀取
b.如果是多個資源打包在一起,我們要通過bundle.Load(),加載特定的資源
c.掛載在模型上的腳本也可以一起打包,但是保證腳本在原目錄也要存在,否則加載出來無法運行。關於如何更新腳本,我將放在以后的章節中闡述。
- AssetBundle依賴關系
如果一個公共對象被多個對象依賴,我們打包的時候,可以有兩種選取。一種是比較省事的,就是將這個公共對象打包到每個對象中。這樣會有很多弊端:內存被浪費了;加入公共對象改變了,每個依賴對象都得重新打包。AssetBundle提供了依賴關系打包。我們通過一個簡單的例子來學習
- 總結
這一節的內容偏實際操作,官方文檔和雨松的blog中已經有更加詳細介紹。如果大家還有不明白的地方,可以結合文檔再實際操作一下。后面的章節,我將花更多的時間介紹核心的內容:資源的增量更新,和代碼程序的更新。
- 源碼
http://pan.baidu.com/s/1i3BOAPn
- 參考資料
1.http://www.xuanyusong.com/
2.http://game.ceeger.com/Manual/DownloadingAssetBundles.html
來源:http://www.im286.com/thread-11924518-1-1.html
Assetbundle 是Unity Pro提供提供的功能,它可以把多個游戲對象或者資源二進制文件封裝到Assetbundle中,提供了封裝與解包的方法使用起來很便利。
1.預設
Assetbundle可以將Prefab封裝起來,這是多么方便啊! 而且我也強烈建議大家將Prefab封裝成Assetbundle,因為Prefab可以將游戲對象身上帶的游戲游戲組件、游戲腳本、材質都封裝在一起。當從服務器上將Assetbundle下載以后直接Instantiate就可以放入游戲中。
試想一下,如果只能將原始的二進制資源文件放在服務器上下載,當資源文件下載完畢后,需要動態的創建游戲對象、然后動態的將腳本綁定在游戲對象、動態的將貼圖賦予游戲對象等等各種動態的操作。。所以強烈建議使用Prefa,不解釋!!!!!
另外,我在舉個例子,因為模型有可能會帶很多動畫文件,那么這樣一組模型資源就可能是多個FBX 文件 和 若干png貼圖文件 材質文件。這時我只需要把原始模型放入Prefab中,它就會包含這個模型的所有組件、甚至包括它的動畫資源、貼圖。那么如下圖所示,Mode就是模型的Prefab文件,那么我僅僅只需要把Mode這個預設打包成Assetbundle即可。 當我在服務器上下載這個Assetbundle並且載入游戲中就可以直接使用了,切換動畫、換貼圖都可以。。
2.二進制文件
也並不是Assetbundle中全都要用預設,Assetbundle它也可以將二進制文件直接封裝在里面,比如圖片、聲音、文本信息等等。
3.場景文件
在Unity中可以將一個場景保存在Scene中,Scene就會包含這個場景中的所有,那能不能把Scene也封裝成Assetbundle中?答案是能,但是它不能在移動平台上用,因為移動平台上是不能更新腳本的,換句話來說就是即使將腳本綁定在Prefab中,然后下載Assetbundle后,所有腳本是不會執行的,后面說另外一種巧妙用法。
4.移動平台
上面MOMO已經將Assetbundle 的使用原理大致介紹了一下 ,我們在談談移動平台。腳本不能更新是移動平台下最大的傷,這就意味着開發者無法繞過App store和 google Play這種在線商店升級應用程序。唯一能做到的就是更新資源、舉個例子,游戲中在處理版本升級時,一般會有個大版本號和一個小版本號,大版本號就是 2.0、3.0這種 版本需要在AppStore中更新,大版本主要是升級游戲腳本,然后當小版本號,比如2.0.1 或2.0.2這種只是更新游戲中的資源,通過自己游戲的服務器就可以完成,通過Assetbundle在自己服務器上下載,然后適應在游戲中。如果非要更新腳本,或不得不更新腳本那么只能在Appstore或者google Play去更新大版本。
移動平台上不能更新腳本,那么Prefab上綁定的腳本怎么辦?在任何平台上都可以把腳本添加到Prefab上,然后打包成Assetbundle,只有移動平台上有點特殊,比如將Test.cs這條腳本綁定在Prefab中,最后程序通過服務器下載這個Assetbundle ,當載入工程中這條腳本是不會被執行的。
但是如果本地工程有Test.cs這條腳本,那么Unity會自動將這條腳本綁定在下載的Prefab中,並且他們執行的非常好。如果本地工程中沒有Test.cs這條腳本,那么Prefab上的腳本是永遠都不會執行的。有時我們會在腳本中寫一些Public的變量,有可能不同的Prefab上綁定的是相同的腳本,只是Inspector 腳本中的public參數不同。別擔心這一點Assetbundle 中的Prefab也是沒問題,所以說只要大版本中的腳本沒問題,在小版本中只更新游戲資源是一點問題都么有的。
5.移動優化
之前我們說過可以將游戲中的某個游戲對象封裝成Assetbundle,也可以將游戲中的整個場景也封裝成Assetbundle。但是我認為需要巧妙的使用封裝場景,因為場景中肯定有很多公用的模型,如果打包場景的話那么內存與size就是 公用模型的size * N個場景,想想其實挺恐怖的。其實我們可以巧妙的使用,首先把場景中公用的部分和私有的部分統統放入Unity, 然后烘培整個場景。 當場景烘培完畢后把公用的模型部分在拿出去,場景只只保留私有的模型。還可以做一個工具將公用模型在場景中的坐標保存在XML中(每個場景文件會對應一個公用模型的XML信息),最后在將公用的模型分別封裝在別的Assetbundle中。
服務器上提供每個場景的Assetbundle ,和公用模型的Assetbundle,一般公用模型的Assetbundle可以放在常駐內存中(可能使用頻繁、根據項目的不同而定)場景Assetbundle下載完畢后,現載入場景然后在根據場景對應的XML信息將公用模型部分動態的在添加到場景中,這樣就完成了一個場景的構建。
6.總結
對游戲中所有資源進行打包,比如按類型分為五個大部分 界面,模型,特效,聲音,場景,腳本。
界面部分:
公用資源包(可復用的資源包)和 每個界面獨有得資源包(不可復用的資源包)統一使用Prefab 打包成.assetbundle 二進制格式。
模型部分:
按角色分類,統一使用Prefab 打包成.assetbundle 二進制格式。 模型部分包括模型文件與動畫文件,每一個模型文件對應一組動畫文件。(如果模型需要換裝還需提供對應換裝的模型與貼圖) ,因為unity4的重定向動畫不支持動態加載,所以目前不需要考慮 不同大小 不同規格 不同性別 的模型重定向動畫。
特效部分: 統一使用Prefab 打包成.assetbundle 二進制格式。
聲音部分: 統一使用Prefab 打包成.assetbundle 二進制格式。
場景部分:場景和前面的有點區別,場景需要導出烘培的光信息並且只能烘培場景之上永遠不動的模型,但是這些永遠不動的模型有可能會同時在多個場景中使用,所以場景烘培完畢后要把重復使用的對象刪除,(運行游戲在動態的加載進來)場景中只保留該場景中永遠不會變的模型,以及烘培的光照信息。 打包場景后會生成.unity3D 二進制格式,它和 assetbundle 打包方式是不同的。(另外,也可以考慮 json xml 二進制 來動態組裝場景)。
腳本部分:如果Prefab上是帶腳本打包Assetbundle的話 腳本是不會被運行的(移動平台), 但是unity有一個技巧,Prefab上的腳本 如果本地有的話它會把本地的同名腳本綁定在Prefab對象上,它會很好的執行。
Prefab打包技巧: Prefab打包時自身是不占多少空間的 <=1KB 但是Prefab上是可以關聯 這五大部分 “界面,模型,特效,聲音,場景,腳本”以及在Hierarchy視圖中 坐標/縮放/旋轉。 關聯這些信息以后就會很大,所以為了避免資源的浪費盡量避免Prefab重復關聯。
一個prefab下面可以同時關聯多個游戲對象 ,這里舉個例子如果你的 Prefab下面放了一個模型 它的大小可能是500k ,在 Prefab下面放了十個完全相同模型 它的大小可能是501k 。 如果Prefab下面放了兩個不同的模型,它的大小可能就會是 500k x 2 的size ,也就是說Prefab與關聯的數量是無關的 。
加密部分: assetbundle 是可以轉換成 字節數組 ,客戶端與服務器約定一組解密 字節數組的算法就可以實現資源加密。
大版本升級:
unity的版本升級其實主要是升級主程序中的腳本。 因為所有的資源都是assetbundle 和 .unity3d 這些資源放在本地或者服務器 解包的方式是完全一樣,所以理論上我們的主程序包的大小可以做到很小,可以很好設置把多少資源放在包里 或者把所少資源放在服務器上。在運行的時候服務端應該把所有 assetbundle 和 .unity3d的資源文件的下載地址列表返回給客戶端。
小版本升級:
小版本升級也就是更新資源,因為不能更新腳本, 在登陸的時候服務端應該把所有 assetbundle 和 .unity3d的資源文件的下載地址列表返回給客戶端。
還有個需要考慮的地方,比如現在大版本是2.0.0 ,小版本已經是2.0.5 ,用戶的手機上是一個1.5.0的包。 此時用戶在打開游戲的時候 應當強制它去appstore中去下載大版本2.0.0 ,當用戶下載完畢后登陸游戲,此時服務器告訴客戶端現在已經是2.0.5的小版本了,這時候客戶端去下載對應小版本的所有 assetbundle 和 .unity3d文件地址列表。
增量更新:理論上增量更新是可行的。因為unity不能更新腳本,所以在處理增量更新的話 需要在代碼中做可以兼容增量更新的可能。
因為Assetbundle這塊的代碼比較多,我還是決定分成兩篇文章來寫,這篇文章先說原理、下篇文章說代碼。歡迎大家來討論!
前幾天我和Unity鑫哥聊天,他告訴我IOS上是無法運行時更新腳本、但是Android上是可以運行時更新腳本,我回家也試了一下但是沒能成功,后來我考慮即使成功了項目中我也不打算那么做,因為這樣Android和IOS 做起來的差別就太多了, 另外Unity商店中有一個處理運行時更新腳本的插件 unityLua 大家可以去研究研究。
上一次我們學習了如何將資源進行打包。這次就可以用上場了,我們來探討一下手游資源的增量更新策略。注意哦,只是資源哦。關於代碼的更新,我們稍后再來研究。理論上這個方案可以使用各種靜態資源的更新,不僅僅是assetbundle打包的。
(轉載請注明原文地址http://blog.csdn.net/janeky/article/details/17666409)
- 原理
現在的手游安裝有幾種方式。一種是安裝的時候就把程序和資源安裝到本地。另外一種是只安裝程序和少量的必要資源,然后在啟動的時候再把缺少的資源下載完整。手游一般不建議和傳統頁游一樣,在運行過程中加載資源,那樣做會導致用戶體驗會比較差些。上述的兩種安裝模式,在更新資源上本質都是相同的。都是比較服務器資源的版本和本地資源的版本,以確定哪些資源要下載(包括需要更新的和新增的)。
- 實踐
1.資源打包。
資源打包之前,要先規划好資源之間的相互依賴關系。把一些共性的東西抽取出來,盡量減少不必要的耦合。一些比較好的做法有,所有物件盡可能做成Prefab,場景上的東西越少越好,“一切都是動態加載”。
2.生成文件MD5
關於文件的MD5,這里就不詳細描述了。大家可以簡單理解它為一個文件的狀態標記。如果文件有更改,那么它的md5一定是改變的,單純的移動文件是不會更改的。md5驗證還可以起到安全驗證的作用,保證本地文件不被篡改。舉個例子,我們經常從網上上下載軟件時,一般都會給出一個md5值,你下載后,對比一下已下載文件的md5值,就可以知道文件有沒有被篡改。在版本發布時,我們需要對所有打包好的文件計算md5值,然后保存在一個配置文件中。關於這部分的工作,我之前寫過一個可視化小工具(https://github.com/kenro/File_Md5_Generator),現在分享給大家。如果大家覺得有用,記得打星哦:)
3.版本比較
先加載本地的version.txt,將結果緩存起來。下載服務器的version.txt,與本地的version進行比較,篩選出需要更新和新增的資源
4.下載資源
依次下載更新的資源,如果本地已經有舊資源,則替換之,否則就新建保存起來
5.更新本地版本配置文件version.txt
用服務器的version.txt替換掉本地的version.txt。這樣做是為了確保下次啟動的時候,不會再重復更新了。
6.從本地加載assetbundle進行測試顯示。
這里將一個模型制成Prefab,打包成assetbundle。程序從本地加載后,顯示在場景中
7.更新服務器的assetbundle,重新生成版本號文件。
8.重復6的步驟
我們可以驗證,我們的程序不用任何改動,資源已經實現了更新。場景中顯示的已經是最新的模型了。
關於上述的流程,我寫了一個小的演示demo。我這里沒有用到web服務器,而是將本地的另外一個文件夾作為資源服務器目錄。這里的目錄只針對windows下的版本進行測試。如果要在手機平台上,需要記得更新相關的路徑。
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- public class ResUpdate : MonoBehaviour
- {
- public static readonly string VERSION_FILE = "version.txt";
- public static readonly string LOCAL_RES_URL = "file://" + Application.dataPath + "/Res/";
- public static readonly string SERVER_RES_URL = "file:///C:/Res/";
- public static readonly string LOCAL_RES_PATH = Application.dataPath + "/Res/";
- private Dictionary<string, string> LocalResVersion;
- private Dictionary<string, string> ServerResVersion;
- private List<string> NeedDownFiles;
- private bool NeedUpdateLocalVersionFile = false;
- void Start()
- {
- //初始化
- LocalResVersion = new Dictionary<string, string>();
- ServerResVersion = new Dictionary<string, string>();
- NeedDownFiles = new List<string>();
- //加載本地version配置
- StartCoroutine(DownLoad(LOCAL_RES_URL + VERSION_FILE, delegate(WWW localVersion)
- {
- //保存本地的version
- ParseVersionFile(localVersion.text, LocalResVersion);
- //加載服務端version配置
- StartCoroutine(this.DownLoad(SERVER_RES_URL + VERSION_FILE, delegate(WWW serverVersion)
- {
- //保存服務端version
- ParseVersionFile(serverVersion.text, ServerResVersion);
- //計算出需要重新加載的資源
- CompareVersion();
- //加載需要更新的資源
- DownLoadRes();
- }));
- }));
- }
- //依次加載需要更新的資源
- private void DownLoadRes()
- {
- if (NeedDownFiles.Count == 0)
- {
- UpdateLocalVersionFile();
- return;
- }
- string file = NeedDownFiles[0];
- NeedDownFiles.RemoveAt(0);
- StartCoroutine(this.DownLoad(SERVER_RES_URL + file, delegate(WWW w)
- {
- //將下載的資源替換本地就的資源
- ReplaceLocalRes(file, w.bytes);
- DownLoadRes();
- }));
- }
- private void ReplaceLocalRes(string fileName, byte[] data)
- {
- string filePath = LOCAL_RES_PATH + fileName;
- FileStream stream = new FileStream(LOCAL_RES_PATH + fileName, FileMode.Create);
- stream.Write(data, 0, data.Length);
- stream.Flush();
- stream.Close();
- }
- //顯示資源
- private IEnumerator Show()
- {
- WWW asset = new WWW(LOCAL_RES_URL + "cube.assetbundle");
- yield return asset;
- AssetBundle bundle = asset.assetBundle;
- Instantiate(bundle.Load("Cube"));
- bundle.Unload(false);
- }
- //更新本地的version配置
- private void UpdateLocalVersionFile()
- {
- if (NeedUpdateLocalVersionFile)
- {
- StringBuilder versions = new StringBuilder();
- foreach (var item in ServerResVersion)
- {
- versions.Append(item.Key).Append(",").Append(item.Value).Append("\n");
- }
- FileStream stream = new FileStream(LOCAL_RES_PATH + VERSION_FILE, FileMode.Create);
- byte[] data = Encoding.UTF8.GetBytes(versions.ToString());
- stream.Write(data, 0, data.Length);
- stream.Flush();
- stream.Close();
- }
- //加載顯示對象
- StartCoroutine(Show());
- }
- private void CompareVersion()
- {
- foreach (var version in ServerResVersion)
- {
- string fileName = version.Key;
- string serverMd5 = version.Value;
- //新增的資源
- if (!LocalResVersion.ContainsKey(fileName))
- {
- NeedDownFiles.Add(fileName);
- }
- else
- {
- //需要替換的資源
- string localMd5;
- LocalResVersion.TryGetValue(fileName, out localMd5);
- if (!serverMd5.Equals(localMd5))
- {
- NeedDownFiles.Add(fileName);
- }
- }
- }
- //本次有更新,同時更新本地的version.txt
- NeedUpdateLocalVersionFile = NeedDownFiles.Count > 0;
- }
- private void ParseVersionFile(string content, Dictionary<string, string> dict)
- {
- if (content == null || content.Length == 0)
- {
- return;
- }
- string[] items = content.Split(new char[] { '\n' });
- foreach (string item in items)
- {
- string[] info = item.Split(new char[] { ',' });
- if (info != null && info.Length == 2)
- {
- dict.Add(info[0], info[1]);
- }
- }
- }
- private IEnumerator DownLoad(string url, HandleFinishDownload finishFun)
- {
- WWW www = new WWW(url);
- yield return www;
- if (finishFun != null)
- {
- finishFun(www);
- }
- www.Dispose();
- }
- public delegate void HandleFinishDownload(WWW www);
- }
- 總結
資源更新的原理,本質上都是相似的。我之前也從事過頁游的開發,資源更新流程也類似。所以技術的本質是掌握思維方式,平台和語言都是永遠在變的。我們最后歸納一下流程:比較服務端的資源版本和本地的資源版本,找出需要更新的資源,然后依次下載。如果大家有更好的策略,歡迎分享探討 ken@iamcoding.com。
- 源碼
http://pan.baidu.com/s/1mgNnR8O
- 參考資料
Unity3d官網文檔
之前我們已經學過手機游戲的資源熱更新策略了。在實際手游的開發運營中,我們需要經常修復bug,增加新玩法。這些通常都涉及到代碼的更新。unity游戲代碼的更新比較復雜,也存在不同的更新策略,各有優缺點,在不同的平台上做法也不盡相同。這里主要談一些比較常用的策略和各大手機平台上的策略。大家有更好的思路,歡迎探討。
(轉載請注明出處 http://blog.csdn.net/janeky/article/details/25923151)
- 反射
大部分編程語言都是支持反射的,利用反射,可以動態去加載所需的程序。C#也是同樣可以用反射來實現。要實現代碼的更新,我們在項目初期就要做好規划,將一些容易變更的業務邏輯代碼獨立划分。每次更新時,將代碼打包成dll,再打包成資源文件。程序啟動時,檢查更新到客戶端,客戶端通過反射重新加載代碼運行。下面通過一個簡單的demo來演示。
1.在vs中新建一個代碼庫工程,命名為test
2.添加幾個類Scirpt,Scirpt2,Data
3.將這個項目生成DLL,test.dll
4.新建一個unity項目,將DLL倒入到Asset,改名為test.bytes,不然可能會報錯
5.利用我們之前實現過的打包腳本,將test.bytes打包成test.assetbundle。
6.創建CodeUpdate.cs腳本,用於加載代碼資源,反射調用。
7.為了驗證代碼更新后,可以直接加載使用,我們可以更改一下Data.cs的代碼,重復以上過程,可以看到,更新了代碼打包后,我們重新運行游戲,就可以看到效果
Data.cs
- public class Data
- {
- private int attr;
- public Data()
- {
- attr = 2;
- }
- public override string ToString()
- {
- return attr.ToString();
- }
- }
Script.cs
- public class Script: MonoBehaviour
- {
- void Start()
- {
- Debug.Log("------------------I am script 1");
- Data data = new Data();
- Debug.Log("-------------" + data.ToString());
- }
- }
CodeUpdate.cs
- using UnityEngine;
- using System.Collections;
- using System;
- public class CodeUpdate : MonoBehaviour {
- private static readonly string DLL_URL = "file:///c:/test.assetbundle";
- void Start () {
- StartCoroutine(loadDllScript());
- }
- private IEnumerator loadDllScript()
- {
- WWW www = new WWW(DLL_URL);
- yield return www;
- AssetBundle bundle = www.assetBundle;
- TextAsset asset = bundle.Load("test",typeof(TextAsset)) as TextAsset;
- System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(asset.bytes);
- Type script1 = assembly.GetType("Script");
- GameObject obj = new GameObject();
- obj.AddComponent(script1);
- Type script2 = assembly.GetType("Script2");
- obj.AddComponent(script2);
- }
- }
- 完整安裝包更新
大部分的app更新都是采用完整包更新。在程序啟動的時候,檢查服務器的最新版本,如果比本地的版本要新,就下載服務器的版本,重新安裝替換本地的程序。在IOS平台上,是由App Store來統一管理的。客戶端程序只需檢查版本,跳轉到app store頁面即可。android 平台的更新更靈活,略微復雜。在判斷版本號,確定要更新后,直接就可以下載服務器的最新的apk文件,安裝替換本地的。這里就不演示代碼了。大家先理清楚思路,流程,就容易實現了。
- LUA腳本更新
LUA一直是一種很神奇的腳本語言,無處不在,服務端,客戶端,大型機,嵌入式設備都能看到它的蹤影。雖然Unity3d官方不支持Lua腳本,但是已經有人寫了c#版本的lua解析器了。我們可以將業務代碼用Lua來實現。每次要更新代碼的時候,只要將lua當做資源文件更新到客戶端,運行即可。
C#版 Lua,有很多個版本,這里選擇雲風他們公司開源的UniLua,大家可以去Githunb下載
https://github.com/xebecnan/UniLua/wiki
- IOS平台
比較遺憾,IOS是一個封閉的平台,所以它對app程序監管比較嚴格,一般情況下不運行熱更新,每次版本更新都需要提交審核。所以涉及到手游代碼的更新,都是采用完整包更新。LUA腳本更新的方式,有朋友試過說可以(他們一般是在程序上線一段時間后才使用Lua更新)。但是也存在風險的,如果被蘋果發現,是屬於違規的。這里不建議使用。
- Android平台
目前比較通用的方式是用代碼dll反射更新機制。我們在實際過程中,將穩定不變的底層代碼單獨規划,用作游戲的主程序。全部業務邏輯代碼發布時候,打包成dll,制成資源文件。客戶端下載后,反射加載。只有當底層主程序要更新是,才單獨下載主程序的apk文件,重新安裝替換。平時的代碼更新,可以隨意更新代碼dll
- 總結
上面說的幾種方式,各有優缺點。在不同的平台上策略也不盡相同。說一下我的經驗:一般是優先發布android版本,有問題隨時熱更新代碼調試。待版本穩定后,發布ios越獄版本。全部穩定后,最后才發布app store。眾所周知,app store的審查周期比較長,有可能他們員工去休個假,幾個星期才審核通過:)。每次審核不通過,又得重新修改提交審查,又是漫長的等待。在游戲界,時間就是生命。我們盡量在android平台上調試版本。
ps.大家有什么好的Unity3d技術點想討論的,歡迎告知,我今后將會多多寫一下大家比較感興趣的實戰內容。
最后祝大家工作順利,項目大賣~。