【Unity】スクリプトを生成してGameObjectにアタッチするまで自動化する

## 環境
OSX 10.11.6
Unity 5.4.2

## やりたいこと
1. テンプレートからスクリプトを生成
2. 空のGameObjectを生成してスクリプトをアタッチ
3. プレハブ化

これらを全部自動でやってほしい。

## テンプレートの準備
まず、生成するスクリプトの元となるテンプレートを用意する。

```text:CustomTemplate.txt
using UnityEngine;

public class #SCRIPTNAME# : MonoBehaviour
{
	void Awake()
	{
		Debug.Log("#SCRIPTNAME#");
	}
}
```

テンプレート内の文字列"#SCRIPTNAME#"はスクリプト名に置換される場所とする。


## スクリプト生成からプレハブ化まで
このテキストファイルをStreamReaderで読み込んで置換、csファイルとして保存する。

```csharp:CreateScript.cs
static string templatePath = "テンプレートファイルのパス";
static string scriptName = "スクリプト名";

static void CreateNewScript()
{
	// テンプレートを読み込んでtempに格納
	StreamReader reader = new StreamReader(templatePath, Encoding.GetEncoding("Shift_JIS"));
	string template = reader.ReadToEnd();
	reader.Close();

	// テンプレート中の文字列"#SCRIPTNAME#"をスクリプト名に置換
	string scriptText = template.Replace("#SCRIPTNAME#", scriptName);

	// csファイルとしてScriptsフォルダに保存
	File.WriteAllText("Assets/Scripts/" + scriptName + ".cs", scriptText, Encoding.UTF8);

	AssetDatabase.Refresh();
}
```
スクリプトを生成した後、コンパイルが走る。
コンパイル終了後に空のGameObjectを生成して生成したスクリプトをアタッチしたいのだが、これが少し厄介。
コンパイルが終わったら処理が終了してしまい変数を保持できないため、必要な情報を外に出しておく必要がある。
今回の場合、Unity終了時に自動的に削除されるTempフォルダの中に一時ファイルとして保存することにした。

```csharp:CreateScript.cs
static string tempFilePath = "Temp/一時ファイル名";

static void CreateTempFile()
{
	// 一時ファイルを生成し、必要な情報(ここではスクリプト名のみ)を書き込んで保存
	File.WriteAllText(tempFilePath, scriptName, Encoding.UTF8);
}

[DidReloadScripts]
static void OnCompiled()
{
	// 一時ファイルがあれば処理
	if (File.Exists(tempFilePath)) {
		StreamReader reader = new StreamReader(tempFilePath, Encoding.GetEncoding("Shift_JIS"));

		// スクリプト名を読み込み
		scriptName = reader.ReadLine();

		reader.Close();

		// 空のGameObjectを生成してスクリプトをアタッチし、プレハブ化(後述)
		CreateAttachedPrefab();

		// 一時ファイルの削除
		File.Delete(tempFilePath);
	}
}
```

[DidReloadScripts](https://docs.unity3d.com/jp/540/ScriptReference/Callbacks.DidReloadScripts.html)アトリビュートが設定されたメソッドは、再コンパイル後に自動的に実行される。
コンパイルが正常に終了すれば生成したスクリプトをアタッチできるようになるので、続けてこれをアタッチしたGameObjectを生成してプレハブ化する。

```csharp:CreateScript.cs
static void CreateAttachedPrefab()
{
	// 空のGameObjectの生成
	GameObject go = new GameObject(scriptName);

	// 生成したスクリプトをアタッチ
	go.AddComponent(Type.GetType(scriptName));

	// プレハブの生成
	string prefabPath = "Assets/" + scriptName + ".prefab";
	GameObject prefab = PrefabUtility.CreatePrefab(prefabPath, go, ReplacePrefabOptions.ReplaceNameBased);
	go = PrefabUtility.ConnectGameObjectToPrefab(go, prefab);
}
```

## EditorWindowで実行
これらの処理をGUIで実行できるようにEditorWindowとして作成する。
以下、全ソース。

```csharp:CreateScript.cs
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System;
using System.IO;
using System.Text;

public class CreateScript : EditorWindow
{
	static string templatePath = "Assets/CustomTemplate.txt";
	static string scriptName = "NewScript";
	static string tempFilePath = "Temp/TempCreateScriptInfo";


	[MenuItem("Project/CreateScript")]
	static public void Open()
	{
		GetWindow<CreateScript>(true, "CreateScene");
	}

	private void OnGUI()
	{
		// コンパイル中は操作できないように
		EditorGUI.BeginDisabledGroup(EditorApplication.isCompiling);

		// スクリプト名を入力
		scriptName = EditorGUILayout.TextField("ScriptName", scriptName);

		// Createボタンを押してスクリプトと一時ファイルを生成し、ウィンドウを閉じる
		if (GUILayout.Button("Create")) {
			CreateNewScript();
			CreateTempFile();

			Close();
		}
		EditorGUI.EndDisabledGroup();
	}

	static void CreateNewScript()
	{
		// テンプレートを読み込んでtempに格納
		StreamReader reader = new StreamReader(templatePath, Encoding.GetEncoding("Shift_JIS"));
		string template = reader.ReadToEnd();
		reader.Close();

		// テンプレート中の文字列"#SCRIPTNAME#"をスクリプト名に置換
		string scriptText = template.Replace("#SCRIPTNAME#", scriptName);

		// csファイルとしてScriptsフォルダに保存
		File.WriteAllText("Assets/Scripts/" + scriptName + ".cs", scriptText, Encoding.UTF8);

		AssetDatabase.Refresh();
	}

	static void CreateTempFile()
	{
		// 一時ファイルを生成し、必要な情報(ここではスクリプト名のみ)を書き込んで保存
		File.WriteAllText(tempFilePath, scriptName, Encoding.UTF8);
	}

	[DidReloadScripts]
	static void OnCompiled()
	{
		// 一時ファイルがあれば処理
		if (File.Exists(tempFilePath)) {
			StreamReader reader = new StreamReader(tempFilePath, Encoding.GetEncoding("Shift_JIS"));

			// スクリプト名を読み込み
			scriptName = reader.ReadLine();

			reader.Close();

			// 空のGameObjectを生成してスクリプトをアタッチし、プレハブ化
			CreateAttachedPrefab();

			// 一時ファイルの削除
			File.Delete(tempFilePath);
		}
	}

	static void CreateAttachedPrefab()
	{
		// 空のGameObjectの生成
		GameObject go = new GameObject(scriptName);

		// 生成したスクリプトをアタッチ
		go.AddComponent(Type.GetType(scriptName));

		// プレハブの生成
		string prefabPath = "Assets/" + scriptName + ".prefab";
		GameObject prefab = PrefabUtility.CreatePrefab(prefabPath, go, ReplacePrefabOptions.ReplaceNameBased);
		go = PrefabUtility.ConnectGameObjectToPrefab(go, prefab);
	}
}
```