您當前的位置:首頁 > 收藏

關於Unity中的資源管理,你可能遇到這些問題

作者:由 張鑫 發表于 收藏時間:2016-12-12

原文連結:關於Unity中的資源管理,你可能遇到這些問題 - Blog

在最佳化Unity專案時,對資源的管理可謂是個系統紛繁的大工程。鑑於Unity獨特又絕(cao)妙(dan)資源打包的AssetBundle管理機制,不同資源的屬性適合於不同的儲存和載入方式。此外,要處理好成百上千個資源之間的相互依賴關係也非易事。誰說良好的資源管理不是個藝術呢?:)

——————————————————

關鍵字

AssetBundle

資源製作 紋理\網格\材質\Shader\音訊\動畫

Lightmap

一、AssetBundle 相關

Q1:Unity中的SerializedFile是怎麼產生的?請問用Unload(false)可以清除嗎?因為讀取了Bundle裡面的內容後已經賦值給其他物體了。而且我把圖片都打成了Bundle,然後讀取出來,圖片的大小應該是超過了這個SerializedFile的大小的?

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

SerializedFile是AssetBundle載入時產生的序列化資訊,一般為LoadFromCacheOrDownload、LoadFromFile和New WWW載入本地AssetBundle檔案所致。如果AssetBundle中的資源已經載入,且後續沒有依賴該AssetBundle的資源進行載入,那麼可以透過Unload(false)將其刪除。SerializedFile中記錄的是AssetBundle的序列化資訊,而不是其包含資源的內容,因此,其大小要小於或遠小於資源的實際記憶體。

Q2:我們現在採取了2種資源管理方式, 我想了解下記憶體佔用問題。 方式A:AssetBundle載入好以後在記憶體中保留, 如果需要建立物件就透過Instantiate來建立。方式B:AssetBundle載入好以後立刻透過Instantiate例項化一個物件, 然後Unload(false)這個AssetBundle,如果需要,建立物件透過clone來完成。對於方式A,在Profiler中發現 WebStream中有相關AssetBundle檔案存在, 我們認為這份記憶體是多餘的, 所以就採取了方式B, 方式B又面臨資源解除安裝不乾淨的情況。想確認一個問題, 如果採取方式A: 一個 xxx.assetBundle原始檔案大小是 1MB, 解壓到Webstream記憶體中是2MB, 最終實際記憶體佔用是2MB 還是3MB 呢?

如果採取方式A:最終實際記憶體佔用是2MB,而不是3MB,WebStream中已經包含原始AssetBundle的資料。對於沒有依賴關係的AssetBundle檔案,我們推薦方式B的形式對AssetBundle進行解除安裝,這樣可以免除不必要的記憶體佔用,對於這種方式加載出來的資源,可以透過Resources。UnloadAsset和Resources。UnloadUnusedAssets來進行解除安裝,如果無法解除安裝,則該資源一定被快取了,研發團隊可透過檢測自己的快取池/Constainer來進行檢測。同時,如果是Unity 5。3以後版本,可嘗試透過Memory Profiler來進一步檢視。

Q3:打包時候AssetBundle的md5總變化(被打包的東西沒變),請問能怎麼解決?有說法是加上DeterministicAssetBundle就可以,但是我嘗試後發現md5依然變化。

該方法確實並不受用。對於Unity 4。x版本的AssetBundle檔案,其md5值在某些情況下確實會前後不一致(哪怕是完全一樣的內容進行打包)。對於該系列版本,僅能建議開發團隊建立配置檔案來對AssetBundle進行管理。

而對於Unity 5。x版本,則可以在打包時開啟 AppendHashToAssetBundleName 選項,這樣Unity引擎會在每個AssetBundle檔案後生成一個唯一的HashID(顯示地放在檔名後),開發團隊可以透過該ID來判斷對應的AssetBundle檔案是否發生改變。

Q4:如果採用依賴打包的話,比如NGUI,圖集A作為被依賴包,介面1、2、3作為獨立包,分別依賴打包。當圖集A重新打包的時候,是不是介面1、2、3也都要重新打包?目前我們是介面打包時清空全部的圖集資訊再打包,在客戶端載入後再動態賦值回來。這樣可以實現單獨更新圖集,但是代價就是載入的效能。請問有什麼更好的解決方案呢?

“當圖集A重新打包的時候,是不是介面1、2、3也都要重新打包”,這是不需要的,Unity 4。x的依賴打包的限制在於,在重新打一個包時需要將它依賴的包都重新打一次,但不需要重打依賴它的包。

Q5:請問內建的shader怎麼打包?我用到了內建材質球,不只是Shader,這時候在Profiler中看到載入的結果中會出現多份,如下圖所示

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

通常有兩種方式對內建的Shader進行打包:

將其新增到Graphics Settings中的Always Included Shaders 中,此時新增後的內建Shader就不會被打入AssetBundle包中;

在Unity - Get Unity - Download Archive下載內建的 Shader,將其匯入專案,並替換成非內建的材質球,從而可以直接透過指令碼來控制其打包的方式。

Q6:請問粒子特效的Shader是否不能使用依賴打包? 我們對Shader的模型和特效使用了依賴打包,執行的時候發現模型顯示是正常的,但是粒子特效使用的Shader就不能正常執行,特效顯示不正常。而在編輯器中,我們看到Material中的Shader是存在的。這時候如果重新手動給這個Material指定同樣的Shader,這個粒子特效就能正常顯示,請問這是什麼原因引起的?

