前回の記事でUnityEventを使って別オブジェクトのスクリプトを実行する方法を紹介しました。
ボタンUIなんかにあるクリック時にどのオブジェクトの何のメソッドを実行するか設定するアレの自前版。
ただそれには問題があって、それは実行したいメソッドに引数がある場合、インスペクターから静的(static)な値しか渡せないということ。
その問題を今回は解決し、スクリプトから動的(Dynamic)にUnityEventに引数を渡せるようにします。
UnityEventで動的な引数を渡す方法
以下のようにUnityEventを継承したクラスを定義し、その定義したクラスの変数でInvokeしてあげればOKです。
[Serializable] private class MyEvent : UnityEvent<string> { }
[SerializeField] private MyEvent myEvent = null;
この時、UnityEventを継承したクラスには[Serializable]を付加します。そうしないとインスペクターに表示されません。
スクリプトの準備
上記を踏まえてスクリプトを用意します。
実行のトリガーとなる「TestButton2.cs」と引数を受け取るメソッドが書かれている「TestScript2」の2つを用意します。
スクリプトのコード
TestButton2.cs
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class TestButton2 : MonoBehaviour { [Serializable] private class MyEvent : UnityEvent<string> { } [SerializeField] private MyEvent myEvent = null; public void Click() { string myname = transform.name; myEvent.Invoke(myname); } }
TestScript2.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class TestScript2 : MonoBehaviour { public void CallBack4(string str) { GetComponent<Text>().text = str; } }
スクリプトの解説
TestButton2.cs
using System;
using UnityEngine.Events;
Serializableを使用するために「System」を、UnityEventを使うために「UnityEngine.Events」をそれぞれインポート記述しています。
[Serializable] private class MyEvent : UnityEvent <string>{ }
[SerializeField] private MyEvent myEvent = null;
[Serializable]でUnityEventを継承したクラス(MyEventという独自クラス)を宣言し、[SerializeField]でそのMyEventクラスの変数を宣言します。こうすることでインスペクターに表示されます。
string myname = transform.name;
myEvent.Invoke(myname);
Invokeに値を入れることで登録したメソッドに引数として渡すことができます。
なお、この引数の型はSerializableで宣言したクラスに指定した型が適用されます。
今回ですと、「UnityEvent<string> { }」としているため、渡せる引数の型はstring型となります。
TestScript2.cs
public void CallBack4(string str) {
GetComponent<Text>().text = str;
}
Invokeされた際に呼ばれるメソッドです。このメソッドに”Invoke(値)”で指定した値が引数として渡されます。
引数として受け取れる型はInvokeする際に渡される型と合わせておく必要があります。
今回の場合は、「UnityEvent <string>{ }」としているため、string型が引数として受け取ることができます。
そして受け取った引数をそのままテキストとして画面に表示します。
オブジェクトの準備
動作確認用にボタンUIを2つ、テキストUIを一つヒエラルキーウィンドウに追加しました。 ボタンUIは区別が出来るように名前と表示テキストをButton1、Button2にしています。
作成したButton1、Button2にそれぞれ「TestButton2.cs」をアタッチします、
Textには「TestScript2.cs」をアタッチします。
ボタンのインスペクターには「MyEvent(String)」と、String型の引数アリで表示されています。
MyEventにテスト用メソッドをセット
アタッチしたTestButton2のMyEvent(String)にTextオブジェクトを追加し、TestScriptのメソッドを指定します。
このとき注意する点として、必ず「Dynamic~」側のメソッドを指定します。
「Static Parameters」(下側)にも同じメソッド名が表示されますが、こちらを指定すると静的(Static)な値として、前回同様にインスペクターからしか引数を設定できません。
そして今回は、ボタンをクリックした際にテキストオブジェクト引数を渡したいので、ボタンUIのクリックイベントに自分自身の「Click()」メソッドを指定しておきます。
これを、Butto1、Button2のどちらにも行います。
スポンサードサーチ
動作確認
再生ボタンでゲームを実行します。ボタンを押すと、押したボタンの名前がテキストに表示されます。
「TestScript2」が、それぞれのボタンのオブジェクト名を引数として受け取って画面に表示しているのが確認できます。
スポンサードサーチ
動的な引数を渡すときの注意点
Unityのリファレンスを見ると、UnityEventは以下のように定義されています。
- UnityEvent
- UnityEvent
- UnityEvent<T0,T1>
- UnityEvent<T0,T1,T2>
- UnityEvent<T0,T1,T2,T3>
これがどういうことかといいますと、引数ナシもしくは、1~4つの引数を使える、ということです。
つまり、引数は最大で4つまでとなります。
それ以上引数を使いたい場合は下記の独自クラスを使う方法がいいでしょう。
引数に独自クラスを使う
引数にクラスを使うこともできます。コードの書き方は大きく変わりません。string型だったところがクラス名に変わるだけです。
スクリプトのコード
TestButton2.cs
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; public class TestButton2 : MonoBehaviour { [Serializable] private class MyEvent : UnityEvent<MyClass> { } [SerializeField] private MyEvent myEvent = null; public void Click() { MyClass myClass = new MyClass(transform.name); myEvent.Invoke(myClass); } public class MyClass{ public string myNmae; public MyClass(string str) { myNmae = str; } } }
TestScript2.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class TestScript2 : MonoBehaviour { public void CallBack4(TestButton2.MyClass myClass) { GetComponent<Text>().text = myClass.myNmae; } }
スクリプトの解説
string型の個所がMyClassになっているだけです。
MyClassは内部クラスとして書いていますが、別スクリプトのクラスにしても大丈夫です。
こうすることで、ボタンをクリックした際にクラスをテキストUIに渡すことができます。
スポンサードサーチ
まとめ
クラスを引数として渡すところまで出来ればかなり実用的ではないかと思います。
ただし、この方法にも問題がありまして、それは今回のサンプルケースでいうところのボタンUIをプレハブ化したい場合です。
ボタンUIのみプレハブにすると、MyEventに割り当てているテキストUIオブジェクトが外れてしまいます。
理由としましては、テキストUIがプレハブ化されていないからです。
解決のアプローチとしてボタンUIをプレハブからインスタンス化した際に、テキストUIのスクリプトのメソッドを予めボタン側で確保しておき、必要に応じて確保しておいたメソッドを実行するようにします。
その方法は別記事でご紹介します。