Unityな日々(Unity Geek)

Unityで可視化アプリを開発するための試行錯誤の覚書

Event使用時のエラー"... can only be called from the main thread"

次のようなエラーがでた。

f:id:yasuda0404:20150826180634p:plain

状況は次のとおり。

オリジナルの'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 - テラシュールブログ