部分 Shader 在打包到 Android 版本的 Assetbundle 之後,會因為平臺不相容而無法正確顯示,這是因為打包後的 Shader 程式碼只保留了目標平臺的預編譯程式碼,不一定能夠在 Editor 下執行,所以這是正常現象。 但這並不會影響依賴打包,因為在真機上並不會出現類似的問題。

Q7:Resource的場景下有兩個場景Scene1.unity和Scene2.unity。我要對這些檔案進行打包,生成了

Scene1。assetbundle

Scene1。assetbundle。meta

Scene2。assetbundle

Scene2。assetbundle。meta

如果我有相同的資源,理論上它會在這兩個包裡各存一份,這樣就造成了包體過大。所以有沒有辦法把共享資源做成依賴項單獨打包,這樣的話每個場景就不會過大了。Unity 5.x的BuildAssetBundles打包機制是否和Unity 4.x不一樣?原來的打包機制已經被剔除了嗎?

用Unity 4。x 的 Push/Pop 是可以抽出相同的資源,並且據我們所知該方法在Unity 5。x 中也受用。根據Unity 5。x新的打包機制,只要把相同資源的 AssetBundle Name 設定好,打包時就會自動抽出來。

Q8:現在生成AssetBundle的時候每個檔案會多生成一個Manifest檔案,這個檔案也需要一起隨著AssetBundle上傳嗎,在資源載入的時候具體怎麼用呢?

每個檔案多生成的Manifest 檔案是不需要上傳的,其作用就是供開發人員檢視AssetBundle 中的依賴關係等資訊。

但除了每個檔案多生成的 Manifest 以外,根目錄下還會有一個與根目錄同名的AssetBundle 以及 Manifest 檔案,透過執行時載入這個AssetBundle,可以得到一個 AssetBundleManifest 物件,然後就可以透過這個物件得到AssetBundle直接的依賴關係。

更多資訊可以參考AssetBundleManifest

Q9:如果我有一個Prefab,它的Dependencies都在Resources資料夾中,那麼,當我在AssetBundle打包時,只打包這個Prefab(不指定BuildAssetBundleOptions.CompleteAssets和BuildAssetBundleOptionsCollectDependencies)的話,這個Prefab能夠正確例項化嗎?

這是不能正確例項化的,因為AssetBundle中的資源和Resource資料夾下資源是不會建立依賴關係的(指令碼除外,因為開啟BuildAssetBundleOptionsCollectDependencies 時,指令碼依然不會打包到AssetBundle中)。所以會出現Mesh、Material等的丟失。

Q10:我們透過AssetBundle預載入Shader後,並沒有解除安裝AssetBundle,但是發現後面載入的Object並沒有引用到正確的Shader,這可能是由於什麼原因呢?

很可能是專案中AssetBundle的依賴關係打包不正確。後續載入的AssetBundle都需要與Shader的AssetBundle檔案進行依賴,這樣Unity引擎才會在載入後續AssetBundle時,將Shader進行關聯。

建議開發團隊透過UWA資源檢測來檢測下AssetBundle檔案的依賴關係。主要檢視兩處,一個是Shader是否被冗餘打包;一個是其> 他的AssetBundle是否與Shader的AB進行正確的依賴。具體檢測效果如下:

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

如下圖紅框所示,開發團隊可以直接檢視Shader以及其他資源在AssetBundle包中的冗餘情況。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

Q11:我遊戲裡重複的特效較多,有些只是圖案相同但改變了顏色引數,如果都打成獨立AssetBundle,則記憶體裡面會有多份Texture。關於這樣的打包一般有什麼推薦的方法呢?

如果是相同內容且僅是整體顏色不同的話,那麼建議專案中只保留一份初始紋理資源,並透過Shader在執行時改變其整體配色,從而達到不同的效果。但如果是區域性配色不同,那麼可以在原始紋理的基礎上加一種或幾種Mask紋理,用來負責顏色的自適應調配,然後再透過Shader來達到不同的展示效果。

Q12:請問一下,我兩個預設都引用了第三個AssetBundle的貼圖,如果不希望這張貼圖存在兩份,一定要等這兩個預設都載入好了,才能解除安裝貼圖的AssetBundle嗎 ?

是的,但並不是因為這樣做會使“這張貼圖存在兩份”,而是因為如果先解除安裝貼圖的AssetBundle會導致後續載入兩個預設時會丟失依賴,即找不到貼圖。如果指令碼中會對這個情況進行檢查並重新載入貼圖的AssetBundle,那麼此時才會造成“這張貼圖存在兩份”的問題。

Q13:UGUI的圖集操作中我們有這麼一個問題,載入完一張圖集後,使用這個方式獲取其中一張圖的資訊:assetBundle.Load (subFile, typeof (Sprite)) as Sprite; 這樣會複製出一個新貼圖(圖集中的子圖),不知道有什麼辦法可以不用複製新的子圖,而是直接使用圖集資源 。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

經過測試,這確實是 Unity 在 4。x 版本中的一個缺陷,理論上這張“新貼圖(圖集中的子圖)”是不需要的,並不應該載入。 因此,我們建議透過以下方法來繞過該問題:

在 assetBundle。Load (subFile, typeof (Sprite)) as Sprite; 之後,呼叫

