Sliderの作成(Editor拡張)2020/08/31修正

Sliderの作成(Editor拡張)2020/08/31修正

 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をコントロールできるように変更しました。

GUIStyle.Draw

 GUIStyle.Drawを用いて画像を表示する場合、画像の大きさはGuiStyle.fixedWidth、GuiStyle.fixedHeight及びGUIStyle.overflowで決定されます。また、GuiStyle.fixedWidthもしくはGuiStyle.fixedHeightが0の場合は、GUIStyle.Drawへ渡すRectのwidthまたはheightによって大きさが決まります。

overflowで調整

 fixedWidthまたはfixedHeightで画像の表示される大きさが決定されます。このとき、overflowによって大きさを調整することができます。例えば、fixedHeightが19であり、表示したい画像の高さが5であるとき、overflow.top=-7及びoverflow.bottom=-7を指定とすれば中央に表示できます。

fixedWidthやfixedHeightとRectで調整

 fixedWidthやfixedHeightに値を設定し、overflowの各成分を0とするとfixedWidthやfixedHeightの大きさで画像が表示されます。画像の表示位置はGUIStyle.Drawへ渡すRectによって決定されます。先ほどと同様の画像を表示するとき、fixedHeight=5、overflow.top=0及びoverflow.bottom=0とすることで高さ5で画像を表示することができます。この場合、GUIStyle.Drawへ渡すRectのyへ7加算することで下方へ移動させる必要があります。

Rectのみで調整

 fixedWidthもしくはfixedHeightが0のとき、画像の大きさはGUIStyle.Drawへ渡すRectのwidthとheightによって決定されます。例えば、fixedHeight=0とした場合、GUIStyle.Drawへ渡すRectのyへ7加算し、さらにheight=5とするとこで、同様に画像を表示できます。

Script

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

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)を計算しています。また、つまみがマウスカーソルの先端に移動するように調整しています。

LabelやSliderへ割り当てるIDを取得し、FloatFieldの名前を作成します。また、focus_labelによってlabelの色を変更します。

keyboardControlがSliderのつまみのidとなったときLabelとFloatFieldのfocusされた時の色または画像へ変更しています。また、FloatFieldがFocusされた時、Labelの色が変更されるようにしています。

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

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

実行結果

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

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

修正履歴

2020/08/31

・スライダーのGUIStyleにおけるfixedHeightをコントロールの高さに合わせて調整(style_slider.fixedHeight = control_height;)

・スライダーのつまみ用画像の左右(1px)と下部(2px)に隙間があるため、つまみの位置を画像の隙間を考慮した計算方法へ変更

2020/08/09

・GUIStyle.Drawに関する説明文の変更

・ラベルをGUI.LabelからGUIStyle.Drawへ変更

・クリックによる動作部分(if (ev.button == 0・・・))とコントロール作成部分の順序を逆に変更