【Unityエディタ拡張】SceneView拡張 それっぽいツールバー作成

記事を担当するのは2回目、プログラマの近藤です。
前回(1年ぐらい前)エディタ拡張の記事を書きましたが今回も同じUnityのエディタ拡張です。

今回はSceneViewに自作のツールバーを表示する方法を紹介します。

すっごい簡単なんですが、結構悩んだので記事にします。
多分一番このページを見るのは自分でしょう、間違いない。

エディタ拡張ってなに?って方はこちらの最初の方をどうぞ。

最後にソース全文載せています。

そもそもSceneViewって何ぞ?

Unityを起動した時に一番最初に目に入るウィンドウです。

Unity起動したことのある人で見たことない人はいないのでは?

表示切替や、Gizmo設定、Objectの検索機能まで、色々便利なSceneViewですが、
プロジェクトが進んでいくとあれも欲しいこれも欲しいとなってしまうもの。

そんな時こそ、エディタ拡張しましょう。そうしましょう。

今回作成したツールバー

こんな感じ。

本当にただツールバーが一本増えただけ。地味だよ。

とりあえずSceneViewに何か出してみる

ここから作成方法について紹介していきます。
まずは基本として、Sceneviewに簡単なボタンを表示する方法です。

using UnityEngine;
using UnityEditor;

[InitializeOnLoad]
public class SceneViewSample
{
	static SceneViewSample()
	{
		SceneView.onSceneGUIDelegate += OnGUI;
	}

	private static void OnGUI(SceneView sceneView)
	{
		Handles.BeginGUI();

		// ボタンのサイズ
		var ButtonWidth = 300.0f;
		// ボタン
		if (GUILayout.Button("ボタン", GUILayout.Width(ButtonWidth)))
		{
			// 押されたらダイアログを表示する
			EditorUtility.DisplayDialog("ボタン押した?","今ボタン押したよね?","押した");
		}
	
		Handles.EndGUI();
	}
}

簡単なコードでSceneView内にボタンが追加することができました。さすユニ。

ここで忘れそうなので覚えておくべきことが2点
SceneView.onSceneGUIDelegateにOnGUIを登録する。
OnGUI内はHandles.BeginGUI()で始めて、Handles.EndGUI()で終了。

それ以外は特に通常のGUI表示と変わりません。ボタンに限らず、ラベル等他のGUIにも対応してます。

変な隙間ができてる・・・

よく見なくてもなのですが、上に隙間が…。あと横もちょっとだけ空いている。

OnGUI内でGUILayout系を用いてをGUIを追加すると、このように隙間ができます。GUILayoutさんが他のGUIと競合しないように色々調整してくれた結果が
この位置っぽいのですが、どうにも見栄えが悪い。

元々存在しているツールバーの真下に表示したいので方法を考えます。

GUI.Boxを使用する

先ほどのボタン表示、GUILayout.Button()をGUI.Button()に変更します。
GUI.Buttonでは表示座標を設定することができるので左上に表示されるようにします。

// ボタン
// 左上の座標(0,0)に 表示するように
if (GUI.Button(new Rect(0, 0, ButtonWidth, ButtonHeight), "ピッタリぼたん"))
{
	// ボタンを押した時の処理
}

こうすることで左上に沿う形になりました。

まだ隙間がありますが、これは標準ボタンのスキンの外側が透明なためです。許して。

GUI.Buttonで表示位置を設定するときの注意点
・Rect型で指定する座標はウィンドウの描画領域に対しての相対座標である。左上が(0,0)
・GUIの中心は基本左上になっている。

GUILayout.BeginAreaを使用する

こっちのが絶対いいと思います。さっきのGUI.Buttonの紹介はなんだったのか・・・。

GUILayout.BeginAreaを使用することにより、
GUIを表示する領域(Rect)の設定することができるようになります。

つまり、ウィンドウの左上からGUIを表示する領域設定をすれば、GUIごとに座標設定を行う必要がなくなるというわけです。便利。

GUILayout.BeginArea(new Rect(0,0, // 左上
	Screen.width / EditorGUIUtility.pixelsPerPoint,   // ウィンドウの横最大
	Screen.height / EditorGUIUtility.pixelsPerPoint));  // ウィンドウの縦最大