Texture2D t = assetBundle。Load (subFile, typeof (Texture2D)) as Texture2D;

Resources。UnloadAsset(t);

從而解除安裝這部分多餘的記憶體。

Q14:下圖一是剛進遊戲時獲取的資訊,第二張是開關幾次同一個UI介面後獲取,對比兩圖我們發現有多份重複的Texture。請問這是為什麼?我們的載入方式是UI透過AssetBundle載入,載入後會釋放AssetBundle,然後再次載入 UI 就會造成紋理資源的冗餘。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

剛進遊戲時獲取的圖中出現的“重複”資源可能並不是冗餘,因為 Atlas的一個 Group 中可能包含多張一樣大小的Page(即紋理),而這幾個Page在記憶體中的名字是一樣的。

但是,如果同一UI介面多次開啟後,記憶體中出現了更多同樣的資源,則說明UI的管理方式存在一定問題。對於頻繁使用的UI,我們建議在載入之後透過緩衝池對其進行快取,後續使用時,直接透過緩衝池獲取即可。而不要每次均透過AssetBundle進行載入,這種做法既會造成更大的CPU佔用,同樣會很大機率造成資源的冗餘。

同時,如果多次開啟的是不同UI介面,並且造成記憶體中同種資源的增加,則很有可能是UI在AssetBundle打包時形成了冗餘(這種情況在目前的UGUI系統中較為常見)。對此,如果開發團隊使用的是UGUI,那麼我們的建議如下:

對於使用Unity 5。x的新AssetBundle打包系統,則打包時儘可能將同種Atlas的UI介面打成一個AssetBundle檔案,否則將很有可能出現資源冗餘的情況;

對於使用Unity 4。x的老AssetBundle打包系統,則可以將一個含有Atlas的Prefab(或其他Object)先打包,其他UI元素對其進行依賴即可。

此外,開發團隊可以考慮用

http://

WWW。LoadFromCacheOrDownload

來載入共享包,因為 new WWW 的方式會在記憶體中形成 WebStream 造成較多的記憶體開銷。關於該函式的具體優劣,開發者可以參考你應該知道的AssetBundle管理機制。

Q15:專案在釋出時,Player Setting中勾選的這個選項(Optimize Mesh Data),對於已經打包並且放到了Streaming Assets中的AssetBundle檔案有效果嗎?模型資源裡有個Optimize Mesh,是否能達到同樣的效果?

理論上 Optimize Mesh Data 是 Build Player 或者 Bundle 時才生效的,所以之前打好的 Bundle 就沒效果了。另外,兩個選項的效果是不同的,後者是調整面片排序的。

二、資源使用

紋理相關

Q1:關於貼圖型別設定請問有什麼好的建議呢?是否所有的貼圖都設定為Advanced比較好?這種情況下的貼圖記憶體是不是比較小?

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

Unity預設情況下會將絕大多數紋理設定為Texture模式。一般來說,Texture模式對於絕大多數紋理資源也都是合適的。上面的例子中,Advanced模式下之所以記憶體佔用較小,是因為關閉了Mipmap選項,所以其記憶體下降了。其本質跟Advanced模式無關。Advanced模式較之Texture模式和其他模式,可以更大自由地對紋理資源進行控制。因此,如果你想對紋理資源進行更加自定義地設定,可以選擇Advanced模式進行編輯。

Q2:同樣的包同一個圖集,ETC2格式,在紅米Note1上會比酷派的記憶體會大四倍,請問這是什麼原因造成的?如果不支援OpenGL 3.0,會造成這麼大的影響嗎?

ETC2 的格式理論上只在OpenGL ES 3。0 的裝置上被支援,而在不被支援的裝置上則會內部自動轉成 RGBA32/ARGB32的格式,這對於 RGBA Compressed ETC2 8bits 的紋理就是放大了 4 倍。因此,如果希望在 OpenGL ES 2。0 的裝置上對透明材質進行壓縮,那麼可以嘗試使用分離 Alpha 通道的方式,用兩個 ETC1 來進行壓縮。

Q3:我現在動態載入StreamingAssets下的貼圖,程式碼如下

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

我發現這種方式記憶體消耗很大,一張512 x 512的貼圖佔用了2MB,看官方的解析是記憶體和視訊記憶體各需一份。想了解下動態載入貼圖有什麼推薦的方式嗎?

一般來說,我們比較建議透過AssetBundle來動態載入資源,而非透過bytes流來進行載入。如果你的專案正在使用這種方式來載入紋理,我們建議從策略上考慮將其更改。在我們目前來看,透過bytes流來生成資源,絕大部分原因是想對其進行加密,從而讓資源難於破解。但其實這種加密方式用處不大,因為據我們所知,現在有很多工具可以直接透過底層顯示卡層來直接檢視各種紋理、Mesh資源,比如Mali Graphics Debugger、Qualcomn Profiler等。因此,如果是從加密的角度來透過bytes流生成資源,那麼我們建議透過AssetBundle這種直接的方式進行載入。

Q4:iOS平臺需要對圖集做RGB和Alpha通道的分離嗎?我發現在同樣大小的圖片(正方形),RGB Compressed PVRTC 4bits和RGBA Compressed PVRTC 4bits兩種格式,佔用記憶體是一樣的,如果把一張圖片分成兩張,那麼在iOS平臺是不是佔用記憶體多一倍?有透明通道的,對於它的圖集怎麼處理會更好一點?

