UGUI教程之SpritePacker打包参数实现详解
在UGUI开发中,SpritePacker是一项重要的工具,用于对Sprite图集进行打包。为了更好地理解其使用,我们将先对比一下NGUI的图集打包方式。
NGUI图集打包及优化
NGUI在打包图集时,图集的默认格式是RGBA32,这种格式支持带透明通道的图片。以一张1024大小的图集为例,其占用的内存约为4M。为了优化图集内存占用,可以将带透明通道的图片和不带透明通道的图片分开打包图集,这样能有效减少内存的使用量。不过,在NGUI中,这些操作都需要手动完成。
SpritePacker自动打包
而SpritePacker则实现了全自动打包。在Sprite上有一个Packing Tag属性,UGUI会将Packing Tag标识相同且图片格式相同的图片打包成同一图集。例如,MomoAtals和RUORUOAtlas是Packing Tag的标识符,SpritePacker会根据这两个标识符打出两个图集。如果MomoAtlas中的图片一部分是RGBA32格式,另一部分是ETC 4bits格式,那么MomoAtlas将被进一步分成两个图集,图集名称尾缀会带有Group。
Sprite Packer的打包模式
Sprite Packer有两种打包模式,分别是DefaultPackerPolicy和TightPackerPolicy。
DefaultPackerPolicy
这是默认的打包方式,也称为矩形打包方式。它会把所有的小图按照矩形的方式排列,如果图片的宽高不一样,会自动进行补齐。Unity推荐使用这种模式进行图集打包,理论上所有图集都可以使用DefaultPackerPolicy完成打包。同时,还可以让图集中的某几张图片单独采用不同的打包方式。例如,当当前打包图集采用DefaultPackerPolicy模式时,小图中以[TIGHT]开头的图片就会单独采用TightPackerPolicy打包模式。
TightPackerPolicy
这是紧密打包方式,它会尽可能地将图片都打包到图集上。相比DefaultPackerPolicy,这种方式能打包更多的图片,更节省空间。从图集的布局可以清晰地看到,TightPackerPolicy打包的图集更加紧密。同样,如果当前打包图集采用TightPackerPolicy模式,小图中以[RECT]开头的图片就会单独采用DefaultPackerPolicy打包模式。
自定义打包方式
Unity只提供了上述两种图集打包方法。如果我们想自定义打包方式,比如设置图片打包格式、图集大小等,可以通过编写代码来实现。将以下代码放在Editor文件夹下,就可以在代码里面设置图集的属性。
using System;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.Sprites;
using System.Collections.Generic;
// DefaultPackerPolicy will pack rectangles no matter what Sprite mesh type is unless their packing tag contains "[TIGHT]".
class DefaultPackerPolicySample : UnityEditor.Sprites.IPackerPolicy
{
protected class Entry
{
public Sprite sprite;
public AtlasSettings settings;
public string atlasName;
public SpritePackingMode packingMode;
}
public virtual int GetVersion() { return 1; }
protected virtual string TagPrefix { get { return "[TIGHT]"; } }
protected virtual bool AllowTightWhenTagged { get { return true; } }
public void OnGroupAtlases(BuildTarget target, PackerJob job, int[] textureImporterInstanceIDs)
{
List<Entry> entries = new List<Entry>();
foreach (int instanceID in textureImporterInstanceIDs)
{
TextureImporter ti = EditorUtility.InstanceIDToObject(instanceID) as TextureImporter;
TextureImportInstructions ins = new TextureImportInstructions();
ti.ReadTextureImportInstructions(ins, target);
TextureImporterSettings tis = new TextureImporterSettings();
ti.ReadTextureSettings(tis);
Sprite[] sprites = AssetDatabase.LoadAllAssetRepresentationsAtPath(ti.assetPath).Select(x => x as Sprite).Where(x => x != null).ToArray();
foreach (Sprite sprite in sprites)
{
//在这里设置每个图集的参数
Entry entry = new Entry();
entry.sprite = sprite;
entry.settings.format = ins.desiredFormat;
entry.settings.usageMode = ins.usageMode;
entry.settings.colorSpace = ins.colorSpace;
entry.settings.compressionQuality = ins.compressionQuality;
entry.settings.filterMode = Enum.IsDefined(typeof(FilterMode), ti.filterMode) ? ti.filterMode : FilterMode.Bilinear;
entry.settings.maxWidth = 1024;
entry.settings.maxHeight = 1024;
entry.atlasName = ParseAtlasName(ti.spritePackingTag);
entry.packingMode = GetPackingMode(ti.spritePackingTag, tis.spriteMeshType);
entries.Add(entry);
}
Resources.UnloadAsset(ti);
}
// First split sprites into groups based on atlas name
var atlasGroups =
from e in entries
group e by e.atlasName;
foreach (var atlasGroup in atlasGroups)
{
int page = 0;
// Then split those groups into smaller groups based on texture settings
var settingsGroups =
from t in atlasGroup
group t by t.settings;
foreach (var settingsGroup in settingsGroups)
{
string atlasName = atlasGroup.Key;
if (settingsGroups.Count() > 1)
atlasName += string.Format(" (Group {0})", page);
job.AddAtlas(atlasName, settingsGroup.Key);
foreach (Entry entry in settingsGroup)
{
job.AssignToAtlas(atlasName, entry.sprite, entry.packingMode, SpritePackingRotation.None);
}
++page;
}
}
}
protected bool IsTagPrefixed(string packingTag)
{
packingTag = packingTag.Trim();
if (packingTag.Length < TagPrefix.Length)
return false;
return (packingTag.Substring(0, TagPrefix.Length) == TagPrefix);
}
private string ParseAtlasName(string packingTag)
{
string name = packingTag.Trim();
if (IsTagPrefixed(name))
name = name.Substring(TagPrefix.Length).Trim();
return (name.Length == 0) ? "(unnamed)" : name;
}
private SpritePackingMode GetPackingMode(string packingTag, SpriteMeshType meshType)
{
if (meshType == SpriteMeshType.Tight)
if (IsTagPrefixed(packingTag) == AllowTightWhenTagged)
return SpritePackingMode.Tight;
return SpritePackingMode.Rectangle;
}
}
通过以上内容,我们详细了解了SpritePacker的打包参数及实现方式,包括与NGUI的对比、两种打包模式的特点以及自定义打包方式的代码实现。希望这些内容能帮助你更好地使用SpritePacker进行图集打包。