UGUI的圖集處理方式-SpriteAtlas的前世此生

最糟糕的是人們在生活中常常受到錯誤志向的阻礙而不自知,真到擺脫了那些阻礙時才能明白過來。 —— 歌德

說到UGUI的圖集初學者可能以爲沒什麼難度,包括我剛開始接觸的時候也是,甚至你在開發的時候只須要把圖片導入到項目中,拖拖拽拽就能作出能用的東西來。由於UGUI剛出的時候就打出了「Unity會自動幫你維護圖集」的旗號。可現實真的是這樣的嗎?要解釋這個問題就須要從Unity4.6提及了,那咱們來捋一下!程序員


Sprite Packer

散圖的加載問題

這功能是從Unity4.6版本隨着UGUI的問世一塊兒發佈的,咱們在作開發的時候只須要把圖片導入工程,設置一下,而後在經過tag標籤Unity就會自動打成圖集。可是,這種方法有個問題,就是咱們在運行時沒法經過代碼取出某張圖集中的一張小圖。算法

因此後來開發者們作了一套prefab引用sprite的解決方案,作法就是:建一個空prefab掛一個自定義腳本,腳本里有一個sprite數組,咱們在編輯時把要打的圖集中的sprite添加到這個sprite數組中,而後在把prefab打成bundle,這樣這個bundle中的腳本就會有atlas中的Sprite的引用,咱們在腳本里寫一個GetSprite方法便可。這樣咱們在運行時加載完bundle後就能夠經過腳本取出atlas裏的sprite。數組

using System.Collections.Generic; using UnityEngine; public class Test : MonoBehaviour { public List<Sprite> sprites; public Sprite GetSprite(string spriteName) { return sprites.Find(s => s.name.Equals(spriteName)); } } 

可是,講真,就這麼個函數Unity提供一個API很難嘛!這個問題致使從Unity4.6到Unity2017.1每次作圖集動態加載的時候都要用上面的方法搞一遍。固然要是僅僅是這點工做量也就忍了,重要的是這麼作還會影響到打包、依賴的管理。打包依賴又是個Unity的大坑。。。一個坑跳到了另外一個更深的坑。。。固然也是有解決辦法的,打包依賴問題咱們之後再專門講,這裏就很少說了。編輯器

圖集的拼接算法問題

SpritePacker的TightPackerPolicy(緊湊),根本就不能用!SpritePacker打圖集時有兩個選項:默認、緊湊,然而使用過你會知道其實這個「緊湊」選項是不能用的!!打出來的圖會串,你用一張Image指定了一張Sprite A,運行時你會發現若是用「緊湊」打出來的圖集會把A的範圍算錯,表現就是顯示的圖片不是A,可能只包括A的一部分,其餘部分會顯示別的圖片,其實就是取A這張圖片的時候位置沒算對!!!最終你會發現也只能使用」默認」方式打圖集了。若是想優化圖集的空間利用率,只有一個辦法就是本身寫打圖集的排列算法,固然Unity也提供了這個接口!函數


Sprite Atlas

延遲綁定

SpriteAtlas是2017.1版本之後更新的一個新功能,它能夠把圖片「手動」打成一張圖集。若是你以爲這是Unity的一個新功能或者一個強化圖集的功能那你就錯了,若是僅僅是打張圖集也不必單獨搞這麼個玩意兒,其實這也是Unity在「還SpritePacker的賬」,固然咱們上面也講到了,能夠確定的一點是SpritePacker在商業項目中:是能用的!可是不可避免的你會爲了填補SpritePacker的一些坑。優化

SpritePacker時代「圖集」咱們是看不到的,由於設計者的初衷是想讓開發者徹底不用考慮圖集的事。可是,這跟遊戲開發時的動態加載圖集時矛盾的!咱們要動態加載就必需要知道atlas名字和sprite名,這樣才能在運行時動態的找到一張sprite!因此U3D程序員必需要清楚的知道你當前界面用的是哪atlas的哪一個sprite。可是按照SpritePacker的作法他的初衷應該是想把圖集幹掉(幹掉的意思讓開發者不用關心),只須要考慮sprite便可,可是這是行不通的!ui