通常iOS下是不需要做通道分離的,因為 iOS 通用的 PVRTC 格式支援 Alpha 通道。但目前也有團隊反饋,在 iOS 上進行通道分離有助於減少失真,可以在一定程度上提高視覺效果,因此也可以嘗試做一個對比。

如果發現佔用記憶體是一樣的,那麼原始圖片是RGB的。如果iOS上做通道分離,記憶體確實會增加一倍。UI的紋理在iOS下可以直接選擇預設的 Compress,在打Atlas時會自動處理成 PVRTC,開發團隊可以從Sprite packer視窗來看Atlas的壓縮格式做個確認。

Q5:在Unity 4.x的版本中,所有UI貼圖使用ETC2格式,即使目標裝置不支援該格式,也會解壓成RGBA32使用。 而在目前使用的Unity 5.3.4版本中,iOS平臺無法設定ETC2格式。如果壓縮只能使用PVRTC格式,那麼PVRTC存在顯示效果比較差,並且圖片必須為正方形,但如果用RGBA32格式,貼圖佔用的記憶體和儲存又都過大,請問目前版本的Unity在iOS平臺上應該如何設定UI貼圖的壓縮格式?

我們建議可以先嚐試其他的壓縮格式看是否可以達到類似的效果,如ASTC格式。ASTC 在 iOS 的高階機上是被支援的,因此理論上在 Editor 下不會強制把 ASTC 轉為 RGBA32,建議嘗試設定為 ASTC 後打包,從 Editor。log 中或者直接從包體大小上可以看出是否確實使用了 ASTC。

一般來說,如果 RGBA16 的效果可以接受的話,建議使用 RGBA16,雖然打包時相對大一些,但是記憶體中相比 RGBA32 能夠減半,但使用 ASTC 的話,雖然打包時比較小,但是在普通機型上會被處理成 RGBA32,導致過大的記憶體開銷。

Q6:請問Unity引擎中使用什麼貼圖壓縮格式,可以保證在佔用記憶體相對較小的情況下True Color效果和原圖相當?同時在iOS和Android平臺上圖片的壓縮格式分別用什麼比較合適?有什麼需要注意的地方嗎?

目前來講,並不存在一個所有GPU平臺都支援硬體解壓的壓縮格式。 ETC1 和 PVRTC 分別是Android和iOS上我們最推薦的格式。 但對於透明紋理,ETC1不支援,而 PVRTC 則可能有較大失真,因此更推薦使用 RGBA 16。

一般來說建議直接使用 Unity 預設的壓縮格式(即選擇 Compressed 即可,不需要做特殊設定),Unity 會做如下處理:

Android 上不帶Alpha通道的圖片採用 ETC1,帶Alpha通道的圖片採用True Color中的RGB16,TrueColor中的 RGBA16 會>比 RGBA32 更節省空間,但影象的顯示質量會差一些;

iOS 上使用 PVRTC,但PVRTC格式要求紋理的長寬相等,且都是2的冪次(即POT,在ImportSettings中可以將NPOT的紋理自動轉換成POT)。

另外,針對Android 上的帶Alpha通道的圖片,還有一種常見的做法,即把Alpha通道獨立出來作為另一張紋理,從而將 RGB 部分和 Alpha 部分分別採用 ETC1來壓縮,但渲染時就需要自定義的 Shader來處理。

同時,我們不建議直接使用 RGBA32 格式的紋理,因為它佔用了很大的記憶體。一般建議使用 RGBA16 和 ETC 格式的紋理來進行載入。 如果轉換到 RGBA16 格式時出現了類似“色階”的顏色問題,則建議儘可能避免大量的過渡色使用。

Q7:請問遊戲中特效使用的很多貼圖, 一般有什麼好的方式去管理嗎 ? 不合並圖集的話會有上千張小的透明貼圖, 合併圖集又會有佔用記憶體過多的問題。

可以合併成Atlas,一般將盡可能同時出現頻率較高的Texture合成Atlas,這樣並不會造成記憶體過大。在這方面也可以參考我們前不久推薦的外掛Mesh Baker。

Q8:iOS上方形POT圖片有時候會失真,請問這種情況如何避免?一張NPOT的圖變換成POT,是否有推薦的方法? 採用 ToLarger 的模式拉成POT是否會有損失呢?

在其他設定一致的情況下,這兩種方式無論在載入還是渲染方面其實並沒有實質上的差別。在我們接觸到的大多數案例中,紋理資源方面的問題除了尺寸外,紋理格式、Mipmap設定和Read&Write功能同樣是需要研發團隊時刻關注的。

Q9:紋理Atlas是建議合成一張2048(尺寸)的紋理還是四張1024的紋理?

在其他設定一致的情況下,這兩種方式無論在載入還是渲染方面其實並沒有實質上的差別。在我們接觸到的大多數案例中,紋理資源方面的問題除了尺寸外,紋理格式、Mipmap設定和Read&Write功能同樣是需要研發團隊時刻關注的。

Q10:NGUI的圖集在記憶體裡存了多份,求問怎麼清理?

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

