UGUI 之 全面理解图集与使用(三)

发表于2015-07-20
评论0 6k浏览
UGUI的图集打包与工作原理,看了一天多,终于看明白了~晕~还是记录一下我研究的成果,分享给大家,一起学习和研究。

先说说UGUI的Atlas和NGUI的Atlas的区别,NGUI是必须先打出图集然后才能开始做界面。这一点很烦,因为始终都要去考虑你的UI图集。比如图集会不会超1024 ,图集该如何来规划等等。而UGUI的原理则是,让开发者彻底模糊图集的概念,让开发者不要去关心自己的图集。做界面的时候只用小图,而在最终打包的时候unity才会把你的小图和并在一张大的图集里面。然而这一切一切都是自动完成的,开发者不需要去care它。

如下图所示,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 在这里你就可以预览到你的图集信息。图集的大小还有图集的格式等等很多参数我们都是可以控制的,也可以通过脚本来设置。我在下一篇文章里详细说这个(请期待嘿嘿)。



图集的预览紧紧是让你看看你的图集大概张什么样子。那么我们的图集的这张图片保存在了哪里呢?它保存在和Assets文件夹同级的目录,Libary/AtlasCache里面。你不用管它,也不要删除它,就算你删除了也没用因为只要你打包,它就会生成并且会打到包中。

此时在Hierarchy视图中创建两个Image对象。如下图所示,我们可以清楚的看到此时我的draw call已经被合并成了 1。




这两个图片是我是在Editor模式下预先拖入Hierarchy视图中的,可是如果我想运行时根据图片的名子来动态创建精灵该如何?可是unity根本没有提供加载图集的方法,也没有提供加载图集上某个图片的方法。 因为UGUI就不像让开发者有图集的这个概念,可是我们肯定是要实现这个需求的。。怎么办呢?

第一个设想,先把散=小图打包成图集,然后再把所有散图拷贝在Resources文件夹下,这样运行时就能用Resources.load了。
第二个设想,还是先把小图打成图集,然后把所有小图关联在prefab上,拷贝在Resources文件夹下,这样运行时也能用Resources.load了。到底那个靠谱呢? 给大家看一个图大家就知道答案了。

如下图所示,打成图集的图片如果在放在Resources那么资源就变成双份了。。 所以我们只能把小图关联在Prefab上,把所有的Prefab放在Resources下面,这样就不占用多余的空间了。




好了,现在方法我们已经掌握,那么就开始写工具吧。如下图所示可以按文件夹分,每一个文件夹就是一个图集。然后每一张小图创建一个Prefab,Prefab的名子就起小图的名子,文件关联在Resources下面。



代码比较简单,我就不注释了。

[Applenoxss] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[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);
                        }
                }       
        }



然后是运行时的代码。

[ColdFusion] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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要更简单一些,无论如何我们要先开始打图集。

[ColdFusion] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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;
        }
 
}



如下图所示,我的assetbundle已经打出来了。


然后把UIMain.cs在改一改。

[ColdFusion] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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。


为把NGUI干掉的目标而奋斗,嘻嘻。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引