Programming Serendipity

気まぐれに大まかに生きるブログ

複数のButtonのOnClickイベントのパラメータに一括で`0, 1, 2,...`のように連番を設定する

ButtonのonClickのイベントの引数に0から順番にいっぺんに数字を入れたいことってありますよね。 これを自動化するスクリプトを紹介します。続きからどうぞ。

Editorフォルダ以下の適当なスクリプトに入れてください。
(追記:RegisterIntPersistentListenerだと、OnClickに要素が何もないときにエラーになるので、その場合AddIntPersistentListenerにした)

/// <summary>
/// ButtonのOnClickイベントに連番のパラメータを設定する
/// </summary>
/// <remarks>
/// この機能ではパラメータを連番に設定するかわりに、(スクリプト上の困難さから妥協点として)メソッドがダミーに切り替わる仕様になっている
/// なので、まずこの機能を使ってパラメータを設定してから、呼び出すメソッドを入れる必要がある
/// </remarks>
[MenuItem("Tools/Set Sequential Onclick Parameters")]
static void SetSequentialOnclickParameters()
{
    var selections = Selection.gameObjects.ToList();
    // Selectionで取得されるオブジェクト群は順不同なのでヒエラルキー順にソートする
    // 1つしかない子オブジェクトをピックアップしてる場合は親のSiblingIndexを対象にする
    bool isParent = false;
    if (selections.All(x => x.transform.GetSiblingIndex() == 0))
    {
        selections = selections.Select(x => x.transform.parent.gameObject).ToList();
        isParent = true;
    }
    selections.Sort((a, b) => a.transform.GetSiblingIndex().CompareTo(b.transform.GetSiblingIndex()));

    // 元のButtonに対象を戻す
    if (isParent)
    {
        selections = selections.Select(x => x.GetComponentInChildren<Button>().gameObject).ToList();
    }

    if (selections.Any(x => x.GetComponent<Button>() == null))
    {
        Debug.LogError("選択したオブジェクトはすべてButtonコンポーネントがアタッチされている必要があります。");
        return;
    }

    // 実行
    for (int i = 0; i < selections.Count; i++)
    {
        if (selections[i].GetComponent<Button>().onClick.GetPersistentEventCount() > 0)
        {
            UnityEditor.Events.UnityEventTools.RegisterIntPersistentListener(
                selections[i].GetComponent<Button>().onClick, 0, selections[i].transform.SetSiblingIndex, i);
        }
        else
        {
            UnityEditor.Events.UnityEventTools.AddIntPersistentListener(
                selections[i].GetComponent<Button>().onClick, selections[i].transform.SetSiblingIndex, i);
        }
    }
}

使い方は、

  1. パラメータを連番にしたいButtonオブジェクトをすべて選択
  2. Tools/Set Sequential Onclick Parametersを呼び出す(すると連番が設定される)
  3. そのあとに、呼ぶメソッドの設定をする

となっています。

…実はここにいたるまでに紆余曲折があり、リフレクションを使って内部APIを呼び出したり、PersistentCallやらMethodInfoやら、およそ通常のUnity開発では使わないようなクラスまで使って奮闘したのですが、パラメーターだけ変更する、というようなストレートな解決策は見つからず、 数時間にわたる調査の末、苦肉の策として、SetSiblingIndexを設定する仕組みにしています。

なのでメソッドを設定してからパラメータを変えるのではなく、このスクリプトでパラメータを設定してからメソッドを設定してください。パラメータは引き継がれます。 一応手間としては変わらないはず。

SetSiblingndexにしたのは、GameObjectTransform、つまり全てのオブジェクトからアクセスできるメソッドの中で、引数がintひとつで戻り値がvoidのものがこれだけだったためです。

う~んしかし苦しい。(OnClickに限らず)Editor上で設定したイベントのパラメータだけスクリプトから変更したいという需要は割と大きいと思うのですが、現状イベント丸ごとでしか扱えないようです。