スプライト画像をデザインする際,各キャラクター毎のテクスチャファイルとして作業すると便利でです。しかし,スプライトテクスチャの大部分は,多くの場合,グラフィック要素が空の領域に取り込まれ,この空間は実行時に無駄なビデオメモリを消費します。最適なパフォーマンスのために,いくつかのスプライトテクスチャ画像を一緒にまとめてアトラスとして知られている単一のテクスチャにパックするのが最善です。Unityは,個々のスプライトテクスチャからアトラスを生成するプロセスを自動化するユーティリティ Sprite Packerを提供します。
Unityがスプライトアトラステクスチャの生成と処理を行い,ユーザーは手動で割り当てを行う必要はありません。アトラスは必要に応じて再生モードへの移行やビルド時にパックすることができ,スプライトオブジェクトの画像がアトラスから生成されて取得できます。
ユーザーはそのテクスチャのスプライトのパッキングを有効にするテクスチャインポータでパッキングタグを指定する必要があります。
スプライトパッカーは,デフォルトでは無効になっていますが,エディタの設定からそれを設定することができます。メニューから Disabled から Enabled for Builds (すなわち,パッキングはビルドで有効,プレイモードでは無効),または Always Enabled (すなわち,パッキングはプレイモードとビルドの両方に有効)に変更できます。
)スプライトパッキングモードはSprite Packer windowを開き(menu: Window -> Sprite Packer) ,左上の隅の
ボタンをクリックし,アトラス内にパックされたテクスチャの配置を参照してください。プロジェクトパネルでスプライトを選択した場合は,アトラスでの位置を示すために強調表示されます。輪郭は実際のメッシュの輪郭を描画し,それはまた,結合したパッキングの領域を定義する。
スプライトパッカーウィンドウ上のツールバーには,パッキングとビューイングのためのコントロールを数持っています。Packボタンは,パッキング処理を初期化しますが,アトラスは最後にパッキングされてから,変更がなければ自動更新することはしません(下記のCustomizing the Sprite Packerで説明しているとおり,カスタムパッキングポリシーを実装する際にRepackボタンが表示されます)。View Atlas と Page # のメニューでは,どのアトラスか,どのページかを選択することを決めることができます(最大テクスチャサイズで全てのスプライトのため十分なスペースがない場合は,一つのアトラスを複数の「ページ」に分割することができます)。ページ番号の横のメニューでアトラスのために使用される“パッキング ポリシー” を選択できます(下記参照)。ツールバーの右にある2つのコントロールでビューをズームするとアトラスのためのカラーとアルファー表示を切り替えることができます。
スプライトパッカーは,アトラスにスプライトをどう割り当てるか方法を決めるためにpacking policyを使用しています。あなた自身の独自のパッキングポリシーを作成することは可能ですが(下記参照),Default Packer Policy と Tight Packer Policy オプションが常に用意されており,使用できます。これらのポリシーでは Texture Importerの Packing Tag プロパティはアトラスの名前で選択してスプライトはパックされます。したがって同じパッキングタグを持つすべてのスプライトを同じアトラスにパックされます。アトラスはさらにテクスチャのインポート設定により,ソーステクスチャにセットした内容と合致するようにソートされます。
DefaultPackerPolicyオプションはほとんどの目的には十分ですが,あなた自身が必要とする場合,独自のカスタム·パッキングポリシーを実装することができます。これを行うには,エディタ·スクリプトでクラスのUnityEditor.Sprites.IPackerPolicyインタフェース を実装する必要があります。このインタフェースには次のメソッドが必要です。
DefaultPackerPolicyはデフォルトではタイトなパッキング(SpritePackingModeを参照)を使用しています。カスタムポリシーは,これを上書きし,代わりに矩形パッキングを使用することができます。texture-spaceエフェクトの使用やスプライトをレンダリングするための別のメッシュを使用したい場合に便利です。
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 int anisoLevel;
}
private const uint kDefaultPaddingPower = 2; // 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; } }
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 = 2048;
entry.settings.maxHeight = 2048;
entry.settings.generateMipMaps = ti.mipmapEnabled;
if (ti.mipmapEnabled)
entry.settings.paddingPower = kDefaultPaddingPower;
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);
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; } }
}