Unity中2D和UGUI图集的理解与使用

图集

什么是图集?

在使用3D技术开发2D游戏或制作UI时(即使用GPU绘制),都会使用到图集,而使用CPU渲染的2D游戏和UI则不存在图集这个概念(比如Flash的原生显示列表),那么什么是图集呢?准确的说法图集是一张包含了多个小图的大图和一份记录了每个小图id、位置、尺寸等数据的数据文件,一个图集应该对应两个文件,当然也有人把数据集成到图片中,导致看起来只有一张图片(参考自DragonBones的做法)。

为什么要用图集?

在GPU已经成为PC、手机等设备的必备组件的现在,把所有显示的绘制操作交给专门处理图像的GPU显然比交给CPU更合适,这样空闲下来的CPU可以集中力量处理游戏的逻辑运算。

而GPU处理图像的做法和CPU是不一样的,在GPU中,我们要绘制一个图像需要提交图片(即纹理)到显存,然后在进行绘制(这个过程称为一次DrawCall),那么如果我们一帧要绘制100个就需要提交100次图片,如果使用包含了这100图片的图集,只需要一次提交即可,即一次DrawCall就搞定,处理效率上会有很大的提升。

另外使用图集也方便管理和归类各个模块的图片,可以通过一次加载和一次卸载完成多个图片的处理,同理,加载次数也下来了,可以提升运行效率。

Unity2D中的图集

其实用过NGUI的同学都知道在拼界面之前都必须先制作好对应的图集才行,然而在Unity2D或UGUI中,Unity却自己集成了图集的操作,目的是让我们忘掉图集的存在,更加关注设计这个层面。

如何使用图集

在Unity中我们只要使用小图片即可,可以通过设置图片的Packing Tag来指定小图会被打包到的图集,比如2个小图的Packing Tag都叫“MyAtlas”,则Unity会将这两个小图打包到名为“MyAtlas”的图集中。

注意图片不能放在Resources文件夹下面,Resources文件夹下的资源将不会被打入图集。

是否打包图集的控制选项:Editor->Project Settings 下面有sprite packer的模式。Disabled表示不打包图集,Enabled For Builds 表示只有打包应用的时候才会打包图集,Always Enabled 表示始终会打包图集。

在Windows->Sprite Packer 里,点击packer 在这里你就可以预览到你的图集信息,图集文件被保存在和Assets文件夹同级的目录,Libary/AtlasCache里面。图集的大小还有图集的格式等等很多参数我们都是可以控制的,也可以通过脚本来设置。

通过设置我们就可以发现多个同一Packing Tag的小图放到场景中只会消耗一个DrawCall,表示我们的图集已经开始起作用了。

动态设置的问题

可是Unity的这种设计在下面的这种情况下却给我们带来了很大的困扰:

当我们希望通过代码的方式修改纹理时会无从下手,因为Unity3D并没有给我们提供加载图集和获取图集中指定小图的API。

解决方案

目标是我想把图集放在Resources中加载。

首先我们的原图不能放在Resources文件夹中,这样会导致资源大小翻倍。

具体的思路是通过脚本创建对应的名称的预制件,同时将加载的图片(Sprite)作为该预制件的SpriteRenderer组件的sprite属性即可。

打包代码

打包代码来自我的开源项目中,有一些具体的规则,大家可以看注释:

 1 // =================================================================================================
 2 //
 3 //    Hammerc Library
 4 //    Copyright 2015 hammerc.org All Rights Reserved.
 5 //
 6 //    See LICENSE for full license information.
 7 //
 8 // =================================================================================================
 9
