Cuando diseñe gráficas de Sprite, es conveniente trabajar con un archivo de textura separado por cada personaje. Sin embargo, una parte significativa de una textura de sprite a menudo será ocupado por el espacio vacío entre los elementos gráficos y este espacio va a resultar en memoria de video gastada en el tiempo de ejecución. Para un rendimiento optimo, lo mejor es empacar gráficas de varias texturas de sprite muy juntas dentro de una misma textura conocida como un atlas. Unity proporcionar una utilidad Sprite Packer para automatizar el proceso de generar atlases del las texturas de sprite individuales.
Unity maneja la generación y uso de texturas sprite atlas detrás de escenas para que el usuario no tenga que hacer ninguna asignatura manual. El atlas puede ser opcionalmente empacado al entrar en el modo de reproducción durante una construcción y las gráficas para un objeto sprite serán obtenidas del atlas una vez haya sido generadas.
Los usuarios son requeridos especificar una etiqueta de empaque (Packing Tag) en el importador de texturas(Texture Importer) para habilitar el empacamiento de Sprites de esa textura.
El Sprite Packer es deshabilitado por defecto pero usted lo puede configurar desde los Ajustes del Editor (menú: Edit -> Project Settings -> Editor). El modo Sprite Packing puede ser cambiado de Disabled a Enabled for Builds (i.e. el empaque es utilizado para construcciones pero no para el modo de reproducción) o Always Enabled (i.e. el empaque es habilitado para ambos modo de reproducción y construcciones).
Si usted abre la ventana del Sprite Packer (menú: Window -> Sprite Packer) y hace click en el botón Pack en la esquina superior izquierda, usted vera los arreglos de las texturas empacadas dentro del atlas.
Si usted selecciona un sprite en el panel de proyecto, esto también será subrayado para mostrar su posición en el atlas. El esquema es en realidad el esquema del mesh renderizado y también define el área utilizado para tight packing.
La barra de herramientas en la parte superior de la ventana del Sprite Packer tiene una serie de controles que afectan el empacamiento y la visualización. Los botones Pack inician la operación de empacar pero no obligará ninguna actualización si el atlas no ha cambiado desde la última vez que fue empacada. (Un botón relacionado Repack aparecerá cuando usted implemente una política personalizada de empacamiento como es explicado en Customizing the Sprite Packer a continuación). Los menús View Atlas y Page # le permiten a usted escoger qué página de que atlas es mostrada en la ventana (un solo atlas puede ser dividido a más de una “página” si hay el espacio suficiente para todos los sprites en el tamaño máximo de textura). El menú junto al número de página selecciona qué “parking policy” es utilizada para el atlas (ver abajo). A la derecha de la barra de herramientas están dos controles para enfocar la vista y cambiar entre una visualización en color y alpha para el atlas.
El Sprite Packer utiliza una packing policy para decidir cómo asignar sprites a atlases. Es posible crear sus propias políticas de empaquetamiento (packing) (ver abajo) pero las opciones Default Packer Policy, Tight Packer Policy y Tight Rotate Enabled Sprite Packer Policy siempre están disponibles. Con estas políticas, la propiedad Packing Tag en el Texture Importer directamente selecciona el nombre del atlas dónde el sprite será empaquetado por lo que todos los sprites con la misma tag (etiqueta) de packing (empaquetamiento) serán agrupados en el mismo atlas. Los atlases luego son ordenados por los texture import settings para que coincidan con lo que sea que el usuario configura como las texturas fuentes. Los sprites con los mismos ajustes de compresión de textura serán agrupados al mismo atlas dónde sea posible.
La opción DefaultPackerPolicy es suficiente para la mayoría de propósito pero usted también puede implementar su propia política personalizada de empacamiento si usted necesita. Para hacer esto, usted necesita implementar la interfaz UnityEditor.Sprites.IPackerPolicy para una clase en el script del editor. Esta interfaz requiere los siguientes métodos:
La DefaultPackerPolicy utiliza rect parking (mire SpritePackingMode) por defecto. Este es útil si usted está haciendo efectos texture-space o le gustaría utilizar un diferente mesh para renderizar el sprite. Unas políticas personalizadas pueden anular esto en vez de utilizar tight packing.
using System;
using System.Linq;
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
public class DefaultPackerPolicySample : UnityEditor.Sprites.IPackerPolicy
{
protected class Entry
{
public Sprite sprite;
public UnityEditor.Sprites.AtlasSettings settings;
public string atlasName;
public SpritePackingMode packingMode;
public int anisoLevel;
}
private const uint kDefaultPaddingPower = 3; // Good for base and two mip levels.
public virtual int GetVersion() { return 1; }
protected virtual string TagPrefix { get { return "[TIGHT]"; } }
protected virtual bool AllowTightWhenTagged { get { return true; } }
protected virtual bool AllowRotationFlipping { get { return false; } }
public static bool IsCompressedFormat(TextureFormat fmt)
{
if (fmt >= TextureFormat.DXT1 && fmt <= TextureFormat.DXT5)
return true;
if (fmt >= TextureFormat.DXT1Crunched && fmt <= TextureFormat.DXT5Crunched)
return true;
if (fmt >= TextureFormat.PVRTC_RGB2 && fmt <= TextureFormat.PVRTC_RGBA4)
return true;
if (fmt == TextureFormat.ETC_RGB4)
return true;
if (fmt >= TextureFormat.ATC_RGB4 && fmt <= TextureFormat.ATC_RGBA8)
return true;
if (fmt >= TextureFormat.EAC_R && fmt <= TextureFormat.EAC_RG_SIGNED)
return true;
if (fmt >= TextureFormat.ETC2_RGB && fmt <= TextureFormat.ETC2_RGBA8)
return true;
if (fmt >= TextureFormat.ASTC_RGB_4x4 && fmt <= TextureFormat.ASTC_RGBA_12x12)
return true;
if (fmt >= TextureFormat.DXT1Crunched && fmt <= TextureFormat.DXT5Crunched)
return true;
return false;
}
public void OnGroupAtlases(BuildTarget target, UnityEditor.Sprites.PackerJob job, int[] textureImporterInstanceIDs)
{
List<Entry> entries = new List<Entry>();
foreach (int instanceID in textureImporterInstanceIDs)
{
TextureImporter ti = EditorUtility.InstanceIDToObject(instanceID) as TextureImporter;
TextureFormat desiredFormat;
ColorSpace colorSpace;
int compressionQuality;
ti.ReadTextureImportInstructions(target, out desiredFormat, out colorSpace, out compressionQuality);
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 = desiredFormat;
entry.settings.colorSpace = colorSpace;
// Use Compression Quality for Grouping later only for Compressed Formats. Otherwise leave it Empty.
entry.settings.compressionQuality = IsCompressedFormat(desiredFormat) ? compressionQuality : 0;
entry.settings.filterMode = Enum.IsDefined(typeof(FilterMode), ti.filterMode)
? ti.filterMode
: FilterMode.Bilinear;
entry.settings.maxWidth = 2048;
entry.settings.maxHeight = 2048;
entry.settings.generateMipMaps = ti.mipmapEnabled;
entry.settings.enableRotation = AllowRotationFlipping;
if (ti.mipmapEnabled)
entry.settings.paddingPower = kDefaultPaddingPower;
else
entry.settings.paddingPower = (uint)EditorSettings.spritePackerPaddingPower;
#if ENABLE_ANDROID_ATLAS_ETC1_COMPRESSION
entry.settings.allowsAlphaSplitting = ti.GetAllowsAlphaSplitting ();
#endif //ENABLE_ANDROID_ATLAS_ETC1_COMPRESSION
entry.atlasName = ParseAtlasName(ti.spritePackingTag);
entry.packingMode = GetPackingMode(ti.spritePackingTag, tis.spriteMeshType);
entry.anisoLevel = ti.anisoLevel;
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);
UnityEditor.Sprites.AtlasSettings settings = settingsGroup.Key;
settings.anisoLevel = 1;
// Use the highest aniso level from all entries in this atlas
if (settings.generateMipMaps)
foreach (Entry entry in settingsGroup)
if (entry.anisoLevel > settings.anisoLevel)
settings.anisoLevel = entry.anisoLevel;
job.AddAtlas(atlasName, settings);
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;
}
}
using System;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.Sprites;
using System.Collections.Generic;
// TightPackerPolicy will tightly pack non-rectangle Sprites unless their packing tag contains "[RECT]".
class TightPackerPolicySample : DefaultPackerPolicySample
{
protected override string TagPrefix { get { return "[RECT]"; } }
protected override bool AllowTightWhenTagged { get { return false; } }
protected override bool AllowRotationFlipping { get { return false; } }
}
using System;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.Sprites;
using System.Collections.Generic;
// TightPackerPolicy will tightly pack non-rectangle Sprites unless their packing tag contains "[RECT]".
class TightRotateEnabledSpritePackerPolicySample : DefaultPackerPolicySample
{
protected override string TagPrefix { get { return "[RECT]"; } }
protected override bool AllowTightWhenTagged { get { return false; } }
protected override bool AllowRotationFlipping { get { return true; } }
}