Unity - 圖集的理解及優化(UGUI、NGUI)

合併圖集的好處:

1:減少DrawCall:多張圖片需要多次DrawCall,合併成一張大圖只需要調用一次DrawCall

2:減少對內存的佔用:OpenGL每張貼圖都需要設置爲2的N次方纔能使用,比如你有一張寬高爲100*100和一張10*10的圖片,如過不合成大貼圖,那麼就需要使用128*128的和16*16的圖片,如果是使用一張大圖的話,就可以將100*100和10*10的圖片放在128*128的圖集上了

UGUI圖集的打包及工作原理:

1:首先圖集的Altas的區別,對於NGUI來說,需要對圖片進行一個對圖集的打包,並且需要去關心這個圖集的大小,比如圖集大小是否會超過1024*1024,圖集該如何規劃等問題

2:UGUI的原理則是,讓開發者徹底模糊圖集的概念,讓開發者不要去關心自己的圖集。做界面的時候只用小圖,而在最終打包的時候unity纔會把你的小圖和並在一張大的圖集裏面。然而這一切一切都是自動完成的,開發者不需要去care它。

3:如下圖所示,Editor->Project Settings 下面有sprite packer的模式。Disabled表示不啓用它,Enabled For Builds 表示只有打包的時候纔會啓用它,Always Enabled 表示永遠啓用它。 這裏的啓用它就表示是否將小圖自動打成圖集。

我的選項是Always Enabled 。因爲開發的時候我們需要清楚的看到現在是幾個Draw Call,從而才能優化小圖。在最終打包的時候unity會自動構建大的圖集,可是我開發的時候就想看圖集會佔幾個Draw Call,這怎麼辦呢?如下圖所示,首先將你的圖片拖入unity中,將同一圖集的所有圖片的packing tag設置成一個名子即可。

注意你的圖片不能放在Resources文件夾下面,Resources文件夾下的資源將不會被打入圖集,切記(也就是在這裏混淆了我很久)。然後在Windows->Sprite Packer 裏,點擊packer 在這裏你就可以預覽到你的圖集信息。圖集的大小還有圖集的格式等等很多參數我們都是可以控制的,也可以通過腳本來設置。

此時在Hierarchy視圖中創建兩個Image對象。注意這裏需要將程序運行,如下圖所示,我們可以清楚的看到此時我的draw call已經被合併成了1 。

這兩個圖片是我是在Editor模式下預先拖入Hierarchy視圖中的,可是如果我想運行時根據圖片的名子來動態創建精靈該如何?可是unity根本沒有提供加載圖集的方法,也沒有提供加載圖集上某個圖片的方法。 因爲UGUI就不像讓開發者有圖集的這個概念,可是我們肯定是要實現這個需求的。。怎麼辦呢?

第一個設想,先把散=小圖打包成圖集,然後再把所有散圖拷貝在Resources文件夾下,這樣運行時就能用Resources.load了。

第二個設想,還是先把小圖打成圖集,然後把所有小圖關聯在prefab上,拷貝在Resources文件夾下,這樣運行時也能用Resources.load了。到底那個靠譜呢? 給大家看一個圖大家就知道答案了。

如下圖所示,打成圖集的圖片如果在放在Resources那麼資源就變成雙份了。。 所以我們只能把小圖關聯在Prefab上,把所有的Prefab放在Resources下面,這樣就不佔用多餘的空間了。

好了,現在方法我們已經掌握,那麼就開始寫工具吧。如下圖所示可以按文件夾分,每一個文件夾就是一個圖集。然後每一張小圖創建一個Prefab,Prefab的名子就起小圖的名子,文件關聯在Resources下面。

[MenuItem ("MyMenu/AtlasMaker")]

static private void MakeAtlas()

