使用バージョン:Unity 4.5.2f1
製作中のゲームで海面に大量のブイを並べて配置したいのですが、手作業ではとても無理なのでエディタ拡張を利用して、一定間隔でGameObjectを複製して配置する機能を作ってみました。
初めてのエディタ拡張で間違ってる部分もありそうなので、ご意見など頂けたら幸いです。(@mokusan)
先ず完成イメージは以下。
このように必要なパラメータを設定して「Create」を押下すると、先に挙げたゲーム画面のようにブイが一定間隔で配置される。
・エディタ拡張用のスクリプト
「Editor」という名前のフォルダ配下に置く必要がある。
Resourcesと同様で他のフォルダを掘って、その下に「Editor」フォルダを配置しても大丈夫。
(スクリプト”CreateBuoys.cs”の内容全文はこの投稿の最後に掲載しておきます。)
・UnityEditor名前空間の型の使用を許可
通常のUnityEngine以外にUnityEditorを指定しておく。
using UnityEngine; using UnityEditor; using System.Collections;
[MenuItem("GameObject/Create Other/Create Buoys")] static void Init() { EditorWindow.GetWindow<CreateBuoys>(true, "Create Buoys"); }
“GameObject/Create Other/”配下を指定することで、Hierarchyビューの「Create」のメニューにも追加される。
・ウィンドウの表示
メニューから「Create Buoys」が選択された時に専用のウィンドウを表示する。
[MenuItem("GameObject/Create Other/Create Buoys")] static void Init() { EditorWindow.GetWindow<CreateBuoys>(true, "Create Buoys"); }
・ウィンドウ内容の表示
必要なメンバ変数を定義し、OnGUI()内で記述する。
public class CreateBuoys : EditorWindow { private GameObject parent; private GameObject prefab; private int numX = 1; private int numY = 1; private int numZ = 1; private float intervalX = 1; private float intervalY = 1; private float intervalZ = 1;
void OnGUI() { try { parent = EditorGUILayout.ObjectField("Parent", parent, typeof(GameObject), true) as GameObject; prefab = EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), true) as GameObject; GUILayout.Label("X : ", EditorStyles.boldLabel); numX = int.Parse(EditorGUILayout.TextField("num", numX.ToString())); intervalX = int.Parse(EditorGUILayout.TextField("interval", intervalX.ToString())); GUILayout.Label("Y : ", EditorStyles.boldLabel); numY = int.Parse(EditorGUILayout.TextField("num", numY.ToString())); intervalY = int.Parse(EditorGUILayout.TextField("interval", intervalY.ToString())); GUILayout.Label("Z : ", EditorStyles.boldLabel); numZ = int.Parse(EditorGUILayout.TextField("num", numZ.ToString())); intervalZ = int.Parse(EditorGUILayout.TextField("interval", intervalZ.ToString())); GUILayout.Label("", EditorStyles.boldLabel); if (GUILayout.Button("Create")) Create(); } catch (System.FormatException) {} }
通常のOnGUI()でGUIを作るのと同じ要領だが、EditorGUILayoutのパーツが使える。
詳細はリファレンスを参照。
GUILayout
EditorGUILayout
void OnEnable() { if (Selection.gameObjects.Length > 0) parent = Selection.gameObjects[0]; }
ウィンドウ表示時に自動で呼ばれるメソッド。
Selection.gameObjects
void OnSelectionChange() { if (Selection.gameObjects.Length > 0) prefab = Selection.gameObjects[0]; Repaint(); }
選択内容の変更時に自動で呼ばれるメソッド。
・「Create」ボタン押下時に実際にブイを作成する
private void Create() { if (prefab == null) return; int count = 0; Vector3 pos; pos.x = -(numX - 1) * intervalX / 2; for (int x = 0; x < numX; x++) { pos.y = -(numY - 1) * intervalY / 2; for (int y = 0; y < numY; y++) { pos.z = -(numZ - 1) * intervalZ / 2; for (int z = 0; z < numZ; z++) { GameObject obj = Instantiate(prefab, pos, Quaternion.identity) as GameObject; obj.name = prefab.name + count++; if (parent) obj.transform.parent = parent.transform; Undo.RegisterCreatedObjectUndo(obj, "Create Buoys"); pos.z += intervalZ; } pos.y += intervalY; } pos.x += intervalX; } }
普通にInstantiate()すれば、Hierarchyに追加される。
この際、Undoを設定しておくことにより、後で取り消しができる。(66行目)
Editor拡張を利用したのは初めてだが、食わず嫌いだったと反省している。
Unityで新規にGameObjectを作った時にHierarchyのトップに作成されてしまうというのは、良く聞く不満だが、Editor拡張を使えばその辺りも何とかできそうだと言うのが分かった。
using UnityEngine; using UnityEditor; using System.Collections; public class CreateBuoys : EditorWindow { private GameObject parent; private GameObject prefab; private int numX = 1; private int numY = 1; private int numZ = 1; private float intervalX = 1; private float intervalY = 1; private float intervalZ = 1; [MenuItem("GameObject/Create Other/Create Buoys")] static void Init() { EditorWindow.GetWindow<CreateBuoys>(true, "Create Buoys"); } void OnEnable() { if (Selection.gameObjects.Length > 0) parent = Selection.gameObjects[0]; } void OnSelectionChange() { if (Selection.gameObjects.Length > 0) prefab = Selection.gameObjects[0]; Repaint(); } void OnGUI() { try { parent = EditorGUILayout.ObjectField("Parent", parent, typeof(GameObject), true) as GameObject; prefab = EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), true) as GameObject; GUILayout.Label("X : ", EditorStyles.boldLabel); numX = int.Parse(EditorGUILayout.TextField("num", numX.ToString())); intervalX = int.Parse(EditorGUILayout.TextField("interval", intervalX.ToString())); GUILayout.Label("Y : ", EditorStyles.boldLabel); numY = int.Parse(EditorGUILayout.TextField("num", numY.ToString())); intervalY = int.Parse(EditorGUILayout.TextField("interval", intervalY.ToString())); GUILayout.Label("Z : ", EditorStyles.boldLabel); numZ = int.Parse(EditorGUILayout.TextField("num", numZ.ToString())); intervalZ = int.Parse(EditorGUILayout.TextField("interval", intervalZ.ToString())); GUILayout.Label("", EditorStyles.boldLabel); if (GUILayout.Button("Create")) Create(); } catch (System.FormatException) {} } private void Create() { if (prefab == null) return; int count = 0; Vector3 pos; pos.x = -(numX - 1) * intervalX / 2; for (int x = 0; x < numX; x++) { pos.y = -(numY - 1) * intervalY / 2; for (int y = 0; y < numY; y++) { pos.z = -(numZ - 1) * intervalZ / 2; for (int z = 0; z < numZ; z++) { GameObject obj = Instantiate(prefab, pos, Quaternion.identity) as GameObject; obj.name = prefab.name + count++; if (parent) obj.transform.parent = parent.transform; Undo.RegisterCreatedObjectUndo(obj, "Create Buoys"); pos.z += intervalZ; } pos.y += intervalY; } pos.x += intervalX; } } }