概要
Prefab
へのアタッチにはPrefabUtility
を利用すると簡単に実現できます。
docs.unity3d.com
privatestaticvoid AddComponent(string prefabPath, IReadOnlyList<string> scriptPaths) { if (string.IsNullOrEmpty(prefabPath)) return; if (scriptPaths ==null) return; if (scriptPaths.Count ==0) return; // Prefabを読み込むvar prefab = PrefabUtility.LoadPrefabContents(prefabPath); foreach (var scriptPath in scriptPaths) { // スクリプトをMonoScriptとして読み込むvar scriptAsset = AssetDatabase.LoadAssetAtPath<MonoScript>(scriptPath); if (scriptAsset !=null) { var scriptType = scriptAsset.GetClass(); if (scriptType !=null) { // Prefabへアタッチする prefab.AddComponent(scriptType); } } } // Prefabを保存する PrefabUtility.SaveAsPrefabAsset(prefab, prefabPath); PrefabUtility.UnloadPrefabContents(prefab); }
ただ生成したコードをそのままアタッチするのは一筋縄ではいきません。一度コンパイルを挟まないといけないのです。
コンパイル後にアタッチする
コンパイル後に続きの動作をするためには、シリアライズして保存しておかないといけません。そこで便利なのがScriptableSingleton
です。
docs.unity3d.com
ScriptableSingleton
はコンパイルが走っても値を保持してくれます。
publicclassState : ScriptableSingleton<State> { publicstring[] ScriptPaths { get; set; } }
// 利用する側 State.instance.ScriptPaths =new [] { "Assets/Scripts/Sample.cs", };
コードからコンパイルを要求するには以下のメソッドを用います。
CompilationPipeline.RequestScriptCompilation();
Unity - Scripting API: Compilation.CompilationPipeline.RequestScriptCompilation
そして[DidReloadScripts]
を用いることで、コンパイルした後のタイミングで属性を付与したメソッドを実行することができます。
docs.unity3d.com
publicstaticclassHoge { [DidReloadScripts] publicstaticvoid OnDidReloadScripts() { Debug.Log("Compiled"); } }
実験
パーツは全て整いました。今まで紹介したものを全て組み合わせて、実験をしてみます。
using System.Collections.Generic; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.Compilation; using UnityEngine; namespace Sample { publicclassSampleEditor : EditorWindow { [MenuItem("Sample/Sample Editor")] publicstaticvoid ShowWindow() { GetWindow<SampleEditor>("Sample Editor"); } // コンパイル後に呼び出される [DidReloadScripts] publicstaticvoid OnDidReloadScripts() { if (Application.isBatchMode) return; if (State.instance.ScriptPaths ==null) return; if (State.instance.ScriptPaths.Length ==0) return; AddComponent("Assets/Prefabs/Sample.prefab", State.instance.ScriptPaths); State.instance.ScriptPaths =null; } publicvoid OnGUI() { if (GUILayout.Button("Create And Attach")) { // アタッチするスクリプトを設定してコンパイルを走らせる// 今回は省くがSample.csは生成したものとする State.instance.ScriptPaths =new [] { "Assets/Scripts/Sample.cs", }; AssetDatabase.Refresh(); CompilationPipeline.RequestScriptCompilation(); } } privatestaticvoid AddComponent(string prefabPath, IReadOnlyList<string> scriptPaths) { if (string.IsNullOrEmpty(prefabPath)) return; if (scriptPaths ==null) return; if (scriptPaths.Count ==0) return; // Prefabを読み込むvar prefab = PrefabUtility.LoadPrefabContents(prefabPath); foreach (var scriptPath in scriptPaths) { // スクリプトをMonoScriptとして読み込むvar scriptAsset = AssetDatabase.LoadAssetAtPath<MonoScript>(scriptPath); if (scriptAsset !=null) { var scriptType = scriptAsset.GetClass(); if (scriptType !=null) { // Prefabへアタッチする prefab.AddComponent(scriptType); } } } // Prefabを保存する PrefabUtility.SaveAsPrefabAsset(prefab, prefabPath); PrefabUtility.UnloadPrefabContents(prefab); } } /// <summary>/// コンパイルが走ってもデータを保持する情報です。/// </summary>publicclassState : ScriptableSingleton<State> { publicstring[] ScriptPaths { get; set; } } }
PrefabPathは適宜替えていただいて、メニューバーのSample/Sample Editor
よりボタンを押すと自動でPrefabへコードがアタッチされます。
またAssets/Scripts/Sample.cs
を自動生成したあとにこの処理を走らせてあげれば、実現したいコードは達成できるでしょう。