{

string spriteDir = Application.dataPath +"/Resources/Sprite";

if(!Directory.Exists(spriteDir)){

Directory.CreateDirectory(spriteDir);

}



DirectoryInfo rootDirInfo = new DirectoryInfo (Application.dataPath +"/Atlas");

foreach (DirectoryInfo dirInfo in rootDirInfo.GetDirectories()) {

foreach (FileInfo pngFile in dirInfo.GetFiles("*.png",SearchOption.AllDirectories)) {

string allPath = pngFile.FullName;

string assetPath = allPath.Substring(allPath.IndexOf("Assets"));

Sprite sprite = Resources.LoadAssetAtPath<Sprite>(assetPath);

GameObject go = new GameObject(sprite.name);

go.AddComponent<SpriteRenderer>().sprite = sprite;

allPath = spriteDir+"/"+sprite.name+".prefab";

string prefabPath = allPath.Substring(allPath.IndexOf("Assets"));

PrefabUtility.CreatePrefab(prefabPath,go);

GameObject.DestroyImmediate(go);

}

}

}

然後是運行時的代碼。

using UnityEngine;

using System.Collections;

using UnityEngine.UI;



public class UIMain : MonoBehaviour {



void Start ()

{

CreatImage(loadSprite("image0"));

CreatImage(loadSprite("image1"));

}



private void CreatImage(Sprite sprite ){

GameObject go = new GameObject(sprite.name);

go.layer = LayerMask.NameToLayer("UI");

go.transform.parent = transform;

go.transform.localScale= Vector3.one;

Image image = go.AddComponent<Image>();

image.sprite = sprite;

image.SetNativeSize();

}



private Sprite loadSprite(string spriteName){

return Resources.Load<GameObject>("Sprite/" + spriteName).GetComponent<SpriteRenderer>().sprite;

}

}

因爲這兩個圖是在同一個圖集上,所以drawcall就是1了。這樣我們就可以根據圖片的名子來運行時加載圖片了。

接下來就是Assetbundle了,如果我們的圖集需要在線更新那該怎麼辦呢? 其實Assetbundle比Resources要更簡單一些,無論如何我們要先開始打圖集。

[MenuItem ("MyMenu/Build Assetbundle")]

static private void BuildAssetBundle()

{

string dir = Application.dataPath +"/StreamingAssets";



if(!Directory.Exists(dir)){

Directory.CreateDirectory(dir);

}

DirectoryInfo rootDirInfo = new DirectoryInfo (Application.dataPath +"/Atlas");

foreach (DirectoryInfo dirInfo in rootDirInfo.GetDirectories()) {

List<Sprite> assets = new List<Sprite>();

string path = dir +"/"+dirInfo.Name+".assetbundle";

foreach (FileInfo pngFile in dirInfo.GetFiles("*.png",SearchOption.AllDirectories))

{

string allPath = pngFile.FullName;

string assetPath = allPath.Substring(allPath.IndexOf("Assets"));

assets.Add(Resources.LoadAssetAtPath<Sprite>(assetPath));

}

if(BuildPipeline.BuildAssetBundle(null, assets.ToArray(), path,BuildAssetBundleOptions.UncompressedAssetBundle| BuildAssetBundleOptions.CollectDependencies, GetBuildTarget())){

}

}

}

static private BuildTarget GetBuildTarget()

{

BuildTarget target = BuildTarget.WebPlayer;

#if UNITY_STANDALONE

target = BuildTarget.StandaloneWindows;

#elif UNITY_IPHONE

target = BuildTarget.iPhone;

#elif UNITY_ANDROID

target = BuildTarget.Android;

#endif

return target;

}

如下圖所示,我的assetbundle已經打出來了。

然後把UIMain.cs在改一改。

using UnityEngine;

using System.Collections;

using UnityEngine.UI;



public class UIMain : MonoBehaviour {



AssetBundle assetbundle = null;

void Start ()

{

CreatImage(loadSprite("image0"));

CreatImage(loadSprite("image1"));

}



private void CreatImage(Sprite sprite ){

GameObject go = new GameObject(sprite.name);

go.layer = LayerMask.NameToLayer("UI");

go.transform.parent = transform;

go.transform.localScale= Vector3.one;

Image image = go.AddComponent<Image>();

image.sprite = sprite;

image.SetNativeSize();

}



private Sprite loadSprite(string spriteName){

#if USE_ASSETBUNDLE

if(assetbundle == null)

assetbundle = AssetBundle.CreateFromFile(Application.streamingAssetsPath +"/Main.assetbundle");

return assetbundle.Load(spriteName) as Sprite;

#else

return Resources.Load<GameObject>("Sprite/" + spriteName).GetComponent<SpriteRenderer>().sprite;

#endif

}

}

依然還是一個drawcall。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章