資源管理原理:
制作:
1.創(chuàng)建AssetBundle,用BuildAssetBundleExplicitAssetsName(可以根據(jù)Asset名稱來(lái)加載資源,優(yōu)先),BuildAssetBundle,BuildStreamScenexxx.
記得需要指明Assets資源名稱,地址,,創(chuàng)建所在的平臺(tái),。
2.創(chuàng)建資源依賴關(guān)系,可以用Push/Pop AssetDependencies來(lái)減少資源量,,復(fù)用資源,。
注意事項(xiàng)
a.AssetBundle的保存后綴名可以是assetbundle或者unity3d
b.BuildAssetBundle要根據(jù)不同的平臺(tái)單獨(dú)打包,BuildTarget參數(shù)指定平臺(tái),,如果不指定,,默認(rèn)的webplayer
下載和加載:
3.下載分為緩存機(jī)制和非緩存機(jī)制,緩存機(jī)制可以根據(jù)資源是否已經(jīng)下載過(guò)直接讀入不需要網(wǎng)絡(luò)上下載,,非緩存機(jī)制每次都下載,。
WWW直接使用為非緩存機(jī)制,LoadFromCacheOrDownload為緩存機(jī)制下載,。
4.將AssetBundle加載到內(nèi)存(會(huì)進(jìn)行解壓和加載,,解壓時(shí)候存在解壓緩存,完成后留下一塊以下次復(fù)用),,assetBundle屬性直接加載,,CreateFromFile只對(duì)非壓縮格式的bundle, CreateFromMemory用www.bytes適合自定義解密算法的用。
AssetBundle之間存在依賴關(guān)系的話,,需要按照依賴關(guān)系來(lái)加載bundle,。
5.從AssetBundle加載Asset,可以通過(guò)Assetbundle.load/loadAll/loadAsync來(lái)加載到內(nèi)存中;場(chǎng)景類型的AssetBundle需要用loadLevel/loadLevelAsync來(lái)實(shí)現(xiàn),。
AssetBundleRequest request = bundle.LoadAsync("myObject",typeof(GameObject));
GameObject obj = request.asset as GameObject;
卸載:
6.卸載:真正包中的數(shù)據(jù)是webStreamData,,因?yàn)閃WW對(duì)象是對(duì)webStreamData的引用,AssetBundle是對(duì)webStreamData的引用,,AssetBundle還對(duì)從它創(chuàng)建的Assets存在引用,。
所以卸載時(shí)候:對(duì)于www = null,www.dispose();
對(duì)于AssetBundle.Unload(false)只是刪掉索引結(jié)構(gòu)自身;AssetBundle.Unload(true)會(huì)對(duì)自身和由它創(chuàng)建的Asset刪除(不管場(chǎng)景是否引用不推薦),;
對(duì)于Asset資源,,可以恰當(dāng)時(shí)候用Resource.UnloadUnuseAsset()刪除整個(gè)進(jìn)程不用的資源,,GameObject.Destroy/DestroyImmdiate刪除不用的Asset,或者通過(guò)隱藏的方式達(dá)到復(fù)用GameObject的目的,。
webStreamData在www,bundle對(duì)象都刪除后會(huì)被刪除掉,當(dāng)然也意味著不能再?gòu)腷undle streamData里面創(chuàng)建asset了,,綜合上面過(guò)程中內(nèi)存存在4種占用內(nèi)存的資源
(www對(duì)象數(shù)據(jù),streamData內(nèi)存對(duì)象數(shù)據(jù),,bundle對(duì)象數(shù)據(jù),,asset-Object對(duì)象數(shù)據(jù), clone-object對(duì)象數(shù)據(jù))。
Asset-Object是由Resources系統(tǒng)管理的,,包括Asset中的Texture,、AnimationClips等,為內(nèi)存開(kāi)銷的主要來(lái)源,。Resources系統(tǒng)使用弱引用管理Asset-Object,,同時(shí)提供了Resources.UnloadUnusedAsset()以刪除沒(méi)有不再被強(qiáng)引用的Asset-Object。
導(dǎo)出場(chǎng)景只能用BuildStreamedSceneAssetBundle,,不能使用BuildAssetBundle,。場(chǎng)景文件仍然可以用CreateFromMemory讀取,只不過(guò)Clone的時(shí)候必須使用Application.LoadLevel,。而且LoadLevel過(guò)程和結(jié)果貌似不可控,,比如你從程序上并不能得到“場(chǎng)景加載完畢”或者“場(chǎng)景的內(nèi)容是什么”這樣的信息。
7.內(nèi)存資源的管理:
創(chuàng)建或Instaniate一個(gè)Prefab對(duì)象處理可能是clone也可能是引用,往往數(shù)據(jù)是一份的引用關(guān)系,,代碼和Transform是獨(dú)立的復(fù)制關(guān)系,。
腳本也是可以復(fù)制出來(lái)的,是copy+ref方式,。GameObject.Destroy是指引用計(jì)數(shù)減去1,,AssetBundle.Unload(true)可以釋放由它創(chuàng)建的Assets但是不安全;善于用SetActive(false)復(fù)用,用GameObject.Destroy減去引用計(jì)數(shù),,恰當(dāng)使用Resources.UnloadUnusedAssets來(lái)釋放Assets,。當(dāng)然AssetBundle,WWW的資源釋放也要及時(shí),。
Resources.UnloadAsset(Obj)釋放指定的Asset對(duì)象資源,,只能卸載磁盤文件加載的Asset對(duì)象。
8.Unity的資源管理:
先建立一個(gè)AssetBundle,無(wú)論是從www還是文件還是memory
用AssetBundle.load加載需要的asset
加載完后立即AssetBundle.Unload(false),釋放AssetBundle文件本身的內(nèi)存鏡像,,但不銷毀加載的Asset對(duì)象,。(這樣你不用保存AssetBundle的引用并且可以立即釋放一部分內(nèi)存)
釋放時(shí):
如果有Instantiate的對(duì)象,用Destroy進(jìn)行銷毀
在合適的地方調(diào)用Resources.UnloadUnusedAssets,釋放已經(jīng)沒(méi)有引用的Asset.
如果需要立即釋放內(nèi)存加上GC.Collect(),,否則內(nèi)存未必會(huì)立即被釋放,,有時(shí)候可能導(dǎo)致內(nèi)存占用過(guò)多而引發(fā)異常。
這樣可以保證內(nèi)存始終被及時(shí)釋放,,占用量最少,。也不需要對(duì)每個(gè)加載的對(duì)象進(jìn)行引用,。
當(dāng)然這并不是唯一的方法,只要遵循加載和釋放的原理,,任何做法都是可以的,。
系統(tǒng)在加載新場(chǎng)景時(shí),,所有的內(nèi)存對(duì)象都會(huì)被自動(dòng)銷毀,,包括你用AssetBundle.Load加載的對(duì)象和Instaniate克隆的。但是不包括AssetBundle文件自身的內(nèi)存鏡像,,那個(gè)必須要用Unload來(lái)釋放,,用.net的術(shù)語(yǔ),這種數(shù)據(jù)緩存是非托管的,。
9.用法總結(jié):
總結(jié)一下各種加載和初始化的用法:
AssetBundle.CreateFrom.....:創(chuàng)建一個(gè)AssetBundle內(nèi)存鏡像,,注意同一個(gè)assetBundle文件在沒(méi)有Unload之前不能再次被使用
WWW.AssetBundle:同上,當(dāng)然要先new一個(gè)再 yield return 然后才能使用
AssetBundle.Load(name): 從AssetBundle讀取一個(gè)指定名稱的Asset并生成Asset內(nèi)存對(duì)象,,如果多次Load同名對(duì)象,,除第一次外都只會(huì)返回已經(jīng)生成的Asset 對(duì)象,也就是說(shuō)多次Load一個(gè)Asset并不會(huì)生成多個(gè)副本(singleton),。
Resources.Load(path&name):同上,只是從默認(rèn)的位置加載,。
Instantiate(object):Clone 一個(gè)object的完整結(jié)構(gòu),包括其所有Component和子物體(詳見(jiàn)官方文檔),淺Copy,,并不復(fù)制所有引用類型,。有個(gè)特別用法,雖然很少這樣 用,,其實(shí)可以用Instantiate來(lái)完整的拷貝一個(gè)引用類型的Asset,比如Texture等,,要拷貝的Texture必須類型設(shè)置為 Read/Write able。
總結(jié)一下各種釋放
Destroy: 主要用于銷毀克隆對(duì)象,,也可以用于場(chǎng)景內(nèi)的靜態(tài)物體,,不會(huì)自動(dòng)釋放該對(duì)象的所有引用。雖然也可以用于Asset,但是概念不一樣要小心,,如果用于銷毀從文 件加載的Asset對(duì)象會(huì)銷毀相應(yīng)的資源文件,!但是如果銷毀的Asset是Copy的或者用腳本動(dòng)態(tài)生成的,只會(huì)銷毀內(nèi)存對(duì)象,。
AssetBundle.Unload(false):釋放AssetBundle文件內(nèi)存鏡像
AssetBundle.Unload(true):釋放AssetBundle文件內(nèi)存鏡像同時(shí)銷毀所有已經(jīng)Load的Assets內(nèi)存對(duì)象
Reources.UnloadAsset(Object):顯式的釋放已加載的Asset對(duì)象,,只能卸載磁盤文件加載的Asset對(duì)象
Resources.UnloadUnusedAssets:用于釋放所有沒(méi)有引用的Asset對(duì)象
GC.Collect()強(qiáng)制垃圾收集器立即釋放內(nèi)存 Unity的GC功能不算好,沒(méi)把握的時(shí)候就強(qiáng)制調(diào)用一下
在3.5.2之前好像Unity不能顯式的釋放Asset,。
實(shí)例1-Asset Object和Clone Object同時(shí)存在問(wèn)題:
一個(gè)常見(jiàn)的錯(cuò)誤:你從某個(gè)AssetBundle里L(fēng)oad了一個(gè)prefab并克隆之:obj = Instaniate(AssetBundle1.Load('MyPrefab”);
這個(gè)prefab比如是個(gè)npc然后你不需要他的時(shí)候你用了:Destroy(obj);你以為就釋放干凈了
其實(shí)這時(shí)候只是釋放了Clone對(duì)象,,通過(guò)Load加載的所有引用、非引用Assets對(duì)象全都靜靜靜的躺在內(nèi)存里,。
這種情況應(yīng)該在Destroy以后用:AssetBundle1.Unload(true),,徹底釋放干凈,。
如果這個(gè)AssetBundle1是要反復(fù)讀取的 不方便Unload,那可以在Destroy以后用:Resources.UnloadUnusedAssets()把所有和這個(gè)npc有關(guān)的Asset都銷毀,。
一是靜態(tài)引用,,建一個(gè)public的變量,在Inspector里把prefab拉上去,,用的時(shí)候instantiate
二是Resource.Load,,Load以后instantiate
三是AssetBundle.Load,Load以后instantiate
三種方式有細(xì) 節(jié)差異,前兩種方式,,引用對(duì)象texture是在instantiate時(shí)加載,,而assetBundle.Load會(huì)把perfab的全部assets 都加載,instantiate時(shí)只是生成Clone,。所以前兩種方式,,除非你提前加載相關(guān)引用對(duì)象,否則第一次instantiate時(shí)會(huì)包含加載引用 assets的操作,,導(dǎo)致第一次加載的lag,。
AssetBundle.Load是更好的方式。
例子2-多個(gè)引用Asset-Object或Clone-Object問(wèn)題:
從磁盤讀取一個(gè)1.unity3d文件到內(nèi)存并建立一個(gè)AssetBundle1對(duì)象
AssetBundle AssetBundle1 = AssetBundle.CreateFromFile("1.unity3d");
從AssetBundle1里讀取并創(chuàng)建一個(gè)Texture Asset,把obj1的主貼圖指向它
obj1.renderer.material.mainTexture = AssetBundle1.Load("wall") as Texture;
把obj2的主貼圖也指向同一個(gè)Texture Asset
obj2.renderer.material.mainTexture =obj1.renderer.material.mainTexture;
Texture是引用對(duì)象,,永遠(yuǎn)不會(huì)有自動(dòng)復(fù)制的情況出現(xiàn)(除非你真需要,,用代碼自己實(shí)現(xiàn)copy),只會(huì)是創(chuàng)建和添加引用
如果繼續(xù):
AssetBundle1.Unload(true) 那obj1和obj2都變成黑的了,,因?yàn)橹赶虻腡exture Asset沒(méi)了
如果:
AssetBundle1.Unload(false) 那obj1和obj2不變,,只是AssetBundle1的內(nèi)存鏡像釋放了
繼續(xù):
Destroy(obj1),//obj1被釋放,但并不會(huì)釋放剛才Load的Texture
如果這時(shí)候:
Resources.UnloadUnusedAssets();
不會(huì)有任何內(nèi)存釋放 因?yàn)門exture asset還被obj2用著
如果
Destroy(obj2)
obj2被釋放,,但也不會(huì)釋放剛才Load的Texture
繼續(xù)
Resources.UnloadUnusedAssets();
這時(shí)候剛才load的Texture Asset釋放了,,因?yàn)闆](méi)有任何引用了
最后CG.Collect();強(qiáng)制立即釋放內(nèi)存。
IEnumerator OnClick()
{
WWW image = new www(fileList【n++】),;
yield return image;
Texture tex = obj.mainTexture;
obj.mainTexture = image.texture;
n = (n>=fileList.Length-1)?0:n;
Resources.UnloadAsset(tex);
}
這樣卸載比較快,。
用法匯總:
感覺(jué)這是Unity內(nèi)存管理暗黑和混亂的地方,特別是牽扯到Texture我最近也一直在測(cè)試這些用AssetBundle加載的asset一樣可以用Resources.UnloadUnusedAssets卸載,, 但必須先 AssetBundle.Unload, 才會(huì)被識(shí)別為無(wú)用的 asset ,。 比較保險(xiǎn)的做法是
創(chuàng)建時(shí):
先建立一個(gè)AssetBundle,無(wú)論是從www還是文件還是memory
用AssetBundle.load加載需要的asset。Resources.Load和靜態(tài)引用用的時(shí)候才真正的Load數(shù)據(jù),。
用完后立即AssetBundle.Unload(false),關(guān)閉AssetBundle但不摧毀創(chuàng)建的對(duì)象和引用
銷毀時(shí):
對(duì)Instantiate的對(duì)象進(jìn)行Destroy
在合適的地方調(diào)用Resources.UnloadUnusedAssets,釋放已經(jīng)沒(méi)有引用的Asset.
如果需要立即釋放加上GC.Collect()
這樣可以保證內(nèi)存始終被及時(shí)釋放
只要你Unload過(guò)的AssetBundle,那些創(chuàng)建的對(duì)象和引用都會(huì)在LoadLevel時(shí)被自動(dòng)釋放,。
所以:UnusedAssets不但要沒(méi)有被實(shí)際物體引用,也要沒(méi)有被生命周期內(nèi)的變量所引用,,才可以理解為 Unused(引用計(jì)數(shù)為0),。
Texture 加載以后是到內(nèi)存,顯示的時(shí)候才進(jìn)入顯存的Texture Memory,。
所有的東西基礎(chǔ)都是Object,。
Load的是Asset,Instantiate的是GameObject和Object in Scene,。
Load的Asset要Unload,new的或者Instantiate的object可以Destroy。
二,,資源結(jié)構(gòu)圖:
1.Resources文件夾
Resources文件夾是一個(gè)只讀的文件夾,,通過(guò)Resources.Load()來(lái)讀取對(duì)象。因?yàn)檫@個(gè)文件夾下的所有資源都可以運(yùn)行時(shí)來(lái)加載,,所以Resources文件夾下的所有東西都會(huì)被無(wú)條件的打到發(fā)布包中,。建議這個(gè)文件夾下只放Prefab或者一些Object對(duì)象,因?yàn)镻refab會(huì)自動(dòng)過(guò)濾掉對(duì)象上不需要的資源,。舉個(gè)例子我把模型文件還有貼圖文件都放在了Resources文件夾下,,但是我有兩張貼圖是沒(méi)有在模型上用的,那么此時(shí)這兩張沒(méi)用的貼圖也會(huì)被打包到發(fā)布包中,。假如這里我用Prefab,那么Prefab會(huì)自動(dòng)過(guò)濾到這兩張不被用的貼圖,,這樣發(fā)布包就會(huì)小一些了,。
Resources資源是指在Unity工程的Assets目錄下面可以建一個(gè)Resources文件夾,在這個(gè)文件夾下面放置的所有資源,,不論是否被場(chǎng)景用到,,都會(huì)被打包到游戲中,并且可以通過(guò)Resources.Load方法動(dòng)態(tài)加載,。這是平時(shí)開(kāi)發(fā)是常用的資源加載方式,,但是缺點(diǎn)是資源都直接打包到游戲包中了,沒(méi)法做增量更新,。
2.StreamingAssets
StreamingAssets文件夾也是一個(gè)只讀的文件夾,,但是它和Resources有點(diǎn)區(qū)別,Resources文件夾下的資源會(huì)進(jìn)行一次壓縮,,而且也會(huì)加密,,不使用點(diǎn)特殊辦法是拿不到原始資源的。但是StreamingAssets文件夾就不一樣了,,它下面的所有資源不會(huì)被加密,,然后是原封不動(dòng)的打包到發(fā)布包中,這樣很容易就拿到里面的文件,。
所以StreamingAssets適合放一些二進(jìn)制文件,,而Resources更適合放一些GameObject和Object文件。 StreamingAssets 只能用過(guò)www類來(lái)讀?。,。?br>
AssetBundle資源是指我們可以通過(guò)編輯器腳本來(lái)將資源打包成多個(gè)獨(dú)立的AssetBundle,。這些AssetBundle和游戲包是分離的,,可以通過(guò)WWW類來(lái)加載,。AssetBundle的使用很靈活:可以用來(lái)做分包發(fā)布,例如大多數(shù)頁(yè)游資源是隨著游戲的過(guò)程增量下載的,,或者有些手游資源過(guò)大,,渠道要求發(fā)布的包限制在100M以內(nèi),那只能把一開(kāi)始玩不到的內(nèi)容做成增量包,,等玩家玩到的時(shí)候通過(guò)網(wǎng)絡(luò)下載,。
打包過(guò)程只需要BuildPipeline.BuildAssetBundles一句話就行了,Unity5會(huì)根據(jù)依賴關(guān)系自動(dòng)生成所有的包,。每個(gè)包還會(huì)生成一個(gè)manifest文件,,這個(gè)文件描述了包大小、crc驗(yàn)證,、包之間的依賴關(guān)系等等,,通過(guò)這個(gè)manifest打包工具在下次打包的時(shí)候可以判斷哪些包中的資源有改變,只打包資源改變的包,,加快了打包速度,。manifest只是打包工具自己用的,發(fā)布包的時(shí)候并不需要,。
更深的坑在于,,如果你公用的是一個(gè)FBX模型,你只給這個(gè)模型設(shè)置BundleName還不行,,它用到的貼圖,,材質(zhì)都要設(shè),否則模型是公用了,,貼圖沒(méi)有公用,,結(jié)果貼圖還是被打包到多個(gè)包中了。所以設(shè)置BundleName這個(gè)工作最好還是由編輯器腳本來(lái)完成,。
Unity3D 里有兩種動(dòng)態(tài)加載機(jī)制:一個(gè)是Resources.Load,,另外一個(gè)通過(guò)AssetBundle,其實(shí)兩者區(qū)別不大。 Resources.Load就是從一個(gè)缺省打進(jìn)程序包里的AssetBundle里加載資源,,而一般AssetBundle文件需要你自己創(chuàng)建,,運(yùn)行時(shí) 動(dòng)態(tài)加載,可以指定路徑和來(lái)源的,。
其實(shí)場(chǎng)景里所有靜態(tài)的對(duì)象也有這么一個(gè)加載過(guò)程,,只是Unity3D后臺(tái)替你自動(dòng)完成了。
為GameObject動(dòng)態(tài)的添加游戲其它組件:
public Component AddComponent(Type componentType);
[ExcludeFromDocs]
public void BroadcastMessage(string methodName);
Type classType = typeof(UnityEngine.UI.Text);
this.gameObject.AddComponent(classType);
ulua中用GetClassType()
例如:return u3DObj:AddComponent(BGCUUIDisplayController.GetClassType())
創(chuàng)建Asset內(nèi)存對(duì)象:
你 Instaniate一個(gè)Prefab,,是一個(gè)對(duì)Assets進(jìn)行Clone(復(fù)制)+引用結(jié)合的過(guò)程,,GameObject transform 是Clone是新生成的。其他mesh / texture / material / shader 等,這其中些是純引用的關(guān)系的,,包括:Texture和TerrainData,,還有引用和復(fù)制同時(shí)存在的,包括:Mesh/material /PhysicMaterial,。引用的Asset對(duì)象不會(huì)被復(fù)制,,只是一個(gè)簡(jiǎn)單的指針指向已經(jīng)Load的Asset對(duì)象。這種含糊的引用加克隆的混合,,
大概是搞糊涂大多數(shù)人的主要原因,。
專門要提一下的是一個(gè)特殊的東西:Script Asset,看起來(lái)很奇怪,,Unity里每個(gè)Script都是一個(gè)封閉的Class定義而已,并沒(méi)有寫調(diào)用代碼,,光Class的定義腳本是不會(huì)工作的。其 實(shí)Unity引擎就是那個(gè)調(diào)用代碼,,Clone一個(gè)script asset等于new一個(gè)class實(shí)例,,實(shí)例才會(huì)完成工作。把他掛到Unity主線程的調(diào)用鏈里去,,Class實(shí)例里的OnUpdate OnStart等才會(huì)被執(zhí)行,。多個(gè)物體掛同一個(gè)腳本,其實(shí)就是在多個(gè)物體上掛了那個(gè)腳本類的多個(gè)實(shí)例而已,,這樣就好理解了。在new class這個(gè)過(guò)程中,,數(shù)據(jù)區(qū)是復(fù)制的,,代碼區(qū)是共享的,算是一種特殊的復(fù)制+引用關(guān)系,。
你可以再Instaniate一個(gè)同樣的Prefab,還是這套mesh/texture/material/shader...,,這時(shí)候會(huì)有新的GameObject等,但是不會(huì)創(chuàng)建新的引用對(duì)象比如Texture.
所以你Load出來(lái)的Assets其實(shí)就是個(gè)數(shù)據(jù)源,,用于生成新對(duì)象或者被引用,,生成的過(guò)程可能是復(fù)制(clone)也可能是引用(指針)。
當(dāng)你Destroy一個(gè)實(shí)例時(shí),,只是釋放那些Clone對(duì)象,,并不會(huì)釋放引用對(duì)象和Clone的數(shù)據(jù)源對(duì)象,Destroy并不知道是否還有別的object在引用那些對(duì)象,。
等到?jīng)]有任何 游戲場(chǎng)景物體在用這些Assets以后,,這些assets就成了沒(méi)有引用的游離數(shù)據(jù)塊了,是UnusedAssets了,,這時(shí)候就可以通過(guò) Resources.UnloadUnusedAssets來(lái)釋放,Destroy不能完成這個(gè)任 務(wù),,AssetBundle.Unload(false)也不行,AssetBundle.Unload(true)可以但不安全,除非你很清楚沒(méi)有任何 對(duì)象在用這些Assets了,。
三,,優(yōu)化資源:
1.代碼優(yōu)化
當(dāng)使用Unity開(kāi)發(fā)時(shí),默認(rèn)的Mono包含庫(kù)可以說(shuō)大部分用不上,,在Player Setting(Edit->Project Setting->Player或者Shift+Ctrl(Command)+B里的Player Setting按鈕)
面板里,,將最下方的Optimization欄目中“Api Compatibility Level”選為.NET 2.0 Subset,表示你只會(huì)使用到部分的.NET 2.0 Subset,,不需要Unity將全部.NET的Api包含進(jìn)去,。接下來(lái)的“Stripping Level”表示從build的庫(kù)中剝離的力度,每一個(gè)剝離選項(xiàng)都將從打包好的庫(kù)中去掉一部分內(nèi)容,。你需要保證你的代碼沒(méi)有用到這部分被剝離的功能,,
選為“Use micro mscorlib”的話將使用最小的庫(kù)(一般來(lái)說(shuō)也沒(méi)啥問(wèn)題,不行的話可以試試之前的兩個(gè)),。庫(kù)剝離可以極大地降低打包后的程序的尺寸以及程序代碼的內(nèi)存占用,,唯一的缺點(diǎn)是這個(gè)功能只支持Pro版的Unity。
2.托管堆優(yōu)化 Unity有一篇不錯(cuò)的關(guān)于托管堆代碼如何寫比較好的說(shuō)明,,在此基礎(chǔ)上我個(gè)人有一些補(bǔ)充,。
首先需要明確,托管堆中存儲(chǔ)的是你在你的代碼中申請(qǐng)的內(nèi)存(不論是用js,,C#還是Boo寫的),。
一般來(lái)說(shuō),無(wú)非是new或者Instantiate兩種生成object的方法(事實(shí)上Instantiate中也是調(diào)用了new),。
在接收到alloc請(qǐng)求后,,托管堆在其上為要新生成的對(duì)象實(shí)例以及其實(shí)例變量分配內(nèi)存,如果可用空間不足,,則向系統(tǒng)申請(qǐng)更多空間,。
都需要對(duì)其Destory(),然后新的金幣進(jìn)入臺(tái)子時(shí)又需要Instantiate,,這對(duì)性能是極大的浪費(fèi),。一種通常的做法是在不需要時(shí),不摧毀這個(gè)GameObject,,而只是隱藏它,。
如果不是必要,應(yīng)該在游戲進(jìn)行的過(guò)程中盡量減少對(duì)GameObject的Instantiate()和Destroy()調(diào)用,,因?yàn)閷?duì)計(jì)算資源會(huì)有很大消耗,。在便攜設(shè)備上短時(shí)間大量生成和摧毀物體的
話,很容易造成瞬時(shí)卡頓,。如果內(nèi)存沒(méi)有問(wèn)題的話,,盡量選擇先將他們收集起來(lái),,然后在合適的時(shí)候(比如按暫停鍵或者是關(guān)卡切換),將它們批量地銷毀并 且回收內(nèi)存,。
四,,如何管理資源:
Unity提供的就這些了,下面就自己發(fā)揮:如何做一個(gè)方便的資源管理方案,,既可以開(kāi)發(fā)時(shí)方便,,又可以方便發(fā)布更新包呢?開(kāi)發(fā)過(guò)程全用AssetsBundle是不合適的,,因?yàn)殚_(kāi)發(fā)中資源經(jīng)常添加和更新,,每次添加或者更新都生成一下AssetsBundle才能運(yùn)行是很麻煩的。而且我們要做的是自動(dòng)更新而不是分包下載,,這也就是說(shuō)在發(fā)布游戲的時(shí)候這些資源應(yīng)該都是在游戲包中的,,所以他們也不該從AssetsBundle加載。
分析完需求,,方案也就出來(lái)了:資源還是放在Resources下面,,但是這些資源同時(shí)也會(huì)打包到AssetBundle中。代碼中所有加載資源的地方都通過(guò)自己的ResourceManager來(lái)加載,,由ResourceMananger來(lái)決定是調(diào)用Resources.Load來(lái)加載資源還是從AssetsBundle加載,。在開(kāi)發(fā)環(huán)境下(Editor)這些資源顯然是直接從Resources加載的,發(fā)布的完整安裝包資源也是從Resources加載,,只有當(dāng)有一個(gè)增量版本時(shí),,游戲主程序才會(huì)去服務(wù)器把增量的AssetBundle下載下來(lái),然后從AssetBundle加載資源,。
比較合理的做法是根據(jù)邏輯來(lái),,例如每個(gè)角色可以有獨(dú)立的AssetBundle,公用的一些UI資源可以打到一個(gè)AssetBundle里面,,每個(gè)場(chǎng)景獨(dú)立的UI資源可以打成獨(dú)立的AssetBundle。這樣做資源預(yù)加載的時(shí)候也方便,,每個(gè)場(chǎng)景需要用到幾個(gè)Bundle就加載幾個(gè)Bundle,無(wú)關(guān)的資源不會(huì)被加載,。
下面要考慮的是如何來(lái)確定一個(gè)資源是從Resources加載還是AssetBundle加載。為此我們需要一個(gè)配置文件resourcesinfo,。這個(gè)文件隨打包過(guò)程自動(dòng)生成,。里面包含了資源版本號(hào)version,所有包的名字,,每個(gè)包的HashCode以及每個(gè)包里面包含的資源的名字,。HashCode直接可以從Unity生成的manifest中得到(AssetBundleManifest.GetAssetBundleHash),用來(lái)檢查包的內(nèi)容是否發(fā)生變化,。這個(gè)resourceinfo每次打包AssetBundle時(shí)都會(huì)生成一個(gè),,發(fā)布增量時(shí)將它和新的Bundle一起全部復(fù)制到服務(wù)器上。同時(shí)在Resources文件夾下也存一份,隨完整安裝包發(fā)布,,這就保證了新安裝游戲的玩家手機(jī)上也有一份完整的資源配置文件,,記錄了這個(gè)完整包包含的資源。
當(dāng)游戲啟動(dòng)時(shí),,首先請(qǐng)求服務(wù)器檢查版本號(hào),,前端用的版本號(hào)就是Resources下面的這個(gè)resourcesinfo中的version。服務(wù)器比對(duì)這個(gè)版本號(hào)來(lái)告訴前端是否需要更新,。如果需要更新,,前端就去獲取服務(wù)器端的新resourcesinfo,然后比對(duì)里面每個(gè)bundle的HashCode,,把HashCode不同的bundle記錄下來(lái),,然后通過(guò)WWW類來(lái)下載這些發(fā)生改變的bundle,當(dāng)然如果服務(wù)器版的resourcesinfo中包含了本地resourceinfo中所沒(méi)有的Bundle,,這些Bundle就是新增的,,也需要下載下來(lái)。所有下載完成后,,前端將這個(gè)新的resourceinfo保存到本地存儲(chǔ)中,,后面前端的所有操作都將以這個(gè)resourceinfo為準(zhǔn)而不再是Resources下面的resourceinfo了。
加載AssetBundle,,我們直接使用WWW類而不用WWW.LoadFromCacheOrDownload, 因?yàn)槲覀兊馁Y源在游戲開(kāi)始的時(shí)候已經(jīng)下載到外部存儲(chǔ)了,,不要再Download也不要再Cache。注意WWW類加載是異步的,,在游戲中我們需要同步加載資源的地方就要注意把資源預(yù)加載好存在ResourceManager中,,不然等用的時(shí)候加載肯定要寫異步代碼了。大部分時(shí)候我們應(yīng)該在一個(gè)場(chǎng)景初始化時(shí)就預(yù)加載好所有資源,,用的時(shí)候直接從ResourceManager的緩存取就可以了,。
|