カスタムエディタでシーンビューにバウンディングボックスを表示するのバウンディングボックスの中にあるゲームオブジェクトをエディタウィンドウを使って選択してみました。
エディタウィンドウを作る
エディタウィンドウを作ってBoundsを持ったスクリプタブルオブジェクトをアタッチし、選択ボタンを押すと中のゲームオブジェクトを選択します。
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class SelectGameObjectsWithinBounds : EditorWindow
{
[MenuItem("Window/Test/SelectGameObjectsWithinBounds")]
public static void CreateWindow()
{
EditorWindow.GetWindow<SelectGameObjectsWithinBounds>();
}
// 選択に使うBoundsを持ったスクリプタブルオブジェクト
TestBounds bounds;
// 選択されたゲームオブジェクトのリスト
List<GameObject> selectedObjs = new List<GameObject>();
private void OnGUI()
{
// スクリプタブルオブジェクトのフィールドを表示
bounds = (TestBounds)EditorGUILayout.ObjectField(bounds, typeof(TestBounds), false);
// スクリプタブルオブジェクトがアタッチされているとき
if (bounds != null)
{
// 選択ボタンを押す
if (GUILayout.Button("選択"))
{
// リストをクリア
selectedObjs.Clear();
// すべてのゲームオブジェクト
foreach (GameObject go in Resources.FindObjectsOfTypeAll(typeof(GameObject)) as GameObject[])
{
// ヒエラルキーにあるゲームオブジェクト
if (!EditorUtility.IsPersistent(go.transform.root.gameObject) && !(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave))
{
// 内部オブジェクトを除外
if (go.name == "InternalIdentityTransform") continue;
// バウンディングボックスの中にあるオブジェクト
if (bounds.bounds.Contains(go.transform.position))
{
// リストに追加
selectedObjs.Insert(0, go);
continue;
}
// バウンディングボックスの外にあるとき
else
{
// レンダラーコンポーネントを取得
var r = go.GetComponent<Renderer>();
// レンダラーコンポーネントがあるとき
if (r != null)
{
// ボウンディングボックスが交差しているとき
if (bounds.bounds.Intersects(r.bounds))
{
// リストに追加
selectedObjs.Insert(0, go);
continue;
}
}
}
}
}
// リストにゲームオブジェクトがあるとき
if (selectedObjs.Count > 0)
{
// すべて選択
Selection.objects = selectedObjs.ToArray();
}
}
// クリアボタンを押す
else if (GUILayout.Button("クリア"))
{
// リストをクリア
selectedObjs.Clear();
}
// リストにゲームオブジェクトがあるとき
if (selectedObjs.Count > 0)
{
// リストの要素を縦に並べる
for (int i = 0; i < selectedObjs.Count; i++)
{
// ボタンを表示する。ボタン上にゲームオブジェクトの名前を表示。
if (GUILayout.Button(selectedObjs[i].name, EditorStyles.textField))
{
// ボタンをクリックすると、そのゲームオブジェクトを選択する。
Selection.activeGameObject = selectedObjs[i];
}
}
}
}
}
}
まず、MenuItem属性を静的メソッドにつけて、メインメニューからこのエディタウィンドウを表示できるようにします。
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class SelectGameObjectsWithinBounds : EditorWindow
{
[MenuItem("Window/Test/SelectGameObjectsWithinBounds")]
public static void CreateWindow()
{
EditorWindow.GetWindow<SelectGameObjectsWithinBounds>();
}
// 選択に使うBoundsを持ったスクリプタブルオブジェクト
TestBounds bounds;
// 選択されたゲームオブジェクトのリスト
List<GameObject> selectedObjs = new List<GameObject>();
アタッチするスクリプタブルオブジェクトや、選択されたゲームオブジェクトのリストのフィールドを宣言しています。
エディタウィンドウの内容をOnGUIメソッドに実装します。まずEditorGUILayout.ObjectFieldメソッドでスクリプタブルオブジェクトをアタッチするフィールドを表示しています。
private void OnGUI()
{
// スクリプタブルオブジェクトのフィールドを表示
bounds = (TestBounds)EditorGUILayout.ObjectField(bounds, typeof(TestBounds), false);
アタッチされているときは、UILayout.Buttonメソッドで「選択」ボタンを表示します。選択ボタンを押すと、まずリストをクリアします。
// スクリプタブルオブジェクトがアタッチされているとき
if (bounds != null)
{
// 選択ボタンを押す
if (GUILayout.Button("選択"))
{
// リストをクリア
selectedObjs.Clear();
リストをクリアしたら、シーンにあるゲームオブジェクトを検索して、条件に合うものをこのリストに追加していきます。Resources.FindObjectsOfTypeAllメソッドとEditorUtility.IsPersistentメソッドを使ってシーンのゲームオブジェクトを検索します。
// すべてのゲームオブジェクト
foreach (GameObject go in Resources.FindObjectsOfTypeAll(typeof(GameObject)) as GameObject[])
{
// ヒエラルキーにあるゲームオブジェクト
if (!EditorUtility.IsPersistent(go.transform.root.gameObject) && !(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave))
{
// 内部オブジェクトを除外
if (go.name == "InternalIdentityTransform") continue;
ヒエラルキーウィンドウには表示されていない「InternalIdentityTransform」という名前のゲームオブジェクトも見つかるので除外しています。
今回は、検索したゲームオブジェクトのうちワールド位置がバウンディングボックスの中にあるものや、メッシュレンダラーなどのBoundsが指定のバウンディングボックスと交差するものをリストに追加してみます。
// バウンディングボックスの中にあるオブジェクト
if (bounds.bounds.Contains(go.transform.position))
{
// リストに追加
selectedObjs.Insert(0, go);
continue;
}
位置がバウンディングボックスに含まれているかはBounds.Containsメソッドを使って調べています。
含まれていなければ、レンダラーコンポーネントが取得できるかを調べ、バウンディングボックスがRenderer.boundsと交差している場合にリストに入れています。Bounds.Intersectsメソッドを使うと他のBoundsとの交差を判定できます。
// バウンディングボックスの外にあるとき
else
{
// レンダラーコンポーネントを取得
var r = go.GetComponent<Renderer>();
// レンダラーコンポーネントがあるとき
if (r != null)
{
// ボウンディングボックスが交差しているとき
if (bounds.bounds.Intersects(r.bounds))
{
// リストに追加
selectedObjs.Insert(0, go);
continue;
}
}
}
}
}
そして、リストが空でなければ、リストに入れたすべてのゲームオブジェクトを選択しています。
// リストにゲームオブジェクトがあるとき
if (selectedObjs.Count > 0)
{
// すべて選択
Selection.objects = selectedObjs.ToArray();
}
}
「クリア」ボタンも表示しています。ボタンを押すと、リストがクリアされます。
// クリアボタンを押す
else if (GUILayout.Button("クリア"))
{
// リストをクリア
selectedObjs.Clear();
}
また、リストが空でないときは、2つのボタンの下にリストの要素数だけボタンを表示し、各ボタンにゲームオブジェクトの名前を表示します。
// リストにゲームオブジェクトがあるとき
if (selectedObjs.Count > 0)
{
// リストの要素を縦に並べる
for (int i = 0; i < selectedObjs.Count; i++)
{
// ボタンを表示する。ボタン上にゲームオブジェクトの名前を表示。
if (GUILayout.Button(selectedObjs[i].name, EditorStyles.textField))
{
// ボタンをクリックすると、そのゲームオブジェクトを選択する。
Selection.activeGameObject = selectedObjs[i];
}
}
}
}
}
}
ボタンを押すと対応するゲームオブジェクトが選択されます。
バウンディングボックスでゲームオブジェクトを選択する
シーンに5つのCubeを追加しました。
プロジェクトウィンドウで前の記事のスクリプタブルオブジェクトを選択するとシーンビューにバウンディングボックスが表示されます。
ハンドルで移動やサイズを変更して、中央の3つが含まれるようにしました。
左側のCubeは完全に内側にあります。
右側はバウンディングボックスにかかっていますが、大半は外側にあります。
このスクリプタブルオブジェクトをエディタウィンドウのフィールドにドラッグアンドドロップします。
選択ボタンが表示されるので、押すと3つのCubeが選択されました。
バウンディングボックスの位置やサイズを変更してもう一度「選択」ボタンを押すと、選択されるゲームオブジェクトが変わります。
別のスクリプタブルオブジェクトをアタッチして使うこともできます。
これで、バウンディングボックスを使ってゲームオブジェクトを選択することができました。