縦横の分割数を指定し一枚の画像をMultiple Spriteに分割するEditor拡張コード


　この投稿では、UnityのEditor拡張のコードを用いて、縦と横それぞれの分割数を指定して、一枚の画像をMultiple Spriteに分割する方法を紹介します。

　画像を分割する箇所のコードは、[kyusyukeigo](@https://github.com/anchan828)さんがUnite Japan 2014ので発表された[MultiSpriteEditorWindow](https://github.com/anchan828/unitejapan2014/blob/master/MultiSpriteEditorWindow/Assets/MultiSpriteEditorWindow/Editor/MultiSpriteEditorWindow.cs)を参考にさせていただきました。

# なにが嬉しいの？


　GUIを使い、プロジェクトに取り込んだ画像をSpriteに設定し、Sprite ModeをMultipleにし、Sprite Editorを開いて、GridモードでSpriteを分割すること作業は若干煩雑です。そして、この若干煩雑なプロセスを複数枚にわたり、または何回も繰り返し手動で行うの非常に面倒です。

　このような手順はEditor拡張のコードを用い、自動化もしくは適切なレベルまで半自動化してしまうべきでしょう。

# ソースコード

　縦横の分割数を指定し一枚の画像をMultiple Spriteに分割するEditor拡張コードを下記に示します。

　`DividSprite`メソッドの第1引数`texturePath`が分割対象の画像のパス、第2引数`horizontalCount`は水平方向の分割数、第3引数`verticalCount`は垂直方向の分割数です。


```csharp:分割数を指定して、一枚のTextureをMultiplepriteにするEditor拡張コード
using UnityEditor;
using UnityEngine;
using System.Linq;

public static class SpriteDivider
{
	public static void DividSprite (string texturePath, int horizontalCount, int verticalCount)
	{
		TextureImporter importer = TextureImporter.GetAtPath (texturePath) as TextureImporter;

		importer.textureType = TextureImporterType.Sprite;
		importer.spriteImportMode = SpriteImportMode.Multiple;
		importer.filterMode = FilterMode.Point;
		EditorUtility.SetDirty (importer);
		AssetDatabase.ImportAsset (texturePath, ImportAssetOptions.ForceUpdate);

		Texture texture = AssetDatabase.LoadAssetAtPath (texturePath, typeof(Texture)) as Texture;
		importer.spritePixelsPerUnit = Mathf.Max (texture.width / horizontalCount, texture.height / verticalCount);
		importer.spritesheet = CreateSpriteMetaDataArray (texture, horizontalCount, verticalCount);

		EditorUtility.SetDirty (importer);
		AssetDatabase.ImportAsset (texturePath, ImportAssetOptions.ForceUpdate);
	}

	static SpriteMetaData[] CreateSpriteMetaDataArray (Texture texture, int horizontalCount, int verticalCount)
	{
		float spriteWidth = texture.width / horizontalCount;
		float spriteHeight = texture.height / verticalCount;

		return Enumerable
			.Range (0, horizontalCount * verticalCount)
			.Select (index => {
				int x = index % horizontalCount;
				int y = index / horizontalCount;

				return new SpriteMetaData {
					name = string.Format ("{0}_{1}", texture.name, index),
					rect = new Rect (spriteWidth * x, texture.height - spriteHeight * (y + 1), spriteWidth, spriteHeight)
				};
			})
			.ToArray ();
	}
}

```

# やっていること

メソッドの引数の`texturePath`が示す画像に対して以下のことを行います。

* `TextureType`を`Sprite`に設定
* `SpriteImportMode`を`Multiple`に設定
* `FilterMode`を`Point`に設定
* `SpritePixelsPerUnit`を分割後の`Sprite`の長辺の長さに設定
* 引数に指定した縦横それぞれの分割数でSpriteに分割(順序はGUIから手動で分割した時と揃えている、後述)

# ポイント

## 一度Spriteへの変更を反映させる

　下記のコードは、一度画像をSpriteに変更後、その変更を反映させている部分を再掲した物です。

```csharp:一部抜粋(1)
importer.textureType = TextureImporterType.Sprite;
importer.spriteImportMode = SpriteImportMode.Multiple;
importer.filterMode = FilterMode.Point;
EditorUtility.SetDirty (importer);
AssetDatabase.ImportAsset (texturePath, ImportAssetOptions.ForceUpdate);
```

　こうしないと画像のサイズを正しく読み込めない場合があります。

## GUIから手動で分割した時と同じSpriteの順序・名前に

　下記のコードは、分割後のSpriteの設定を生成する箇所を再掲した物です。

```csharp:一部抜粋(2)
int x = index % horizontalCount;
int y = index / horizontalCount;

return new SpriteMetaData {
	name = string.Format ("{0}_{1}", texture.name, index),
	rect = new Rect (spriteWidth * x, texture.height - spriteHeight * (y + 1), spriteWidth, spriteHeight)
};
```

　GUIのGridモードでSpriteを分割した場合、左上が起点で、まずは水平方向に、0,1,2...と順序がふられていきます。これと同じ順序で分割するようにしています。
　また名前もGUIで分割したものと同じになるようにしています。

# 利用例(呼び出すコード)

　画像を分割する`SpriteDivider.DivideSprite`メソッドを呼び出すための、Editor拡張のコードを下記に示します。

　これを使えば、同じ分割設定の画像であれば、何枚でも一気にSpriteを分割することが可能です。煩雑な処理を何度もやる必要はありません。

　分割の対象になるのは、Project Window中で選択しているTexutreが対象です。複数選択している場合、全てのTextureが分割されます。Texutureを選択した状態でメニューから、`Assets -> Divide Textures`を選択してください。

　ここでは横の分割数を3、縦の分割数を4にしています。


```csharp:分割するメソッドを呼び出すコード
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Collections.Generic;

public static class SpriteDividerCaller
{
	[MenuItem("Assets/Divide Textures")]
	public static void DivideImages ()
	{
		int horizontalCount = 3;
		int verticalCount = 4;

		IEnumerable<Texture> targets = Selection.objects.OfType<Texture> ();

		if (!targets.Any ()) {
			Debug.LogWarning ("Please selecting textures.");
			return;
		}

		foreach (Texture target in targets) {
			SpriteDivider.DividSprite (AssetDatabase.GetAssetPath (target), horizontalCount, verticalCount);
		}
	}
}
```

　ポイントはLINQを使っている分割対象を取得する次の箇所です。

```csharp:
IEnumerable<Texture> targets = Selection.objects.OfType<Texture> ();
```

　`Selection.objects`は現在選択しているものを`UnityEngine.Object[]`で取得するプロパティです。この配列には、`Texture`型以外の要素が存在する可能性があります。ここでは`OfType`メソッドを使って、`Texture`型にキャスト可能な要素はキャストをして、キャスト不可能な要素は取り除いています。また、これまたLINQのAnyメソッドで分割対象の要素の存在を確認し、分割対象が存在しない場合はその旨を通知し、処理を終了しています。

# 参考・関連リンク

* [MultiSpriteEditorWindow](https://github.com/anchan828/unitejapan2014/blob/master/MultiSpriteEditorWindow/Assets/MultiSpriteEditorWindow/Editor/MultiSpriteEditorWindow.cs) by [kyusyukeigo](@https://github.com/anchan828)
* [TexureImporterのScript Reference](http://docs.unity3d.com/ScriptReference/TextureImporter.html)
 
# 関連投稿

* [複数枚のSpriteからAnimationClipを生成するEditor拡張コード](http://qiita.com/RyotaMurohoshi/items/5cb865c23a50055cf92f)
* [非常に単純なAnimatorControllerを生成するEditor拡張コード](http://qiita.com/RyotaMurohoshi/items/c1f29f9afbce910c5438)
* [プロジェクトに新たなPrefabを生成するためのEditor拡張コード](http://qiita.com/RyotaMurohoshi/items/ce01b8f5221a31a5a5e6)