遊戲執行中,UI Mesh出現多份不同記憶體的情況,是正常的,因為隨著UI widget使用的增加或減少,建立的UI Mesh是會隨著變化的。同時,如果不同UIPanel中存在相同Atlas的Widgets,則也會出現上圖中的情況。因此,建議大家遇到這種情況時,檢視單幀中NGUI UI Mesh重名的是否有多份重名資源。如果存在,則說明相同Atlas中的資源被多個不同的UIPanel所使用,這種情況是需要儘可能避免的。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

Q11:我在UWA報告中看到大量的n/a資源,其格式的高度和寬度等資訊都無法獲取,並且存在大量重複,請問我該如何定位這些檔案?能否最佳化解決呢?

“圖中n/a資源是我們在專案執行時檢測到的無名稱資源,一般來說,該型別資源並非Asset資料夾中的美術資源,而是專案透過指令碼動態生成的資源,比如new Mesh、new Texture等操作,即會生成這種型別的無名稱資源。

對此,我們建議研發團隊詳細檢測邏輯程式碼/第三方外掛中諸如New Mesh/Textue等此類操作,同時,在指令碼動態生成該類資源後,為其賦上一個名字,這樣即可在後續測試中看到對應名稱的資源使用情況。”

網格相關

Q1:如果一個模型對應Skinned Mesh Renderer例項,那其所佔的記憶體會隨著角色增加而增長麼 ?

簡單地從一個角色Prefab例項化(Instantiate)出多個例項時,Mesh並不會出現多份(這個行為與其他資源是一致的,包括Texture,AnimationClip,Material等等)。如果在記憶體中發現多份,可以考慮從專案中AssetBundle的載入方式入手,因為即使是同一個AssetBundle中的同一個角色Prefab,如果被反覆進行“載入-例項化-解除安裝”操作,依然是會導致Mesh出現多份的(當然其他的資源也是一樣)。

Q2:我有一個特效依賴了兩個FBX。我把這兩個FBX的這個勾選去掉,Editor執行正常。否則就會宕機,請問這是什麼原因?

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

將FBX上的Read/Write Enabled關閉後,記憶體中便不再儲存其Mesh的副本(只存在視訊記憶體中),因此其屬性就不可再被訪問和修改。而粒子系統通常需要動態地修改其粒子的頂點屬性。因此,理論上來說,供粒子系統使用的Mesh是需要開啟Read/Write Enabled的,而在Editor下Mesh和Texture都是強制開啟的,所以在真機上就會出現問題。

Q3:MeshBaker 烘焙的Mesh可以儲存到Prefab中,但是不能像FBX一樣,設定Model匯入設定中的Generate Lightmap UVs 等資訊,請問有沒有大招可以處理此情況?

首先從理論上說,一個 Mesh 只能有一個 LightmapIndex 和 LightmapOffset 屬性,這就決定了 Mesh 的合併必須在 Lightmap 的烘焙之前。

而在做Mesh合併時,需要注意到UV2對於一個Mesh而言,其所有三角面的UV區域都必須是互不重疊的,所以不能簡單直接地合併。

考慮到合併前每一個Mesh的UV2區域都應該是在[0,1]x[0,1]的區間中,在合併時可以給每一個UV2區域做一個縮小和平移,從而可以把每個區間在互不重疊的情況下,放到同一個[0,1]x[0,1]的區間中。

在UV2也正確拼合後,重新進行Lightmap的烘焙即可得到正確的效果。

動畫片段相關

Q1:我想要在Editor下批次地對Animator Controller檔案中每一個的State裡的Animation Clip進行替換,但好像沒有看到 Unity引擎有提供類似的API,只提供了能在Runtime時進行替換的方法,類似下圖:

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

我現在想要達到的目地是在Editor下寫一個批次替換Animation Clip的外掛,請問下Unity引擎是否有提供這樣的介面呢?

在Unity 5。x 中已有一套完整且穩定的 API 可以訪問、修改和建立 Animator 中的任何元素。其名稱空間是在 UnityEditor。Animations 下,以下Blog 中有一個簡單的例子是透過指令碼建立一個 AnimatorController 以及其中的各種屬性和引數。Shiny new animation features in Unity 5。0 – Unity Blog

另外,如果需要修改Animation Clip,可以直接修改一個 AnimationState 的 Motion 屬性。 Animation Clip可以繼承自 Motion。

Q2:如果我的Animator是直接引用了FBX裡的動畫檔案,而不是複製了FBX的動畫檔案出來再引用,那麼打包的時候會把FBX都打包嗎?

這種情況下並不會把 FBX 打入 AssetBundle 中。將動畫檔案Copy出來通常是為了直接對動畫檔案打包,作為依賴包。

Q3:我們的動畫是放在FBX檔案裡的

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

這樣做導致打包的時候把一些無用的檔案也打進AssetBundle包裡了,實際上我們只想使用最後這個動畫檔案。但在編輯器裡選中FBX檔案裡的動畫檔案時卻沒有AssetLabels這個視窗,設定不了AssetBundle Name。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

是不是隻能用程式碼設定?還是意味著不能設定AssetBundle Name,只能把動畫檔案提取出來,類似下圖這樣單獨的一個檔案?

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

在Unity 5。x 的打包機制下確實無法手動為 FBX 下的 Mesh 或 AnimationClip 單獨資源設定 AssetBundle Name。因此,如果需要將這部分資源抽出來作為依賴包,目前確實就需要先透過指令碼將這部分資源提取出來了。

音訊相關