10 using System.IO;
11 using UnityEditor;
12 using UnityEngine;
13
14 /// <summary>
15 ///
16 /// 2D 图集生成对应预制件脚本类.
17 ///
18 /// 解决问题:
19 /// 解决不能动态设置 Sprite 对象的贴图的问题, 因为分散的小图最终需要打包为图集而 Unity3D 又将图集的处理隐藏, 所以需要将小图的信息存放到预制件中.
20 ///
21 /// 使用方法及步骤:
22 /// 1.修改源目录及目标目录为自己使用的目录;
23 /// 2.把小图存放到源目录中;
24 /// 3.在菜单栏点击 Hammerc/2D/MakeSpritePrefabs 即可在目标目录下生成对应的预制件;
25 /// 4.通过获取生成的预制件的 sprite 对象即可在程序中使用小图.
26 ///
27 /// </summary>
28 public class MakeSpritePrefabsScript
29 {
30     /// <summary>
31     /// Assets 目录下的小图片目录, 包括子目录的所有图片文件都会进行处理.
32     /// </summary>
33     private const string ORIGIN_DIR = "\\RawData\\Sprites";
34
35     /// <summary>
36     /// Assets 目录下的小图预制件生成的目标目录, 注意该目录下不要存放其他资源, 每次生成时都会清空该目录下的所有文件.
37     /// </summary>
38     private const string TARGET_DIR = "\\Resources\\Sprites";
39
40     /// <summary>
41     /// 将制定目录下的原始图片一对一打包成 Prefab 方便在游戏运行中读取指定的图片.
42     /// </summary>
43     [MenuItem("Hammerc/2D/MakeSpritePrefabs")]
44     private static void MakeSpritePrefabs()
45     {
46         EditorUtility.DisplayProgressBar("Make Sprite Prefabs", "Please wait...", 1);
47
48         string targetDir = Application.dataPath + TARGET_DIR;
49         //删除目标目录
50         if(Directory.Exists(targetDir))
51             Directory.Delete(targetDir, true);
52         if(File.Exists(targetDir + ".meta"))
53             File.Delete(targetDir + ".meta");
54         //创建空的目标目录
55         if(!Directory.Exists(targetDir))
56             Directory.CreateDirectory(targetDir);
57
58         //获取源目录的所有图片资源并处理
59         string originDir = Application.dataPath + ORIGIN_DIR;
60         DirectoryInfo originDirInfo = new DirectoryInfo(originDir);
61         MakeSpritePrefabsProcess(originDirInfo.GetFiles("*.jpg", SearchOption.AllDirectories), targetDir);
62         MakeSpritePrefabsProcess(originDirInfo.GetFiles("*.png", SearchOption.AllDirectories), targetDir);
63
64         EditorUtility.ClearProgressBar();
65     }
66
67     static private void MakeSpritePrefabsProcess(FileInfo[] files, string targetDir)
68     {
69         foreach(FileInfo file in files)
70         {
71             string allPath = file.FullName;
72             string assetPath = allPath.Substring(allPath.IndexOf("Assets"));
73             //加载贴图
74             Sprite sprite = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Sprite)) as Sprite;
75             //创建绑定了贴图的 GameObject 对象
76             GameObject go = new GameObject(sprite.name);
77             go.AddComponent<SpriteRenderer>().sprite = sprite;
78             //获取目标路径
79             string targetPath = assetPath.Replace("Assets" + ORIGIN_DIR + "\\", "");
80             //去掉后缀
81             targetPath = targetPath.Substring(0, targetPath.IndexOf("."));
82             //得到最终路径
83             targetPath = targetDir + "\\" + targetPath + ".prefab";
84             //得到应用当前目录的路径
85             string prefabPath = targetPath.Substring(targetPath.IndexOf("Assets"));
86             //创建目录
87             Directory.CreateDirectory(prefabPath.Substring(0, prefabPath.LastIndexOf("\\")));
88             //生成预制件
89             PrefabUtility.CreatePrefab(prefabPath.Replace("\\", "/"), go);
90             //销毁对象
91             GameObject.DestroyImmediate(go);
92         }
93     }
94 }

加载代码

加载就很简单了,加载对应的预制件,然后取出Sprite即可,核心代码如下:

1 public Sprite LoadSprite(string spriteName)
2 {
3     return Resources.Load<GameObject>("Sprites/" + spriteName).GetComponent<SpriteRenderer>().sprite;
4 }

设置贴图就是向GameObject添加SpriteRenderer脚本后设置其sprite属性即可,如下:

this.gameObject.GetComponent<SpriteRenderer>().sprite = LoadSprite("img1");

打包的问题

虽然解决了动态设置的问题,但是如果我们要让图集支持热更新,使用AssetBundle来打包又该怎么办呢?

解决方案

目标是我想把图集放在AssetBundle中加载,AssetBundle的处理比Resources的处理要简单得多了。

和打包到Resources不同,在AssetBundle中,我们添加图片就是以Sprite的类型存在的,所以打包以后,实际上是已经制作为图集了,但是还是可以按获取单个文件的方式获取指定的图片,也就是Sprite,获取后可以直接使用。

打包代码

