(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

エディタ拡張で配列の入れ替えが簡単に出来るReorderableListの使い方と全コールバック【Unity】【エディタ拡張】


このエントリーをはてなブックマークに追加



この記事でのバージョン
Unity 2017.2.0f3


はじめに

今回はエディタ拡張で要素の追加や入れ替えの出来るReorderableListのご紹介です!


f:id:kan_kikuchi:20171125111315g:plain


ReorderableList

エディタ拡張で要素の追加や入れ替えが簡単に出来るGUIを実装するときには

ReorderableListを使います。


第14章 ReorderbleList - エディター拡張入門


なお、ReorderableListを使うためにはusing UnityEditorInternalが必要なのですが、

残念ながらUnityEditorInternalはドキュメント化はされていません。


試しにReorderableListを使ってみると、以下のような感じ。

private ReorderableList _reorderableList;
private List<string> _stringList = new List<string>(){"0", "1", "2"}; //表示する要素

private void OnEnable (){
  //ReorderableListを作成
  _reorderableList = new ReorderableList(
    elements            : _stringList,    //要素
    elementType         : typeof(string), //要素の種類
    draggable           : true,           //ドラッグして要素を入れ替えられるか
    displayHeader       : true,           //ヘッダーを表示するか
    displayAddButton    : true,           //要素追加用の+ボタンを表示するか
    displayRemoveButton : true            //要素削除用の-ボタンを表示するか
  );
}

private void OnGUI(){
  //ReorderableListを表示
  _reorderableList.DoLayoutList();
}

f:id:kan_kikuchi:20171125111315g:plain


なんと、これだけで表示と並び替えが出来るようになります!(+はまだ機能しない)

もちろん、GUI上で入れ替えると_stringListの中身もちゃんと入れ替わっています。


コールバック

ReorderableListには色々なコールバックがあり、それを使って機能や見た目を改良していきます。

//追加、削除
_reorderableList.onAddCallback         += Add;         //+ボタンを押した時(onAddDropdownCallbackにコールバックを設定している場合は無効)
_reorderableList.onRemoveCallback      += Remove;      //-ボタンを押した時
_reorderableList.onAddDropdownCallback += AddDropdown; //+ボタンにメニューを追加

//判定
_reorderableList.onCanAddCallback    += CanAdd;    //+ボタンを押した時に要素を追加出来るか判定(コールバックを設定してない場合は常にtrue)
_reorderableList.onCanRemoveCallback += CanRemove; //-ボタンを押した時に要素を削除出来るか判定(コールバックを設定してない場合は常にtrue)

//動作後
_reorderableList.onReorderCallback += OnReorder; //要素を並び替えた時
_reorderableList.onChangedCallback += OnChanged; //要素に変化(要素の並び替え、追加、削除...)があった時
_reorderableList.onSelectCallback  += OnSelect;  //要素を選択した時
_reorderableList.onMouseUpCallback += OnMouseUp; //要素をクリックした時

//描画
_reorderableList.drawElementCallback           += DrawElement;           //要素の描画
_reorderableList.drawElementBackgroundCallback += DrawElementBackground; //背景の描画
_reorderableList.drawHeaderCallback            += DrawHeader;            //ヘッダーの描画(displayHeaderをfalseにしてても有効)
_reorderableList.drawFooterCallback            += DrawFooter;            //フッダーの描画(設定すると+ボタンや-ボタンが消える)
_reorderableList.elementHeightCallback         += GetElementHeight;      //要素の高さ

f:id:kan_kikuchi:20171126163754j:plain


なお、「コールバック?デリゲート?」という方は以下の記事をどうぞ!



では順に各コールバックについて説明していきます。

ただし、どのコールバックも設定は必須でないため、必要な時に必要なだけ設定しましょう。


onAddCallback (+ボタンを押した時)

onAddCallbackは+ボタンを押した時の処理を設定します。

なお、処理を追加してない状態で+ボタンを押すとエラーが出ます。

//+ボタンを押した時の処理を設定
_reorderableList.onAddCallback += Add;
//要素を追加する
private void Add(ReorderableList list){
  Debug.Log("要素を追加");
  _stringList.Add(_stringList.Count.ToString());//現在の要素の個数を文字列で追加する
}

f:id:kan_kikuchi:20171126154630g:plain


onRemoveCallback (-ボタンを押した時)

onRemoveCallbackは-ボタンを押した時の処理を設定します。

なお、処理を追加してない状態で-ボタンを押すと選択している要素が削除されます。

//-ボタンを押した時の処理を設定
_reorderableList.onRemoveCallback += Remove;
//要素を削除
private void Remove(ReorderableList list){
  Debug.Log("要素を削除");
  _stringList.RemoveAt(_stringList.Count - 1);//後ろの要素から削除
}

f:id:kan_kikuchi:20171126155001g:plain


onAddDropdownCallback (+ボタンにメニューを追加)

onAddDropdownCallbackは+ボタンにメニューを追加する処理を設定します。

なお、処理を追加すると、onAddCallbackに追加した処理は無効になります。

//+ボタンにメニューを追加する処理を設定
_reorderableList.onAddDropdownCallback += AddDropdown;
//+ボタンにメニューの追加
private void AddDropdown(Rect buttonRect, ReorderableList list){
  //追加するメニュー作成
  GenericMenu menu = new GenericMenu ();

  //チェックマークが付いたボタン追加(funcで押した時の処理を設定)
  menu.AddItem (new GUIContent ("Button1"),  on : true, func : () => {_stringList.Add(_stringList.Count.ToString());});

  //チェックマークが付いてないボタン追加(funcで押した時の処理を設定)
  menu.AddItem (new GUIContent ("Button2"), on : false, func : () => {_stringList.Add(_stringList.Count.ToString());});

  //区切り線追加
  menu.AddSeparator ("");

  //押せないボタン追加
  menu.AddDisabledItem (new GUIContent ("Button3"));

  //メニュー表示
  menu.DropDown(buttonRect);
}

f:id:kan_kikuchi:20171126155319g:plain


onCanAddCallback (要素を追加出来るか)

onCanAddCallbackは+ボタンを押した時に要素を追加出来るか判定する処理を設定します。

なお、設定してない場合は常にtrueが返されます。

//+ボタンを押した時に要素を追加出来るか判定する処理を設定
_reorderableList.onCanAddCallback += CanAdd;
//要素を追加出来るか
private bool CanAdd(ReorderableList list){
  return _stringList.Count < 5;//5個以下しか追加できないように
}

f:id:kan_kikuchi:20171126155700g:plain


onCanRemoveCallback (要素を削除出来るか)

onCanRemoveCallbackは-ボタンを押した時に要素を削除出来るか判定する処理を設定します。

なお、設定してない場合は常にtrueが返されます。

//-ボタンを押した時に要素を削除出来るか判定する処理を設定
_reorderableList.onCanRemoveCallback += CanRemove;
//要素を削除出来るか
private bool CanRemove(ReorderableList list){
  return _stringList.Count >= 3;//3個以上の時しか削除できないように
}

f:id:kan_kikuchi:20171126160033g:plain


onReorderCallback (要素を並び替えた時)

onReorderCallbackは要素を並び替えた直後の処理を設定します。

//要素を並び替えた直後の処理を設定
_reorderableList.onReorderCallback += OnReorder;
//要素を並び替えた
private void OnReorder(ReorderableList list){
  Debug.Log("並び替え!");
}

f:id:kan_kikuchi:20171126160212g:plain


OnChangedCallback (要素に変化があった時)

OnChangedCallbackは要素に変化(要素の並び替え、追加、削除...)があった時の処理を設定します。

//要素に変化(要素の並び替え、追加、削除...)があった時の処理を設定
_reorderableList.onChangedCallback += OnChanged;
//要素に変化(要素の並び替え、追加、削除...)があった
private void OnChanged(ReorderableList list){
  Debug.Log("配列が変化!");
}

f:id:kan_kikuchi:20171126160849g:plain


onSelectCallback (要素を選択した時)

onSelectCallbackは要素を選択した時の処理を設定します。

//要素を選択した時の処理を設定
_reorderableList.onSelectCallback += OnSelect;
//要素を選択した
private void OnSelect(ReorderableList list){
  Debug.Log("選択!");
}

f:id:kan_kikuchi:20171126161001g:plain


onMouseUpCallback (要素をクリックした時)

onMouseUpCallbackは要素をクリックした時の処理を設定します。

//要素をクリックした時の処理を設定
_reorderableList.onMouseUpCallback += OnMouseUp;
//要素をクリックした
private void OnMouseUp(ReorderableList list){
  Debug.Log("クリック!");
}

f:id:kan_kikuchi:20171126161509g:plain


drawElementCallback (要素の描画)

drawElementCallbackは要素の描画の設定します。

//要素の描画の設定
_reorderableList.drawElementCallback += DrawElement;
//要素の描画
private void DrawElement(Rect rect, int index, bool isActive, bool isFocused){
  //要素を書き換えられるようにフィールドを表示
  _stringList[index] = EditorGUI.TextField (rect, _stringList[index]);
}

f:id:kan_kikuchi:20171126161850j:plain


drawElementBackgroundCallback (背景の描画)

drawElementBackgroundCallbackは背景の描画の設定します。

//背景の描画の設定
_reorderableList.drawElementBackgroundCallback += DrawElementBackground;
//背景の描画(GUI.backgroundColorを変更すると+や-ボタンなど、余計な所の色も変わってしまう)
private void DrawElementBackground(Rect rect, int index, bool isActive, bool isFocused){
  //選択しているやつだけ色を変更する
  if (isFocused){
    Texture2D tex = new Texture2D(1, 1);
    tex.SetPixel(0, 0, new Color(1f, 0.5f, 0.5f, 0.5f));
    tex.Apply();
    GUI.DrawTexture(rect, tex as Texture);
  }
}

f:id:kan_kikuchi:20171126161909j:plain


drawHeaderCallback (ヘッダーの描画)

drawHeaderCallbackはヘッダーの描画の設定します。

なお、displayHeaderをfalseにしてても表示されます。

//ヘッダーの描画の設定
_reorderableList.drawHeaderCallback += DrawHeader;
//ヘッダーの描画
private void DrawHeader(Rect rect){
  EditorGUI.LabelField(rect, "ヘッダー!");
}

f:id:kan_kikuchi:20171126161923j:plain


drawFooterCallback (フッターの描画)

drawFooterCallbackはフッターの描画の設定します。

なお、設定すると+ボタンや-ボタンが消えます。

//フッターの描画の設定
_reorderableList.drawFooterCallback += DrawFooter;
//フッターの描画の描画
private void DrawFooter(Rect rect){
  EditorGUI.LabelField(rect, "フッター!");
}

f:id:kan_kikuchi:20171126161930j:plain


drawFooterCallback (要素の高さ)

drawFooterCallbackは要素の高さを取得する処理を設定します。

//要素の高さを取得する処理を設定
_reorderableList.drawFooterCallback += DrawFooter;
//要素の高さを取得
private float GetElementHeight(int index){
  return 50;
}

f:id:kan_kikuchi:20171126161937j:plain