Q1:請問音訊中的 Quality 什麼意思?一般設定為多少合適?我拖進去一首歌曲,試了一下 在0 和 100 的情況下區別不大,但是生成的音訊檔案大小差別很大。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

Quality 表示在壓縮音訊時的失真程度(實際上可以認為是壓縮演算法的一個引數),該值越大,壓縮後的檔案越大,但音質保留的越好。而對於其失真的程度是視音訊資料以及內部的壓縮演算法而定的,確實會有區別不大的情況。該值的設定原則就是,在音質失真可接受的情況下,越小越好。

材質相關

Q1:我們發現材質例項數量特別多,想問下這個對效能的影響如何,有沒有什麼建議?

Material的記憶體佔用一般很小,所以大量的Material資源對於記憶體的壓力其實很小的。但是,它本身對於場景的切換時間是有影響的,即資源冗餘得越多,切換場景時,UnloadUnusedAssets的開銷越大,進而增加了場景的切換時間,同時也會影響DrawCall的拼合。所以,建議研發團隊儘可能定位資源冗餘的原因,並對其進行修復和完善。這一點,我們在UWA效能測評報告中的“分析和建議”中都有詳細說明。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

Q2:我在UWA上進行了效能檢測,在資源記憶體這裡看到詳情如下。請問這個數量峰值大於1是不是就是有問題的?但一個Material是很可能被例項化多份的。例項化後物件釋放掉,然後清理掉原始的Material,按道理就不算冗餘吧?畢竟大部分都是材質在操作,對貼圖資源應該沒有影響。而且,如果確實有那麼多角色同屏怎麼辦呢?

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

是的,如果數量峰值>1,則說明存在資源冗餘的風險。如果是Material例項化,那麼後者是有個(instance)字尾的,比較好識別。如果是new Material(Shader)出來的,那麼是沒有instance字尾的。但無論是哪種方式,如果數量峰值較大,都應引起大家注意。一般都是可以透過其他方法來儘可能避免冗餘的。

如果變化不是隨機的,且Material引數變化情況比較少,那麼可以根據引數的變化情況只做幾種Material,然後透過指令碼動態賦值。如果是隨機或者動畫,那就沒什麼辦法了,要麼改需求,要麼就接受這麼多。

Shader相關

Q1:我用內建的Shader渲染場景,深度圖裡有內容。而用自己的Shader,取到的深度圖什麼都沒有,都是1,什麼原因導致的呢?我已經開啟ZWrite了。

Unity的_CameraDepthTexture 的生成,會根據Rendering Path的選擇和裝置的不同使用不同的方法實現,一種是直接從depth buffer獲取,另一種是新增額外的Pass來渲染到紋理。目前在移動裝置上主要是通過後者實現,而後者的實現藉助了Shader Replacement的機制(批次替換為簡單的Shader,並將深度渲染到紋理中)。

因此,在使用自定義的Shader時,就需要正確地設定RenderType的Tag(所有內建Shader都是設定好的),從而使得Shader Replacement正確地執行。具體可見文件:Rendering with Replaced Shaders

Q2:我們的遊戲使用 Spine 外掛,因為要用到裁切動畫,所以修改了Shader,但在使用的時候出現異常: Shader wants normals, but the mesh Skeleton Mesh doesn't have them,可能是什麼原因?

開發團隊需要注意:Surface Shader在生成程式碼中預設會處理normal(即 Spine/Skeleton 實際上是需要 normal 的),而對應的Mesh並沒有包含normal,所以在預覽窗口裡渲染的時候會檢查 Mesh 是否包含 normal 資訊,沒有的話會報這個錯誤。

開發團隊可以嘗試編寫一個Vertex & Fragment Shader 從而避免處理normal,也可以嘗試建立帶normal的Mesh,來避免該問題。

Q3:以前端遊時代,材質根據Pass不同、光照環境不同可以離線預編譯成ShaderCache,執行時並不需要拼材質再實時編譯,只要載入二進位制程式碼就好了。那Unity有沒有做這件事呢?我們是根據平臺和環境預編譯的Shader。

對於支援 Binary Shader 載入的裝置,在首次編譯某個 Shader 的時候是會生成其對應的 Binary Shader Cache ,生成的 Binary 檔案位於和 Application。persistantPath 並列的Cache 目錄下。

Q4:相同效果前提下,就效能而言,Shader 是用 V&F 還是Surface好?

Surface生成的V&F比較龐雜,分支較多,如果不注意 #pragma surface 引數的選擇,容易出現不必要的開銷。舉例來說,如果直接用 Unity 5。x 中預設建立的 Surface Shader (預設引數為 #pragma surface surf Standard fullforwardshadows),那麼 Shader 是會做 Physically based Standard Lighting 的,而這在移動端開銷非常大,且並非必要。

Q5:某個Shader裡設定了Culling Off,會影響到後面所有Shader的渲染狀態麼(假設後面不再設定Culling)?

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

一次 Draw Call 提交所相關的 Render State 是不會影響到下一次的渲染狀態的。如果不在 Shader 中顯示指定 Cull 模式,則會使用預設的 Cull Back。

Q6:我們將Shader放到了Resource的目錄下,也已加到Editor的GraphicSetting裡,也試過在載入一個空的Prefab時繫結對應的Shader。 但該Shader在Editor裡無法正常顯示,看執行時指向是有的,重新指一下就能顯示了, 然而打包以後在手機上顯示正常。