打包代码同样来自我的开源项目中,有一些具体的规则,大家可以看注释:

 1 // =================================================================================================
 2 //
 3 //    Hammerc Library
 4 //    Copyright 2015 hammerc.org All Rights Reserved.
 5 //
 6 //    See LICENSE for full license information.
 7 //
 8 // =================================================================================================
 9
10 using UnityEditor;
11 using UnityEngine;
12
13 /// <summary>
14 ///
15 /// 简单的 AssetBundle 创建类.
16 ///
17 /// 解决问题:
18 /// 实现简单的资源打包.
19 ///
20 /// 使用方法及步骤:
21 /// 1.修改要打包到的目标平台枚举;
22 /// 2.选中要打包的文件在菜单栏点击对应的功能菜单.
23 ///
24 /// </summary>
25 public class SimpleCreateAssetBundle
26 {
27     /// <summary>
28     /// 打包的目标平台.
29     /// </summary>
30     private const BuildTarget BUILD_TARGET = BuildTarget.StandaloneWindows64;
31
32     /// <summary>
33     /// 将选定的一个对象进行打包, 同时包含依赖项, 可通过 AssetBundle 的 main 属性获取.
34     /// </summary>
35     [MenuItem("Hammerc/AssetBundle/CreateSingleAssetBundle")]
36     private static void CreateSingleAssetBundle()
37     {
38         if(Selection.activeObject != null)
39         {
40             //显示保存窗口
41             string path = EditorUtility.SaveFilePanel("Create Single AssetBundle:", "", "New AssetBundle", "assetbundle");
42
43             if(path.Length > 0)
44             {
45                 //打包
46                 BuildPipeline.BuildAssetBundle(Selection.activeObject, null, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BUILD_TARGET);
47             }
48         }
49     }
50
51     /// <summary>
52     /// 将选定的多个对象进行打包, 同时包含依赖项, 不指定 AssetBundle 的 main 属性获取.
53     /// </summary>
54     [MenuItem("Hammerc/AssetBundle/CreateMultipleAssetBundle")]
55     private static void CreateMultipleAssetBundle()
56     {
57         if(Selection.objects.Length > 0)
58         {
59             //显示保存窗口
60             string path = EditorUtility.SaveFilePanel("Create Multiple AssetBundle:", "", "New AssetBundle", "assetbundle");
61
62             if(path.Length > 0)
63             {
64                 //打包
65                 BuildPipeline.BuildAssetBundle(null, Selection.objects, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BUILD_TARGET);
66             }
67         }
68     }
69 }

我们不需要关心文件类型,Unity3D会自动帮我们把图片格式的文件转换为Sprite类型。

加载代码

加载代码就比较简单了,加载AssetBundle后从这个AssetBundle中直接加载图片即可。

1 public Sprite LoadSprite(string spriteName)
2 {
3     if(assetbundle == null)
4         assetbundle = AssetBundle.CreateFromFile(Application.streamingAssetsPath +"/Img.assetbundle");
5     return assetbundle.Load(spriteName) as Sprite;
6 }

设置贴图就是向GameObject添加SpriteRenderer脚本后设置其sprite属性即可,如下:

this.gameObject.GetComponent<SpriteRenderer>().sprite = LoadSprite("img1");
时间: 2024-10-27 08:29:02

Unity中2D和UGUI图集的理解与使用的相关文章

关于Unity中的NGUI和UGUI

用Unity开发2D游戏,有三套关系 1.GUI:Unity本身的对象 2.NGUI:以前在Unity中广泛来做2D的,是第三方的包,需要安装 3.UGUI:Unity5.X后,Unity找到NGUI的作者,开发了UGUI,变成内置于Unity中的包,官方主推 所有的元素都在Unity的UI工具栏 3D做2D游戏的方法: 1: 使用正交摄像机;2: 使用透视摄像机,将2D元素移动到合适的距离. 例如设计分辨率为 960x640, 得到在3D世界里面一个图片的大小w*h米,将这个图片移动到一定的距

Unity的2D图集处理,并切割出一张张小图片

