次のようなエラーがでた。
状況は次のとおり。
オリジナルの'subClass'のスクリプトで、別クラスであるmainClassのイベントReadに、('subClass'のスクリプト内の)Actionメソッドを登録していた。イベントが発行されるとsubClassのAction()メソッドが呼ばれ、そこからさらにMove()メソッドが呼ばれる。Move()は、他クラスのアニメーションの再生(Playメソッドをコール)するものだ。
//subClass void Start () { mainClass.Read += Action; //Register Custom Function. } public void Action(string id) //このメソッドがmainClassのイベントを通じて実行される { Move(id); }
ちなみにイベント発行側であるmainClassのデリゲーションは次のように記述。
public class mainClass : MonoBehaviour { //Delegate public delegate void MyDelegate(string id); public event MyDelegate Read;
上を実行したところ、subClassは、mainClassが発行するイベントを受け取り、Action()メソッドも実行されるのだが、Move()内で実際にアニメーションを実行するところでエラーが出ているようだ。
同じsubClass内のUpdateループからActionメソッドを実行する場合は、何も問題がなくアニメーションが実行されることがわかった。すなわち、同じアニメーションを実行するのに、Updateからだと問題なく、イベント・トリガーだとエラーになる。
void Update() { if(Input.GetKeyDown("a")) { //Executed when "a" pressed down Action("key a"); } }
このエラーの原因は、非同期処理のイベントを通じて、UnityのアニメーションAPIを呼び出そうとしたことにある。アニメーションAPIに限らずUnityのAPIは、メインスレッド(Awake、Start、Update、FixedUpdateなど)から呼び出さねばならないようだ。なるほど、Unityは基本的にシングルスレッドなのだ。別スレッドからUnity APIは使えない。
この対策として、イベントハンドラAction()では、イベント発行先から送られた情報(この場合、string id)を自クラスの変数に格納するのみにして、update()ループ内でその値を評価してアニメーションメソッド(Move())を実行するようにした。これで解決。
public void Action(string id) { Debug.Log("ControllerDirect::Action id="+id); currentID = id; } void Update() { if(Input.GetKeyDown("a")) { //テスト用 Key入力は問題なく動作する Action("key a"); } Move(); } //Custom Function private void Move() { if(latestID != currentID) { latestID = id; animationNo = (animationNo+1)%5; Debug.Log("animationNo "+animationNo); Debug.Log("Controller::Action id="+id); // switch(animationNo) { case 0: move.ActionStop(); break; <以下略>
参考:Unityでスレッドを使いつつUnityAPIを使う Spicy Pixel Concurrency Kit - テラシュールブログ