続きからどうぞ。
Tools > Copy To All Scenesで出てきたウインドウにプレハブをDDしてボタンを押せば、BuildSettingに入ってるシーン全てにプレハブをコピーします。
using UnityEngine; using UnityEditor; using UnityEngine.UI; using UnityEditor.SceneManagement; public class ScriptableGameObject : ScriptableObject { public GameObject prefab; } [ExecuteInEditMode] public class CopyToAllScenes : EditorWindow { static SerializedProperty sp; static GUIStyle style = new GUIStyle(); [MenuItem("Tools/Copy To All Scenes #%&c")] public static void ShowWindow() { var window = GetWindow(typeof(CopyToAllScenes), true, "Copy to all scenes"); window.minSize = new Vector2(400, 100); var obj = CreateInstance<ScriptableGameObject>(); obj.hideFlags = HideFlags.DontSave; // 追記:シーン切り替えで破棄されないように設定 var serializedObject = new SerializedObject(obj); sp = serializedObject.FindProperty("prefab"); // set gui style style.alignment = TextAnchor.MiddleCenter; style.fontStyle = FontStyle.Bold; style.border = new RectOffset(5, 5, 5, 5); style.normal.textColor = Color.green; } /// <summary> /// プレハブかどうかを判定。インスタンスかされていないプロジェクトビュー上のプレハブはこれで検知できる /// </summary> bool IsPrefab(Object obj) { return PrefabUtility.GetPrefabParent(obj) == null && PrefabUtility.GetPrefabObject(obj) != null; } void OnGUI() { GUILayout.Space(10); GUILayout.Label("Select the prefab you want to copy to all scenes", style); GUILayout.Space(10); EditorGUILayout.PropertyField(sp); GUILayout.Space(10); if (GUILayout.Button("Copy this prefab to all scenes")) { // プレハブでなければコピーしない if (!IsPrefab(sp.objectReferenceValue)) { Debug.LogFormat("'{0}' is not a prefab. No copying performed.", sp.objectReferenceValue.name); return; } // 今のシーンが編集中の場合、他のシーンを開くと変更が破棄されてしまうため、セーブしておく if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) { Debug.Log("Process cancelled."); return; } // 今開いているシーンを覚えておく var currentScenePath = EditorSceneManager.GetActiveScene().path; // 全てのシーンに対応 var scenes = EditorBuildSettings.scenes; var copiedCount = 0; var skippedCount = 0; foreach (var scene in scenes) { EditorSceneManager.OpenScene(scene.path); // 存在していれば生成しない if (GameObject.Find(sp.objectReferenceValue.name) != null) { Debug.LogWarningFormat("'{0}' already exists in the scene '{1}'.", sp.objectReferenceValue.name, scene.path); skippedCount++; continue; } // 生成してセーブ PrefabUtility.InstantiatePrefab(sp.objectReferenceValue); copiedCount++; EditorSceneManager.MarkAllScenesDirty(); EditorSceneManager.SaveOpenScenes(); } Debug.LogFormat("'{0}' was copied:{1}, already exists:{2}, total:{3}", sp.objectReferenceValue.name, copiedCount, skippedCount, scenes.Length); // 最後に、実行前に開いていたシーンを開く EditorSceneManager.OpenScene(currentScenePath); } } }
こんな感じです。特に書くこともなくコードのままですが、ちなみに。
エディタメニューのショートカットの制御キーの覚え方
#%&がShift,Ctrl,Altに対応しているので、日本語キーボードなら左から順番に356と覚えれば忘れません。
追記:Type Mismatchエラーについて
このスクリプト、追記部分のhideFlagsの設定をしていないと、実行し終わったあとにType Mismatchと表示されてプレハブを受け付けなくなってしまいます。
これは、SctiptableObject
もGameObject
同様、シーンの切り替えで破棄されてしまうためです。
実はこれ、バグとしてUnityに報告していたのですが、"By Design"という返事が返ってきて、事情を教えていただきました。そのとき紹介してもらった記事も張っておきます。
そこで、HideFlags
のDontSave
を登録することでシーンが切り替わっても破棄されないようにすることでエラーが出なくなります。
リファレンスを見る限り、なんとも名前付けを失敗している気がしますが…HideFlags
というより、HideDestroyFlags
、DontSave
というよりDontDestroyOnSceneLoading
という気がしますが…。
ちなみに、一般的にはScriptableObject
にはHideAndDontSave
を指定するのがセオリーらしいですが、このケースではそうやるとドラッグアンドドロップを受け付けなくなってしまうので、DontSave
でよさそうです。