(转载请注明出处:http://blog.csdn.net/hongyouwei/article/details/45011315) 在我们找资源的时候,有时候会遇到那种把一堆图片放进一张png图片里面的情况,在做2D游戏的时候,我们往往需要将里面的一块块小图片切割出来使用,那么问题就来了,怎么切割呢,现在我们提供一张小图片,给大家演示一下: 那么我们现在把图片导入到Unity中,选中图片你会看到上边的Inspector界面 然后,选择Texture Type类型为Advanced,如下图: 将

unity中计算ugui中Text控件的所需长度

背景是这样的,我在unity中需要在我按下某个按键的时候,然后显示出被射线碰撞到的物体的信息.物体的信息之前已经整理好成txt文本了.读取一下就可以了.那么这时候就有一个问题,因为每个物体的介绍信息不是一样的长度,那么如果当text控件里面文字的长度超过显示的长度时,超过部分就不再显示.那么这该怎么办. 我是通过去便利txt文本中的所有文字和符号,然后将所有换行符也就是/n的个数记录下来,然后计算一下对应字体大小一行有几个字,比如我的是18号字体,那么一行可以写30个字,然后计算一下txt文本中

Spine工具在Unity中的使用

Spine工具以前都是跟Cocos 2d结合的比较多,很多人以为Unity有了Toolkit 2d和NGUI不需要Spine了,其实这种理解是错误的,Spine非常适合Unity,不需要Toolkit 2D,自己本身就可以适应Unity. 而且由于他的资源量非常少,在效率和安装包方面都比Toolkit 2D好一些,现在Spine工具已经开始在Unity中使用了. Spine说白了它就是一种调试2D骨骼动画的工具,经过它调试的工具如何在Unity中使用?我们下面开始围绕这个问题展开讨论. 首先我们

Unity中Mesh分解与边缘高亮加上深度检测

一个比较简单的需求,不过遇到些坑,记录下. 房间有多个模型,每个模型可能多个SubMesh,点击后,需要能具体到是那个SubMesh,并且在这个SubMesh上显示边缘高光,以及能个性这单个SubMesh对应的Material.如一个桌子的Mesh,其实有二个材质,分别对应二个SubMesh,一个桌面和一个桌脚,点击桌面后,只有这个桌面高光,而不是整个桌子,并且能单独更换这个桌面的Material. 我们知道Unity中,Mesh和Ogre一样,也是可以有多个SubMesh,每个SubMesh有

游戏的物理和数学:Unity中的弹道和移动目标提前量计算

下载地址:http://yunpan.cn/cK6pCrpdhfbTd  提取码 5472 弹道计算是游戏里常见的问题,其中关于击中移动目标的自动计算提前量的话题,看似简单,其实还是挺复杂的数学.网上这方面的资料还真不多,而且都是写的含含糊糊.抽空总结一下自己的方法. 讨论的前提是,假设目标是在3D空间里以匀速直线方式运动. 1.直线弹道在不考虑重力和空气阻力影响的情况下,子弹的弹道呈直线运动.这种情况下,其实是个纯平面几何空间的问题,不需要微积分和线代知识.分析的情况如下图:        

用体渲染的方法在Unity中渲染云

最近在知乎上看到一篇文章讲云层的渲染(https://zhuanlan.zhihu.com/p/34836881?utm_medium=social&utm_source=qq) 原文简单的讲了噪声生成云体的办法,以及一个光照模型. 看了之后很感兴趣,加上本科毕设做的就是体渲染,于是打算在unity里山寨一个出来. 原原文(知乎上的文章引用的文章)是2015年地平线黎明时分制作团队的一个talk(http://advances.realtimerendering.com/s2015/The%20

unity中camera摄像头控制详解

目录 1. 缘起 2. 开发 2.1. 建立项目 2.2. 旋转 2.2.1. 四元数 2.3. 移动 2.3.1. 向量操作 2.4. 镜头拉伸 2.5. 复位 2.6. 优化 1 缘起 我们的产品是使用unity开发水利BIM(水利建筑信息模型),项目中需要控制摄像 头对模型进行360度查看,请注意所有操作都是移动摄像头,不是移动模型.摄 像头能进行移动.旋转.改变焦距操作,类似于SketchUp的控制操作: 摄像头移动时,根据当前旋转方向(Rotation)进行移动 摄像头距离模型越远,摄

2019.9.27 Unity中Sprite和UI Image的区别

来源:https://blog.csdn.net/coffeecato/article/details/78536488 coffeecato写在前面:本文确实不错,作者用以说明自动生成网格的示图非常具有代表性,从drawcall的生成过程分析性能开销的重点,引出了overdraw和达到GPU像素填充率限制的原因,从中也可以看出作者对这个主题的理解颇有深度.查看作者的个人自述,居然是个2012年毕业的小伙子,后生可畏啊!翻译本文对自己也是个考验.英文水平捉急,如果错误请多多指正. 原文:UNIT