はじめに
UnityのuGUI、すばらしい書籍も出たこの機会に、しっかりと勉強し直そうという方もいらっしゃるかもしれません。
ところでUnityActionって知っていますか?もしかしたら知らない方もいるかもしれませんね。ところで、ボタンのクリックやスライダーの移動などuGUIのイベントに対するコールバックの登録を、コードから行ったことはありませんか?あるという方、UnityActionというデリゲートは知らないけれど、実はUnityActionを使っていますよ!
この投稿ではuGUIのイベントコールバック登録をコードから行う方法とUnityActionデリゲート群の関係について紹介します。
uGUIのイベントコールバック登録をコードから行う
以下のコードは、クリックイベントコールバック登録をUnityエディタからでなくコードから行うサンプルです。書いた事がある人も多いかと思います。ボタンがクリックされたら、「Hello UnityAction」とログに表示されます。
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
public class Sample : MonoBehaviour
{
[SerializeField] Button button;
void Start ()
{
button.onClick.AddListener (ShowLog);
}
void ShowLog ()
{
Debug.Log ("Hello UnityAction");
}
}
コード的には本投稿のタイトルにもあるUnityAction
とは書いてありませんね。でも実はUnityAction
を使っています。
同じような例をもう一例。Sliderクラスを使って、Sliderの値の変更(つまみの移動)イベントを扱うコードです。
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
public class Sample : MonoBehaviour
{
[SerializeField] Slider slider;
void Start ()
{
slider.onValueChanged.AddListener (OnSlide);
}
void OnSlide (float value)
{
Debug.Log (value);
}
}
これも実はUnityAction<float>
という型を使っています。コードには書いてありませんが。
さて
button.onClick.AddListener (ShowLog);
slider.onValueChanged.AddListener (OnSlide);
という記述に注目してください。二つともAddListenerというメソッドの引数にメソッド名を書いています。これっていったいなんでしょうか?
button.onClick.AddListenerって何を引数にとるの?
前説のコード中にあった、button.onClick.AddListener
の引数に取っているものは何でしょうか?どのようなクラスなのでしょうか?
実はAddListnerメソッドの引数に取るのものが、本投稿のテーマでもあるUnityAction
なのです。同じように、slider.onValueChanged.AddListener
では、引数にUnityAction<float>
型のデリゲートを渡しています。
デリゲートはメソッドを参照するものです。デリゲートを返り値として返したり引数として渡す事ができます。先のコードではメソッド名を記述し、そのメソッドを参照するUnityAction
、UnityAction<float>
型のデリゲートを作って、AddListenerメソッドの引数として渡しているのです。
デリゲートって何?という方は、よかったらこちらの「【LINQの前に】ラムダ式?デリゲート?Func?な人へのまとめ【知ってほしい】」を読んでみてください。
UnityAction系について
前節で述べた通り、UnityActionやUnityAction<T>は、デリゲート型です。これらはUnityEngine.Events名前空間に定義されていて、この名前空間には他にも次のようなデリゲート型が定義されています。
UnityEventやUnityEvent<T0>などのUnityEvent系、そしてButtonClickedEventを初めとするUnityEvent系のサブクラス群というのがあります。それらは、AddListener・RemoveListenerというメソッドを持っていて、その引数としてUnityAction系のデリゲートが使われるのです。
普通のデリゲートなのでラムダ式を使って生成したり、インスタンスメソッドやクラスメソッドを参照してインスタンスを生成することができます。
UnityAction系を知らない方も、uGUIのイベントコールバック登録の際、実はUnityActionを意識せず使っているのです。
「ボタンクリックイベント発火時の処理をコードから設定」を一歩深く
クリックイベント発火時の処理をコードから設定するサンプルを再掲します。
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
public class Sample : MonoBehaviour
{
[SerializeField] Button button;
void Start ()
{
button.onClick.AddListener (ShowLog);
}
void ShowLog ()
{
Debug.Log ("Hello UnityAction");
}
}
コード的にはUnityActionがありませんが、UnitActionを使っているのです。一歩深く、上記のコードのStartメソッド内の処理をそれぞれのクラスやメソッドを確認することで見ていきましょう。
上記のコードはButtonクラスのonClickプロパティを使っています。このonClickプロパティはUnityEvent型を継承したButtonClickedEvent型のプロパティです。前節で述べた通り、UnityActionを引数にとるAddListenerメソッドを持っていて、このメソッドはUnityAction型を引数にとります。上記のコードではShowLogメソッドを参照するUnityActionデリゲート型のインスタンスを引数に渡しています。
ここでStartメソッド内を書き換えてみます。一番最初のコードと違い、一度UnityAction型の変数を宣言しインスタンスを生成し、それをAddListenerメソッドの引数として渡してみます。
void Start ()
{
UnityAction onClickAction = ShowLog;
button.onClick.AddListener (onClickAction);
}
一度、ShowLogメソッドを参照するUnityActionデリゲート型のonClickActionを生成しています。(これが分からないという方は、よかったら「【LINQの前に】ラムダ式?デリゲート?Func?な人へのまとめ【知ってほしい】」を読んでください。)
もちろんメソッドを定義せずとも、次のようにラムダ式を用いてデリゲートを生成する事ももちろん可能です。
void Start ()
{
UnityAction onClickAction = () => Debug.Log ("Hello UnityAction");
button.onClick.AddListener (onClickAction);
}
もちろん直接ラムダ式を渡す事も可能です。
void Start ()
{
button.onClick.AddListener (() => Debug.Log ("Hello UnityAction"));
}
このように実際にコードは書かなくても裏側にはUnityActionが隠れているのです。
「スライダのスライディングイベント発火時の処理をコードから設定」を一歩深く
次にUnityAction<T0>型の登場例を、Sliderクラスのスクロールイベント検知を例に紹介します。
Sliderクラスを使って、Sliderの値の変更(つまみが動いた)イベントを扱うコードを再掲します。(スライダrのスライディングイベント発火時の処理をコードから設定)
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
public class Sample : MonoBehaviour
{
[SerializeField] Slider slider;
void Start ()
{
slider.onValueChanged.AddListener (OnSlide);
}
void OnSlide (float value)
{
Debug.Log (value);
}
}
この例では、SliderクラスのonValueChangedプロパティを使っています。onValueChangedは、UnityEvent<float>型を継承したSliderEvent型のプロパティです。ここで使っているSliderEventクラスのAddListenerメソッドは、UnityAction<float>を引数にとります。
これも同じようにUnityAction<float>を使って書き換えることができます。(メリットはありませんが。)
void Start ()
{
UnityAction<float> onSlideAction = OnSlide;
slider.onValueChanged.AddListener (onSlideAction);
}
同様に、ラムダ式も使えます。
void Start ()
{
UnityAction<float> onSlideAction = value => Debug.Log (value);
slider.onValueChanged.AddListener (onSlideAction);
}
void Start ()
{
slider.onValueChanged.AddListener (value => Debug.Log (value));
}
まとめ
- uGUIのButton、SliderなどのコンポーネントはコードからもUIイベントのコールバック登録ができる
- コールバック登録は
button.onClick.AddListener
のようなプロパティ・メソッドを使う - UnityEvent系クラス群・そのサブクラス群のAddListenerメソッドは
UnityAction
やUnityAction<T>
デリゲートなどを引数にとる - メソッド名やラムダ式を記述する事でUnityAction型などのデリゲートを生成し、それによるコールバック登録ができる
uGUIのイベントコールバック登録をコードから行う際に、UnityAction系のデリゲート群が、裏で活躍している事を理解して頂けたでしょうか?
button.onClick.AddListener(メソッド名);
という記述の裏で何が起きたていたか疑問だった方の助けになれば嬉しいです。