這確實是Unity已知的一個問題,Android 和 iOS 的部分Shader在打包後,在Editor 下無法正常顯示。 主要原因是在打包時,只會把對應平臺的Shader預編譯程式碼(如 gles )打入包中,因此在 Editor 下會執行失敗(通常 Editor 是 d3d 驅動)。 因此,目前只能嘗試在Editor下重新指定Shader來繞過這個問題。

Q7:我在shader裡這麼寫的程式碼

o。texcoord1 = vec2(mod(v。texcoord。x,1。0),mod(v。texcoord。y,1。0));

但是報錯 undeclared identifier 'mod' at line 106 (on d3d11),請問是什麼原因導致的呢?

報錯表明mod在d3d11的Shader中是未定義的,如果開發團隊無需在d3d11的平臺上使用該 Shader,則可以新增以下預編譯指令:

#pragma exclude_renderers d3d11

如果Shader依舊無法正常顯示,那可能是因為在Editor中使用的是 DX11(可以從標題欄中看出)。 可以嘗試修改DX9的引數 :Build Settings -> 點選 PC, Mac 。。。 -> Player Settings(不需要點選 Switch Platform) -> 去掉 Auto Graphics API for Windows 的勾選,只保留 Direct3D9。同時,開發團隊也可以直接使用 fmod 替換 mod,理論上 fmod 在各個平臺都是支援的。

字型相關

Q1:我們現在為了美觀,需要同時使用2套字型。但是每增加一套字型,就會記憶體增加50MB左右。請問你們在最佳化其他Unity遊戲時,怎麼處理類似情況的?

如果美術字不多的話,建議使用單獨的字型貼圖來進行實現,從而來達到特定美術字顯示的效果;如果美術字較多的話,那麼僅能建議使用另外一個套字型來進行實現。一般來說,字型的記憶體量應該也是完全可以控制在10MB以下的。

開發者可以直接在Unity Profiler中檢視字型的記憶體佔用,也可以透過UWA網站上測評報告中的具體資源資訊頁面檢視對應的字型資源使用情況。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

Q2:我做的預設用到了這個字型

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

我將這個預設打成AssetBundle包,透過Profiler分析時發現佔了兩份記憶體。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

下面這個SIMHEI應該是那個TTF的,為什麼會佔兩份記憶體呢?

通常TTF檔案會包含一個字型的多個字型,如可能包含正常字型、加粗字型、斜體字型等。而在Unity中會將其分為不同的Font資源,且他們之間會相互依賴。所以,如果專案中確實需要加粗字型的話,記憶體裡出現兩個Font是正常的,但如果實際上不需要加粗,那麼可以嘗試尋找一個不包含加粗字型的字型檔案來替換該TTF檔案。

粒子系統

Q1:我在UWA上提交了資源檢測,資源打的是依賴包,報告顯示Default—Particle這個資源存在大量冗餘,這個是正常的嗎?

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

Default—Particle 這個是粒子系統的預設資源。如果使用的是預設的粒子系統,沒有對Material進行修改,且每個粒子系統都是打一個AssetBundle檔案的話,那麼該冗餘問題是正常的。對此,我們建議開發團隊把下圖中預設的Material換成自己的資源,不使用Built-in資源,這樣透過依賴關係打包,就不會出現該資源冗餘的問題了。

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

Lightmap

Q1:Unity 5.x 能把指定集合的Mesh烘焙到一張Lightmap裡嗎?

Unity 5。x 是沒有這樣的功能項的。據我們的判斷,這樣的需求通常是為了動態載入,因此可以嘗試將Mesh分組到不同的場景分別烘焙。而在載入時可透過指令碼動態地合併Lightmap,同時將物件的Lightmap Index進行正確的偏移即可。

Q2:Lightmap在Baked GI的等待時間比較長(Realtime GI已關閉),想請教有沒有什麼建議的引數或是方式,可以縮短等待的時間?

目前就我們的瞭解,在Unity 5。x比較影響烘焙時間的主要是大面積的面片導致Light Transport 過程過久(Enlighten 的機制所限)。可以嘗試拆分面積較大的面片,來提高烘焙的速度(通常,拆分大面積的面片對渲染效能也會有所提升)。

主要原因可參考如下的帖子: Light transport problem with large objects

Q3:如下圖所示,第一張顯示的是沒有烘培Lightmap的場景效果,裡面有一盞點光源;第二張顯示的是烘培Lightmap後的場景效果。請問為什麼同一盞點光源照亮的效果差別那麼大?

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

關於Unity中的資源管理,你可能遇到這些問題

簡單來說,這就是實時的直接光照和全域性光照的差別。

在渲染時前者只能處理光源和單個物件之間的直接光照,而後者在烘焙時是透過光線跟蹤或者輻射度等複雜演算法,計算出所有物體各個表面之間相互反射的光照資訊,這也是烘焙Lightmap需要較久的時間的原因 。可以發現在全域性光照下,即使是物體的背面也會因為其它表面的反射而被照亮,這在直接光線下就無法實現這樣的效果。

Q4:當關閉預渲染GI時會出現IndirectResolution,這個引數有什麼用,為什麼調大了以後會大大增加渲染時間,但是烘培出來沒有啥效果。

