【Unity】3Dオブジェクトのサムネイル画像を一括で作るEditor拡張

# サムネイル画像がたまーに必要になる
Unityを利用したプロジェクトで開発していると、他職種の方とやりとりが必要になります。
特にプランナーの方とやりとりする時には、スプレッドシートに素材をまとめておいて、随時マスタのように反映させていく
なんて事もあるのでは無いでしょうか。

# 素材の管理にサムネイル画像が欲しい！
デザイナーの方にまるっとお願いしちゃうのもいいのですが、
デザイナーが外注だったりして、そう簡単にお願いできなかったり。
2Dならまだしも、3Dモデルを大量に扱っている場合は面倒です。
こんな時はUnity拡張でサムネイルを作成するEditorを作ってしまえば、
大量に増えても楽ちんです。

# ソースコード
[github](https://github.com/jake1256/CaptureCreater)

EditorCoroutineはk_yanaseさんの
[UnityEditor上でStartContinueっぽいのを動かす](http://qiita.com/k_yanase/items/686fc3134c363ffc5239)
を参考にしてください。

```csharp:CaptureCreater.cs

using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.IO;

public class CaptureCreater : EditorWindow
{

	// このディレクトリ以下のprefabのキャプチャを全て取得
	UnityEngine.Object searchDirectory;
	List<GameObject> objList = new List<GameObject>();
	string dirPath = "Captures/"; // 出力先ディレクトリ(Assets/Captures/以下に出力されます)
	int width = 100; // キャプチャ画像の幅
	int height = 100; // キャプチャ画像の高さ
	
	[MenuItem("Window/CaptureCreater")]
	static void ShowWindow()
	{
		EditorWindow.GetWindow (typeof (CaptureCreater));
	}
	
	void OnGUI()
	{
		// Unity EditorのUI
		GUILayout.BeginHorizontal();
		GUILayout.Label("Search Directory : ", GUILayout.Width(110));
		searchDirectory = EditorGUILayout.ObjectField(searchDirectory, typeof(UnityEngine.Object), true);
		GUILayout.EndHorizontal();
		EditorGUILayout.Space();
		
		GUILayout.BeginHorizontal();
		GUILayout.Label("Save directory : ", GUILayout.Width(110));
		dirPath = (string)EditorGUILayout.TextField(dirPath);
		GUILayout.EndHorizontal();
		EditorGUILayout.Space();
		
		GUILayout.BeginHorizontal();
		GUILayout.Label("Width : ", GUILayout.Width(110));
		width = EditorGUILayout.IntField(width);
		GUILayout.EndHorizontal();
		EditorGUILayout.Space();
		
		GUILayout.BeginHorizontal();
		GUILayout.Label("Height : ", GUILayout.Width(110));
		height = EditorGUILayout.IntField(height);
		GUILayout.EndHorizontal();
		EditorGUILayout.Space();
		
		EditorGUILayout.BeginHorizontal();
		if(GUILayout.Button(new GUIContent("Capture")))
		{

			if(searchDirectory == null) return;
			
			// 出力先ディレクトリを生成
			if (!System.IO.File.Exists(dirPath))
			{
				System.IO.Directory.CreateDirectory(dirPath);
			}

			 objList.Clear();
			
			// 指定ディレクトリ内のprefabを全て取り出してListに入れる
			string replaceDirectoryPath = AssetDatabase.GetAssetPath(searchDirectory);
			string[] filePaths = Directory.GetFiles( replaceDirectoryPath , "*.*" );
			foreach(string filePath in filePaths)
			{
				GameObject obj =  AssetDatabase.LoadAssetAtPath( filePath , typeof(GameObject)) as GameObject;
				if(obj != null){
					 objList.Add(obj);
				}
			}
			
			EditorCoroutine.Start(Exec(objList));

			GUILayout.EndHorizontal();
			EditorGUILayout.Space();
		}
	}

	// List内のGameObjectを配置しつつ、キャプチャを取得
	IEnumerator Exec(List<GameObject> objList){
		foreach(GameObject obj in objList)
		{
			// Instantiateして向きを調整して取りやすい位置に
			GameObject unit = Instantiate(obj , Vector3.zero , Quaternion.identity) as GameObject;
			unit.transform.eulerAngles = new Vector3(270.0f , 0.0f , 0.0f);
			
			yield return new EditorCoroutine.WaitForSeconds(1.0f);

			Capture(obj.name);

			// キャプチャ撮った後は捨てる
			DestroyImmediate(unit);
		}
	}
	
	void Capture(string fileName)
	{
		Vector3 nowPos 	= Camera.main.transform.position;
		float nowSize 	= Camera.main.orthographicSize;
		
		// カメラ調整
		Camera.main.transform.position 	= new Vector3 (nowPos.x, nowPos.y, nowPos.z);
		Camera.main.orthographicSize 	= 100.0f;
		
		// RenderTextureを生成して、これに現在のSceneに映っているものを書き込む
		RenderTexture renderTexture = new RenderTexture(width, height, 24);
		Camera.main.targetTexture = renderTexture;
		Camera.main.Render();
		RenderTexture.active = renderTexture;
		Texture2D texture2D = new Texture2D(width, height, TextureFormat.ARGB32, false);
		texture2D.ReadPixels( new Rect(0, 0, width, height), 0, 0);
		Camera.main.targetTexture = null;
		
		for (int y = 0; y < height; y++)
		{
			for (int x = 0; x < width; x++)
			{
				Color c = texture2D.GetPixel(x,y);
				c = new Color(c.r , c.g , c.b , c.a);
				texture2D.SetPixel(x , y , c);
			}
		}

		// textureのbyteをファイルに出力
		byte[] bytes = texture2D.EncodeToPNG();
		System.IO.File.WriteAllBytes( dirPath  + fileName + ".png", bytes );
		
		// 後処理
		Camera.main.targetTexture = null;
		RenderTexture.active = null;
		renderTexture.Release();
		
		// カメラを元に戻す
		Camera.main.transform.position = nowPos;
		Camera.main.orthographicSize = nowSize;
		Resources.UnloadUnusedAssets();
		System.GC.Collect();
	}
}
```

# 使い方
こんな感じで3Dオブジェクトがあるとします。
![スクリーンショット 2015-06-16 23.56.56.png](https://qiita-image-store.s3.amazonaws.com/0/26453/ed455b04-0417-d28b-4324-2977acd3419e.png)

Window -> CaptureCreaterで起動します。
![スクリーンショット 2015-06-16 23.58.01.png](https://qiita-image-store.s3.amazonaws.com/0/26453/e4cb70d6-9173-6356-c3ac-bacf7ea80b11.png)

パラメータ説明
* Search Directory : 撮影したいObjectが格納されているディレクトリ
* Save Directory : 出力先ディレクトリ
* Width : 画像幅
* Height : 画像高さ

キャプチャを取りたいオブジェクトが格納されているディレクトリを
Search DirectoryにD&Dして、パラメータを入れ、
Captureボタンを押下するだけでOKです。

![スクリーンショット 2015-06-17 0.03.36.png](https://qiita-image-store.s3.amazonaws.com/0/26453/8f402fcd-ab10-9ffb-c4e4-47b78b401e65.png)

こんな感じで出力されます。

# 注意
オブジェクトによっては、大きすぎたり小さすぎたりすることがあります。
その時はカメラの位置をずらしたり、オブジェクトをInstantiateする位置を
指定したりして調整すると良いです。

2Dでももちろん使用可能です。

実はこの投稿は次回への布石だったりします。
続きは後日。