チリペヂィア

リンクフリー。サンプルコードなどは関連記事内でライセンスについて明示されない限り商用利用なども自由に行って構いませんが、自己責任でお願いします。またこれら日記内容の著作権自体は放棄していません。引用部分については引用元の権利に従ってください。

PropertyDrawerのGUI配置前夜?

前回はコレ

前回サラッと流しましたが、PropertyDrawer.GetPropertyHeight()は

  • 非Layout処理をする時はきちんと高さ計算して返す
  • Layout処理する時はゼロを返す

ようにします。前者について、このケースでは面倒くさいけど私は次の設計パターンでやってます。

レンダリング関数をつくり、boolパラメータで実際に描画するかを指定する。レンダリング関数の戻り値はGUIを描くのに必要だった高さの累計。」

// 実際のレンダリング関数
// 戻り値:非Layout処理ではRect決定にCalcSize()を繰り返すことになるが、それによって表示に必要となった高さの合計を返す。
// isDrawをfalseに設定する時は、パラメータの表示や更新は行わない。ただし戻り値の計算は行う。
float DrawGUI(Rect position, SerializedProperty prop, GUIContent label, bool isDraw)
{
    float height: // CalcSize()の都度サイズを加算していく。
    // <略>
    return height;
}

この関数をGetPropertyHeight()とOnGUI()でboolパラメータを切り替えて呼び出します。

なお後者についてですが、オーバーライドしないでデフォルトの値(とりあえず1行分?)を返すままにしとくと、無駄に1行スペースが開いてしまいます。

インデントとラベル幅制御のアレコレ

前置きが長くなりましたが今回のトピック。インスペクタにGUI部品を配置構成する上で重要なのは次の2つ。

  • EditorGUI.indentLevel
  • EditorGUIUtility.LookLikeControls

さらにRectを生計算する場合次を使用。

  • EditorGUI.IndentedRect
EditorGUI.indentLevelについて

インデントレベルはグローバル変数的に存在してます。想像力重要。つまり「全てのGUI部品描画は強制的に右にオフセットするよ」って事ですね。例えBeginHorizontal()して1行で横に並べようとも、横に並ぶGUI部品の左には全てタブスペースが生じます。で、まず見切れます。なので、横にGUIを並べる場合は以下のフローが基本です。

  1. EditorGUI.indentLevelをバックアップ
  2. EditorGUI.indentLevelをゼロに設定
  3. 横並びに描画
  4. バックアップからEditorGUI.indentLevelを復元
EditorGUIUtility.LookLikeControlsについて

ラベル幅設定ってゲーム画面用のGUI設計に全く関係ないので、EditorGUIUtilityにコッソリあります。デフォルトのラベル幅設定はやたら長く、制限しないとやはり見切れます。EditorStylesからGUIStyleを取得してCalcSizeすればラベルに必要な幅が計算できます。ちなみにEditorGUIUtility.LookLikeInspectorもありますがこちらはラベル幅設定がないので実質出番ナシ…。

EditorGUI.IndentedRectについて

インデントサイズはコレ以外取得するインターフェースが公開されてません。この関数もまた気の利かない仕様。ILSpyするとわかりますが本当にただ単に引数のpositionに条件反射的に下駄を履かせるだけの計算です。中身は下記。

// UnityEditor.EditorGUI
internal static float indent
{
	get
	{
		if (EditorGUIUtility.lookLikeInspector)
		{
			return (float)EditorGUI.indentLevel * 15f;
		}
		return (EditorGUI.indentLevel > 0) ? (9f + (float)(EditorGUI.indentLevel - 1) * 15f) : 0f;
	}
}

// UnityEditor.EditorGUI
public static Rect IndentedRect(Rect source)
{
	float indent = EditorGUI.indent;
	return new Rect(source.x + indent, source.y, source.width - indent, source.height);
}

インデント量はEditorGUI.indentLevelがそのまま使用されてます。だからEditorGUI.IndentedRectを使う時はEditorGUI.indentLevelのゼロリセットより手前である必要があります(ぶっちゃけこんな仕様ならindentLevelはパラメータ渡しの方が分かりやすいと思うの)。それと、IndentedRectしたRectを元に座標計算して配置する時は、そのRectは既にインデントが適用されてるんだから、描画を開始する前にはEditorGUI.indentLevelをゼロにセットしとかないとおかしくなるのにも注意。