1. Qiita
  2. Items
  3. Unity

Unityでテスト用にインスペクタ上にメソッドを実行してくれるボタンを作ろう

  • 6
    Like
  • 0
    Comment

Unity2のアドベントカレンダー9日目です。

Unityでインスペクタ上にメソッドを実行してくれるボタンを作ろう

最近Rxとか流行ってて
っていうかMVVMやらMVPやらよく使いますよね。

例えばMVPパターンを例に上げると

V(View)いわゆるUI部分アクションを起こすと
P(Presenter)に変更されたという情報が伝播して
M(Model)部分でなんやかんや計算して
P(Presenter)に計算の結果の情報が伝播して
V(View)に情報が反映される

という手順なわけですが・・・

大体として開発するときにMのロジックが簡単でVは手間がかかる、なわけです。
特に分業してたりするとロジックのほうが先に出来てテストのために仮UIつくってUIできたらそれにVをくっつけて・・・あーめんどくさい

テスト書く前に色々試してから・・・
入力UIはできてないけど表示UIはできてるからチェックしたいの・・・?

ってわけでMだけでテストてきたら簡単ですよね。
そもそもMとVは独立してるはず、

なのでModelに簡易的にテストできるボタンがあればみんな幸せかな?

ついでにこの手法は実機確認用のテストメニューとか作るときも使えるので覚えておいて損はないです。

サンプルコード

ここからダウンロードしてね
あとは好きに使ってね
改造の余地は色々あると思います

https://github.com/Marimoiro/unity-executorbutton

Modelを継承したクラスのメソッドにExecutorButton属性を付けたメソッドを作ると実行時にインスペクタにボタンが表示されます。
ボタン押せばメソッドが走ります。
簡易的なものなので戻り値は握りつぶし、引数はなしのみと限定しています。

説明

あんまりやってることは難しくないです。
肝になるのはModelEditorの部分ですね。

ModelEditor


namespace Assets.ExecutorButton.Editor {

    [CustomEditor(typeof (Model),true)]
    public class ModelEditor : UnityEditor.Editor
    {
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            if( !EditorApplication.isPlaying ) return;


            Type type = target.GetType();

            var methodInfos = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                .Where( m => Attribute.GetCustomAttribute(m,typeof(ExecutorButtonAttribute)) != null) // ExecutorButtonAttributeを持つものだけ抜き出し
                .Where( m => !m.GetParameters().Any()); //パラメータなしのみ

            foreach (var mi in methodInfos)
            {
                if (GUILayout.Button(mi.Name))
                {
                    mi.Invoke(target, new object[0]);
                }
            }
        }
    }
}

methodInfosでExecutorButtonAttribute属性を持っているメソッドだけを抜き出して、それを一つづつボタンで表示しています。
でボタンが押されたらメソッドを実行しているだけ、簡単でしょ?

ちなみにリストに要素があるかどうかを調べるのはAnyを使います。

アイデアスケッチ

属性つけるのめんどくせえ!

属性じゃなくて例えばメソッドの名前に~Executeとか~CommandとかOn~とかあったらとかにするか
BindingFlags.DeclaredOnlyでみつかったもの全部引っ張ってくるとか
そのへんは実装者が楽な方法取ればいいと思います

引数つけたい!

foreachの部分に引数入力UI入れてみたらどうでしょうか。

デバッグメニュー

おんなじようにリフレクション使えば自動でデバッグメニュー作ることもできます。
SRDebuggerというAssetがあるから試してみてもいいかもしれません。
(個人的にはあのOptionタブはもう少し改良できると思ってる)

おわりに

こういうユーザーは使わないけど開発者が楽になるものを考えるのって楽しいですよね。
あと開発者の無駄なルーチンワークへらせるから精神的に楽になるしみんな繰り返しとか嫌いなタイプでしょ?