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属性即可。
打包代码
打包代码来自我的开源项目中,有一些具体的规则,大家可以看注释:
// =================================================================================================
//
// Hammerc Library
// Copyright 2015 hammerc.org All Rights Reserved.
//
// See LICENSE for full license information.
//
// ================================================================================================= using System.IO;
using UnityEditor;
using UnityEngine; /// <summary>
///
/// 2D 图集生成对应预制件脚本类.
///
/// 解决问题:
/// 解决不能动态设置 Sprite 对象的贴图的问题, 因为分散的小图最终需要打包为图集而 Unity3D 又将图集的处理隐藏, 所以需要将小图的信息存放到预制件中.
///
/// 使用方法及步骤:
/// 1.修改源目录及目标目录为自己使用的目录;
/// 2.把小图存放到源目录中;
/// 3.在菜单栏点击 Hammerc/2D/MakeSpritePrefabs 即可在目标目录下生成对应的预制件;
/// 4.通过获取生成的预制件的 sprite 对象即可在程序中使用小图.
///
/// </summary>
public class MakeSpritePrefabsScript
{
/// <summary>
/// Assets 目录下的小图片目录, 包括子目录的所有图片文件都会进行处理.
/// </summary>
private const string ORIGIN_DIR = "\\RawData\\Sprites"; /// <summary>
/// Assets 目录下的小图预制件生成的目标目录, 注意该目录下不要存放其他资源, 每次生成时都会清空该目录下的所有文件.
/// </summary>
private const string TARGET_DIR = "\\Resources\\Sprites"; /// <summary>
/// 将制定目录下的原始图片一对一打包成 Prefab 方便在游戏运行中读取指定的图片.
/// </summary>
[MenuItem("Hammerc/2D/MakeSpritePrefabs")]
private static void MakeSpritePrefabs()
{
EditorUtility.DisplayProgressBar("Make Sprite Prefabs", "Please wait...", ); string targetDir = Application.dataPath + TARGET_DIR;
//删除目标目录
if(Directory.Exists(targetDir))
Directory.Delete(targetDir, true);
if(File.Exists(targetDir + ".meta"))
File.Delete(targetDir + ".meta");
//创建空的目标目录
if(!Directory.Exists(targetDir))
Directory.CreateDirectory(targetDir); //获取源目录的所有图片资源并处理
string originDir = Application.dataPath + ORIGIN_DIR;
DirectoryInfo originDirInfo = new DirectoryInfo(originDir);
MakeSpritePrefabsProcess(originDirInfo.GetFiles("*.jpg", SearchOption.AllDirectories), targetDir);
MakeSpritePrefabsProcess(originDirInfo.GetFiles("*.png", SearchOption.AllDirectories), targetDir); EditorUtility.ClearProgressBar();
} static private void MakeSpritePrefabsProcess(FileInfo[] files, string targetDir)
{
foreach(FileInfo file in files)
{
string allPath = file.FullName;
string assetPath = allPath.Substring(allPath.IndexOf("Assets"));
//加载贴图
Sprite sprite = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Sprite)) as Sprite;
//创建绑定了贴图的 GameObject 对象
GameObject go = new GameObject(sprite.name);
go.AddComponent<SpriteRenderer>().sprite = sprite;
//获取目标路径
string targetPath = assetPath.Replace("Assets" + ORIGIN_DIR + "\\", "");
//去掉后缀
targetPath = targetPath.Substring(, targetPath.IndexOf("."));
//得到最终路径
targetPath = targetDir + "\\" + targetPath + ".prefab";
//得到应用当前目录的路径
string prefabPath = targetPath.Substring(targetPath.IndexOf("Assets"));
//创建目录
Directory.CreateDirectory(prefabPath.Substring(, prefabPath.LastIndexOf("\\")));
//生成预制件
PrefabUtility.CreatePrefab(prefabPath.Replace("\\", "/"), go);
//销毁对象
GameObject.DestroyImmediate(go);
}
}
}
加载代码
加载就很简单了,加载对应的预制件,然后取出Sprite即可,核心代码如下:
public Sprite LoadSprite(string spriteName)
{
return Resources.Load<GameObject>("Sprites/" + spriteName).GetComponent<SpriteRenderer>().sprite;
}
设置贴图就是向GameObject添加SpriteRenderer脚本后设置其sprite属性即可,如下:
this.gameObject.GetComponent<SpriteRenderer>().sprite = LoadSprite("img1");
打包的问题
虽然解决了动态设置的问题,但是如果我们要让图集支持热更新,使用AssetBundle来打包又该怎么办呢?
解决方案
目标是我想把图集放在AssetBundle中加载,AssetBundle的处理比Resources的处理要简单得多了。
和打包到Resources不同,在AssetBundle中,我们添加图片就是以Sprite的类型存在的,所以打包以后,实际上是已经制作为图集了,但是还是可以按获取单个文件的方式获取指定的图片,也就是Sprite,获取后可以直接使用。
打包代码
打包代码同样来自我的开源项目中,有一些具体的规则,大家可以看注释:
// =================================================================================================
//
// Hammerc Library
// Copyright 2015 hammerc.org All Rights Reserved.
//
// See LICENSE for full license information.
//
// ================================================================================================= using UnityEditor;
using UnityEngine; /// <summary>
///
/// 简单的 AssetBundle 创建类.
///
/// 解决问题:
/// 实现简单的资源打包.
///
/// 使用方法及步骤:
/// 1.修改要打包到的目标平台枚举;
/// 2.选中要打包的文件在菜单栏点击对应的功能菜单.
///
/// </summary>
public class SimpleCreateAssetBundle
{
/// <summary>
/// 打包的目标平台.
/// </summary>
private const BuildTarget BUILD_TARGET = BuildTarget.StandaloneWindows64; /// <summary>
/// 将选定的一个对象进行打包, 同时包含依赖项, 可通过 AssetBundle 的 main 属性获取.
/// </summary>
[MenuItem("Hammerc/AssetBundle/CreateSingleAssetBundle")]
private static void CreateSingleAssetBundle()
{
if(Selection.activeObject != null)
{
//显示保存窗口
string path = EditorUtility.SaveFilePanel("Create Single AssetBundle:", "", "New AssetBundle", "assetbundle"); if(path.Length > )
{
//打包
BuildPipeline.BuildAssetBundle(Selection.activeObject, null, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BUILD_TARGET);
}
}
} /// <summary>
/// 将选定的多个对象进行打包, 同时包含依赖项, 不指定 AssetBundle 的 main 属性获取.
/// </summary>
[MenuItem("Hammerc/AssetBundle/CreateMultipleAssetBundle")]
private static void CreateMultipleAssetBundle()
{
if(Selection.objects.Length > )
{
//显示保存窗口
string path = EditorUtility.SaveFilePanel("Create Multiple AssetBundle:", "", "New AssetBundle", "assetbundle"); if(path.Length > )
{
//打包
BuildPipeline.BuildAssetBundle(null, Selection.objects, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BUILD_TARGET);
}
}
}
}
我们不需要关心文件类型,Unity3D会自动帮我们把图片格式的文件转换为Sprite类型。
加载代码
加载代码就比较简单了,加载AssetBundle后从这个AssetBundle中直接加载图片即可。
public Sprite LoadSprite(string spriteName)
{
if(assetbundle == null)
assetbundle = AssetBundle.CreateFromFile(Application.streamingAssetsPath +"/Img.assetbundle");
return assetbundle.Load(spriteName) as Sprite;
}
设置贴图就是向GameObject添加SpriteRenderer脚本后设置其sprite属性即可,如下:
this.gameObject.GetComponent<SpriteRenderer>().sprite = LoadSprite("img1");
Unity中2D和UGUI图集的理解与使用的更多相关文章
- UGUI图集
Editor->Project Settings 下面有sprite packer的模式.Disabled表示不启用它,Enabled For Builds 表示只有打包的时候才会启用它,Alw ...
- 在Unity中使用UGUI修改Mesh绘制几何图形
在商店看到这样一个例子,表示很有兴趣,他们说是用UGUI做的.我想,像这种可以随便变形的图形,我第一个就想到了网格变形. 做法1: 细心的朋友应该会发现,每个UGUI可见元素,都有一个‘Canvas ...
- 关于Unity中的UGUI优化,你可能遇到这些问题
https://blog.uwa4d.com/archives/QA_UGUI-1.html 关于Unity中的UGUI优化,你可能遇到这些问题 作者:admin / 时间:2016年11月08日 / ...
- Unity中UGUI之Canvas属性解读版本二
Canvas Render Modes(渲染模式) 1.在screen空间中渲染2.在world空间中渲染 Screen Space-Overlay 在这个渲染模式中,UI元素将在场景的上面.如果场景 ...
- 关于Unity中的NGUI和UGUI
一.用Unity开发2D游戏,有三套关系 1.GUI:Unity本身自带的GUI 2.NGUI:以前在Unity中广泛来做2D的,是第三方的包,需要安装 3.UGUI:Unity5.X后(其实是Uni ...
- 理解ECS的概念和Unity中的ECS设计
组合优于继承 ecs的概念很早就有了,最初的主要目的应该还是为了改善设计. e-c-s三者都有其意义,e-c是组合优于继承,主要用以改善oo的继承耦合过重以及多继承菱形问题. oop常见设计里,每个g ...
- unity 中UGUI制作滚动条视图效果(按钮)
1.在unity中创建一个Image作为滚动条视图的背景: 2.在Image下创建一个空物体,在空物体下创建unity自带的Scroll View组件: 3.对滑动条视图的子物体进行调整: 4.添加滚 ...
- 关于unity中BindChannels的理解
http://blog.csdn.net/wpapa/article/details/51794277 官方文档:http://docs.unity3d.com/Manual/SL-BindChann ...
- 【Unity游戏开发】UGUI不规则区域点击的实现
一.简介 马三从上一家公司离职了,最近一直在出去面试,忙得很,所以这一篇博客拖到现在才写出来.马三在上家公司工作的时候,曾处理了一个UGUI不规则区域点击的问题,制作过程中也有一些收获和需要注意坑,因 ...
随机推荐
- bzoj4046
分组赛的题……madan原题,考试想不出来真是SB得不行 首先,从大往小加边,每次加边如果成环必然弹出环上最大边 考虑询问[x,y],如果边权在[x,y]的边弹出了小于等于y的边j,说明j不在最小生成 ...
- createElement 创建DOM元素
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...
- EF4.0和EF5.0增删改查的写法区别及执行Sql的方法
EF4.0和EF5.0增删改查的写法区别 public T AddEntity(T entity) { //EF4.0的写法 添加实体 //db.CreateObjectSet<T>(). ...
- VI使用的小白教程
vi 使用方法vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强 大不逊色于任何最新的文本编辑器,这里只是简单地介绍一下它的用法和一小部分指令.由于对Unix及Linux系统的任何版本,v ...
- 编写爬虫(spider)的预备知识:用java发送HTTP请求
使用原生API来发送http请求,而不是使用apache的库,原因在于这个第三方库变化实在太快了,每个版本都有不小的变化.对于程序员来说,使用它反而会有很多麻烦,比如自己曾经写过的代码将无法复用. 原 ...
- .NET/ASP.NET Routing路由(深入解析路由系统架构原理)http://wangqingpei557.blog.51cto.com/1009349/1312422
阅读目录: 1.开篇介绍 2.ASP.NET Routing 路由对象模型的位置 3.ASP.NET Routing 路由对象模型的入口 4.ASP.NET Routing 路由对象模型的内部结构 4 ...
- jsoup使用选择器语法来查找元素
问题 你想使用类似于CSS或jQuery的语法来查找和操作元素. 方法 可以使用Element.select(String selector) 和 Elements.select(String sel ...
- asp.net MVC 应用程序的生命周期(上)
首先我们知道http是一种无状态的请求,他的生命周期就是从客户端浏览器发出请求开始,到得到响应结束.那么MVC应用程序从发出请求到获得响应,都做了些什么呢? 本文我们会详细讨论MVC应用程序一个请求的 ...
- 24、AR技术
什么是AR 在介绍增强现实(AR)之前,需要先说说虚拟现实(VR) 虚拟现实是从英文Virtual Reality 一词翻译过来的,简称VR.VR 技术是采用以计算机技术为核心的技术,生成逼真的视.听 ...
- <译>Selenium Python Bindings 1 - Installation
Installation Introduction Selenium Python bindings 提供了一个简单的API来使用Selenium WebDriver编写使用功能/验收测试.通过Sel ...