簡單來說,該值主要控制的是GI的烘焙密度,數值越大,表示每個單位距離內的texel越多,即烘焙得越精緻,自然烘焙的時間也越長。該值並不需要越大越好,場景越小,建議該值越低。該值為1時,對於多數場景,其烘焙效果已經足夠了,升高該值,其效果也不會有明顯提升。

開發者也可以參考Unity官方的說明文件:

Unity - Manual: Lighting Window

Q5:Lightmap丟失。用Unity5.1.2的AssetBundle做熱更新,資源匯出的時候分析了所有的依賴項單檔案匯出。比如在匯出場景的時候場景的烘焙出來的LightmapSnapshot.asset檔案匯出不了,導致執行的時候場景的Lightmap丟失了。

LightmapSnapshot。asset 本身是Editor下使用的,並不能單獨被打包進AssetBundle。 執行時載入Lightmap,一種方法是把場景(。unity 檔案)打成AssetBundle載入,Lightmap 資訊會打入場景AssetBundle(因為Lightmap資訊和場景繫結)。另一種是透過Lightmapsettings。Lightmaps方法來執行時設定。需要注意的是,同時還需要重設Prefab的Lightmap資訊(Lightmapindex和Lightmapscaleoffset),因為Lightmap資訊在Unity 5。x下不會儲存在Prefab 上。

另外存在一種可能,Unity 5。x中加入了Shader Stripping功能,在打包時,預設情況下會根據當前場景的Lightmap及Fog設定對資源中的Shader進行程式碼剝離。這意味著,如果在一個空場景下進行打包,則Bundle中的Shader會失去對Lightmap和Fog的支援,從而出現執行時Lightmap和Fog丟失的情況。 而透過將Edit->Project Settings->Gaphics下Shader Stripping中的modes改為Manual,並勾選相應的Mode即可避免這一問題。

Q6:Lightmap在PC上顯示正常,但是轉到Android平臺上存在色差,顏色普遍偏暗。

一般來講,有兩種情況可能會導致色偏和亮度差異。

Unity烘焙的Lightmap是32bit的HDR圖,而移動裝置通常不支援HDR圖(32bit per channel),會按照LDR圖(8bit per channel)的形式進行處理,因此會出現色偏問題。因此我們建議:

1)在移動平臺下使用Mobile/Diffuse材質,可載入Standard Assets(Mobile) package獲得。如果要獲得更合適的效果,需要自行修改Lightmap的DecodeLightmap函式,該函式可在Unity\Editor\Data\CGIncludes\UnityCG。cginc檔案中找到。需要說明的是,這種方法也不能達到與PC端完全一致的效果。

2)如果需要PC和移動平臺的顯示效果一致,可以用影象編輯軟體修改Lightmap為LDR格式,例如PNG(8bit per channel)。

3)為了避免類似問題,請不要使用過於強烈的Light進行烘焙,因為Light的強度(Intensity)越高,色偏問題會越嚴重。若有陰影丟失時,可以嘗試檢查一下模型的Lightmapindex、Lightmapscaleoffset、UV2等影響Lightmap取樣的一些引數。

另一種可能是存在過曝現象,可以嘗試將playersettings -> use direct3d 11關閉,看問題是否解決。

Q7:在同一場景裡烘培的Lightmap,我用了2張10241024的光照圖,大小是5.3MB;別人用了3張10241024的圖,大小是4.3MB。請問是什麼影響這個光照圖的大小,在哪裡調?

首先,請確認下Lightmap的型別,Single型別只生成一張,而Dual和Directional會生成兩張。 其次,請確認下當前的釋出平臺,Android下的Lightmap會比Standalone更小。因為不同平臺採用的壓縮格式不同。此外,Lightmapping中的Lock Atlas,Resolution,Padding等選項也會影響最後烘焙光照圖的大小。

Q8:在遊戲中,有些Mesh在編輯時候是接收Lightmap的,出於某些原因我們合併了相同的Mesh(材質也相同)。但是發現原先的Lightmap不再影響合併後的Mesh,請問怎麼才能實現讓合併後的Mesh也接收原先的Lightmap?

如果Lightmap不止一個的話,手動合併Mesh是會出現問題的,因為合併的Mesh烘焙資訊很可能出現在不同的Lightmap中,但合併之後的Mesh在渲染時只能使用一個Lightmap,這樣uv2讀取到Lightmap資訊就會出現問題,進而出現這種現象。其實,對於材質相同的Static物體並不需要手動對其Mesh進行combine,因為Unity的Static Batching會自行完成。而如果由於某種特定需求一定要將Mesh進行合併的話,那麼也要將其所需要的Lightmap也一併合併,同時改變相應的uv2。不僅如此,Shader中Lightmap也需要進行相應修改,這是比較複雜的,所以我們並不建議這樣的做法,因為可能會花掉開發團隊大量的開發時間。

Q9:當關閉預渲染GI時會出現IndirectResolution,這個引數有什麼用,為什麼調大了以後會大大增加渲染時間,但是烘培出來卻沒有什麼效果。

該值主要控制的是GI的烘焙密度,數值越大,表示每個單位距離內的Texel越多,即烘焙得越精緻,自然烘焙的時間也越長。

該值並非越大越好,場景越小建議該值越低。該值為1時,對於多數場景,其烘焙效果已經足夠了,升高該值,其效果也不會有明顯提升。

標簽: AssetBundle  Shader  Unity  打包  載入