Sliderの作成(Editor拡張)

Sliderの作成(Editor拡張)

 GUIStyleを自由に変更できるようSliderを自作しました。

Label、Slider、FloatFieldを横並びで表示

 EditorGUILayout.Sliderによって作成されるコントロール(Label、Slider、FloatField)を別々に作成し、これらを横へ並べることでSliderを作成ます。これにより、各要素へ自由にGUIStyleを設定することができるようになります。

Script

 label、Slider及びFloatFieldを作成すると縦並びとなります。よって、EditorGUILayout.BeginHorizontal();とEditorGUILayout.EndHorizontal();の間にlabel、Slider及びFloatFieldを作成し、各コントロールを横並びにします。これにより、各コントロールを分けて記述できるため、コントロールごとにGUIStyleを自由に変更することができます。作成したScriptは以下の通りです。

各コントロール用の名前を作成します。GUIUtility.GetControlID(FocusType.Passive)によって取得したidを追加することで、同じ名前にならないよう処理をしています。

GUI.GetNameOfFocusedControl()によってFocusされているコントロールの名前を取得し、それに応じてGUIStyleの変更を行っています。

下記コードで各コントロールの横幅を求めています。FloatFieldの横幅を65へ固定し、残りをlabelとSliderで二等分しています。

先ほど求めたlabelの横幅(label_width)を用いてlabelの作成を行います。

先ほど求めたSliderの横幅(slider_width)を用いてSliderの作成を行います。labelはEditorGUILayoutで作成していますが、Sliderの場合はEditorGUILayoutではGUIStyleを設定することができません。そのため、GUILayoutを用いてSliderを作成しています。また、Sliderによって変更される数値(val)を小数点第三位までとなるように処理をしています。

EditorGUILayoutを用いてFloatFieldの作成を行います。

ev.mousePositionによりマウスの位置を受け取っています。また、Rect.Containsを用いて、labelもしくはSliderが右クリックされたかを判定しています。labelがクリックされた場合は、idをGUIUtility.hotControlを渡し、SliderをGUI.FocusControlによってFocusします。Sliderがクリックされた場合はSliderをFocusします。そして、マウスドラッグが行われ、かつGUIUtility.hotControlがidであった場合はマウスのx方向の移動量(ev.delta.x)によって数値(val)を変更しています。

実行結果

 label、Slider及びFloatFieldを分離したSliderを実行するScriptは以下の通りです。

ラベルをドラッグすると、数値の変更とそれに伴うSliderのつまみが移動しています。また、FloatFieldの数値を変更するとSliderの移動ができています。しかし、SliderをクリックしてもSliderへFocusが移っていないことが分かります。

そこで、Event.typeを調べるとSliderをクリック及びドラッグした際にEventType.Usedが発生していました。これにより、Sliderをクリックした際にマウスイベントが取得でないため、Focusを変更することができないようです。

GUIStyle.Drawを用いて作成

 先程の問題を解決するため、GUIStyle.Drawを用いてSliderを作成することでFocusをコントロールできるように変更しました。

Script

作成したScriptは以下の通りです。

GUIStyle.Drawを使用する場合、コントロールの高さに応じてGUIStyle.overflowを調整する必要があります。以下の図に示すようにoverflowはコントロールより小さくする場合はマイナス、大きくする場合はプラスを指定します。コントロールの高さが19、高さ5の画像であるとき、overflow.top=-7及びoverflow.bottom=-7を指定とすれば中央に表示できます。よって、overflowは以下のコードで求めることでSliderの画像を中央に表示することができます。ただし、高さ18及び画像の高さ5のように、top=bottomとならない場合はtopの幅が小さくなるよう処理をしています(overflow.top=-6、overflow.bottom=-7)。

Sliderのつまみも同様にコントロールの高さに応じてoverflowを調整する必要があります。overflow.top及びoverflow.leftで表示位置を、overflow.bottomとoverfloaw.rightで大きさを調整できます。

GUIStyle.Drawを使用してコントロールを作成する場合は、作成するための領域(Rect)を指定する必要があります。そのため、GUILayoutUtility.GetRectでSliderを作成する領域を取得します。GUIStyleを指定した場合、GUIStyleに指定されているmarginやfixedHeight等を元に計算されたRectを取得できます。

marginと各コントロールの横幅とSliderの高さを決定します。margin_leftはSlider全体の左側、marginは各コントロール間のmarginです。また、FloatFieldの大きさは65へ固定し、残りの幅をLabelとSliderで等分します。

先ほど求めた幅を元に各コントロールのRectを計算します。rect_slider_drawはSliderを表示するためのRectで、rect_sliderはSliderがマウスクリックされたかを判定するために使用します。

remap_valはleft_val~right_valを0~1へ変更した場合の数値(val)を計算しています。Sliderのつまみが移動する範囲はSliderの大きさよりつまみの幅だけ縮めます(rect_slide_area)。これらの数値を利用してSliderの左端~右端の座標を線形補間することでSliderのつまみの位置(slider_pos)を計算しています。また、つまみがマウスカーソルの先端に移動するように調整しています。

Sliderやつまみへ割り当てるIDを取得し、labelとFloatFieldの名前を作成します。

SliderもしくはFloatFieldへFocusが移った場合、Label及びFloatFieldのGUIStyleを変更します。

求めたRect等を用いてLabel、Slider及びFloatFieldを作成します。GUIStyle.DrawはRepaint時のみ実行されるため、if文によりRepaint時のみGUIStyle.Drawが実行されるよう処理をしています。

右クリックもしくはドラッグされた場合におけるSliderとFloatFieldの動作を記述しています。Sliderがクリックもしくはドラッグされた場合は、マウスカーソルの位置に応じて数値を変更します。また、FloatFieldがドラッグされた場合、ドラッグされた距離に応じて数値を変更します。

実行結果

 GUIStyle.Drawを用いて作成したSliderを実行するScriptは以下の通りです。

前述のScriptではSliderをクリックした際、Focusを移すことができませんでした。GUIStyle.Drawを用いて作成したSliderではFocusを移すことができていることが分かります。また、他の機能も問題なく動作していることが分かります。