SpriteAtlas其實就是爲了解決上面說的問題而發佈的功能。經過它咱們能夠將atlas和UIprefab「解耦」。同時,在Unity編輯器狀態下咱們仍然能夠利用未打成SpriteAtlas的Sprite進行開發。等開發完成咱們再發布成SpriteAtlas。Unity提供了所謂「延遲綁定」(late bind)的技術來讓咱們實現這個功能。具體作法以下:spa

在Unity2017.1以後,UI prefab的Bundle在被加載的時候會有一個回調函數被調用:SpriteAtlasManager.atlasRequested,這個委託的定義是這樣的:void RequestLateBindingAtlas(string atlasName, System.Action output);設計

(代碼不嚴格,當成僞碼看就行))code

SpriteAtlasManager.atlasRequested += (atlasName, output) => { string path;//省略,atlas bundle加載路徑  AssetBundle bundle;//省略,加載atlas的asset bundle  var atlas = bundle.LoadAsset<SpriteAtlas>(path + "/" + atlasName); output(atlas); } 

咱們須要在運行時把atlas綁定到當前的prefab中,這個綁定是經過第一個參數"atlasName"關聯的。若是prefab中引用了多張atlas他會被調用屢次,每次都會把atlasName傳過來,由「用戶」決定加載哪張atlas。最後調用output(atlas)把圖集傳給prefab。

延遲綁定的缺陷(bug)

Include in Build選項

根據實測,這個選項的含義是:選,打包時不考慮依賴。不選,打包時考慮依賴。

舉例:

文件夾"A"存放着要使用的圖片,SpriteAtlas文件「A_Atlas」爲文件A下全部圖片打成的圖集,Prefab文件「A_Prefab」引用了A下的圖片。

此時,

若是選擇Include in Build,那麼打包的時A_Prefab打出來的bundle文件會包含A下面的圖片,A_Atlas打出來的bundle也會包含A下面的圖片,也就是說此時會打包雙份資源。這是咱們不想看到的。

若是不選Include in Build,只有A_Atlas打出來的bundle會包含A下的圖片,而A_Prefab不會包含A下的圖片。這樣資源只打包了一份,是咱們想要的結果。

可是,

有個地方須要注意就是,若是A_Atlas第一次勾選了Include in Build,打包後A_Prefab和A_Atlas的bundle都會包含圖片資源,可是若是此時再把A_Atlas的Include in Build的勾去掉從新打包,Unity只會把A_Atlas的bundle重打,由於Unity認爲你只修改了A_Atlas文件,而A_Prefab被認爲」沒有更改」,因此以前打進去的圖片資源依然會存在,正確的作法應該是:若是Include in Build修改了,那麼打包的時候全部用到這個SpriteAtlas的prefab也要從新打包(刪掉bundle文件便可)

Unity編輯器狀態下運行UI所有白圖(bug)

這應該是Unity的一個bug,在這裏分享一下!

在咱們的實測中發現:在加載資源的時候若是使用延遲綁定的方式顯示UI,在UnityEditor模式下UI的prefab加載顯示後全部圖片都是白圖!可是發佈到設備中能正確顯示。

莫非延遲綁定只能運行時使用?


Texture Packer

Texture Packer最終的解決方案,固然這也是很早之前就使用的方法了。若是Unity能把這件事作好,我相信你們都不想本身單獨用第三方軟件去搞圖集。TexturePacker具體功能和使用方法就很少說了,網上相關教程有不少。

這裏我只說一下TexturePacker解決了Unity的哪些問題:

1.圖集打包算法,選擇不少,也更專業。

2.動態加載圖片,簡單明瞭。編輯時加載:AssetDatabase.LoadAllAssetsAtPath()或者AssetDatabase.LoadAllAssetRepresentationsAtPath();運行時加載:AssetBundle.LoadAllAssets()

3.圖集的依賴關係,很直觀。

固然也有缺點:

須要維護TexturePacker工程文件。

總結一下:

SpritePacker,商業項目可用,須要本身作點東西「填坑」。

SpriteAtlas,商業項目可用,他解決了SpritePacker的小問題,可是本身有帶來了新問題,若是「白圖」的bug解決不了,可能須要單獨寫一套編輯時的加載流程。感受和SpritePacker填的坑差很少!

TexturePacker,商業項目能夠,相對來講比較「健康」,不用額外寫代碼填坑。

從本文還能夠看出一點,Unity對UI的重視程度仍是比較小的!

就說到這裏吧!Have a good day!

相關文章
相關標籤/搜索