GUILayout.Button("ピッタリぼたん",GUILayout.Width(ButtonWidth));

GUILayout.EndArea();

これでGUI.Buttonでの画像と同じ結果になります。

GUILayout.BeginAreaでウィンドウの左上から全体を描画領域と設定することで、
その後のGUILayout.Buttonでも意図しない位置に表示されないようになりました。
GUIの作成方法もこちらの方がスッキリしますのでオススメです。

注意!
GUILayout.BeginAreaしたら絶対GUILayout.EndAreaしましょう!自分は忘れた!

後は色々追加していって完成!

GUILayout.BeginAreaを使用すれば後は通常のGUI作成と変わりません。
どんどん好きなようにやってしまいましょう。

今回はSceneViewのツールバーに似た見た目のツールバーを作成します。
※Gizmoと検索窓は表示量が多すぎるので今回は無しです。

以下ソースコード全文

using UnityEngine;
using UnityEditor;

[InitializeOnLoad]
public class SceneViewSample
{
	static SceneViewSample()
	{
		SceneView.onSceneGUIDelegate += OnGUI;
	}
	private static void OnGUI(SceneView sceneView)
	{
		// ウィンドウ左上から描画するようにする
		GUILayout.BeginArea(new Rect(0,0, 
			Screen.width / EditorGUIUtility.pixelsPerPoint, 
			Screen.height / EditorGUIUtility.pixelsPerPoint));

		// ツールバーの見た目で横置きする
		GUILayout.BeginHorizontal("toolbar");
		{
			var modeContent = new GUIContent("Shaded");
			var in2DContent = new GUIContent("2D");
			var lightingContent = EditorGUIUtility.IconContent("SceneviewLighting");
			var audioContent = EditorGUIUtility.IconContent("SceneviewAudio");
			var fxContent = EditorGUIUtility.IconContent("SceneviewFx");

			var modeRect = GUILayoutUtility.GetRect(modeContent, EditorStyles.toolbarDropDown, GUILayout.Width(120));
			// モード設定表示
			if (EditorGUI.DropdownButton(modeRect, modeContent, FocusType.Passive, EditorStyles.toolbarDropDown))
			{
				   // ここでPopWindiow表示
			}

			EditorGUILayout.Space();

			// 2D表示トグル
			mIsIn2DMode = GUILayout.Toggle(mIsIn2DMode, in2DContent, EditorStyles.toolbarButton);

			EditorGUILayout.Space();

			// Lightingトグルボタン
			mIsLighting = GUILayout.Toggle(mIsLighting, lightingContent, EditorStyles.toolbarButton);

			// Audioトグルボタン
			mIsAudio = GUILayout.Toggle(mIsAudio, audioContent, EditorStyles.toolbarButton);

			// Fx表示トグルボタン&ドロップボタン
			var style = new GUIStyle("GV Gizmo DropDown");
			var fxRect = GUILayoutUtility.GetRect(EditorGUIUtility.IconContent("SceneviewFx"), style);
			var fxRightRect = new Rect(fxRect.xMax - style.border.right, fxRect.y, style.border.right, fxRect.height);

			if (EditorGUI.DropdownButton(fxRightRect, GUIContent.none, FocusType.Passive, GUIStyle.none))
			{
				// ここでPopWindiow表示
			}
			mIsAllOn = GUI.Toggle(fxRect, mIsAllOn, fxContent, style);

			GUILayout.FlexibleSpace();

			// Gizmoと検索窓は無し
		}
		GUILayout.EndHorizontal();
		GUILayout.EndArea();
	}
	static bool mIsIn2DMode = false;
	static bool mIsLighting = false;
	static bool mIsAudio = false;
	static bool mIsAllOn = false;
}

感想

いかがだったでしょうか。SceneViewのエディタ拡張。

SceneViewの機能拡張はデバッグ目的の拡張が多く、今回用意したツールバーもデバッグ目的で作成し始めたものでした。

本当はSceneViewのツールバー自体に項目を追加したかったのですが方法が良く分からず、
ツールバーをもう一本増やすという誤操作の温床のようなエディタ拡張になってしまいました。

でもSceneView内に独自のGUIを表示する勉強になったので個人的には満足です。

ただ、ツールバーが2本になるとシーンGizmo君邪魔ですね、移動させたい。

お付き合い頂きありがとうございました!