メインスレッド以外からUnityのUIを操作しようとして途方に暮れた件
いやもう本当に、そもそもメインスレッド以外で処理が走っているってことを想像できなかったし、それを指摘されたあとも本当に苦労した。いろいろと書くべきテーマがてんこ盛りの数日間だったのだが、まずは複数スレッドでの命令の受け渡しから。
前提
ある関数からUnity上のTextの文字表示を変更しようとするとエラー。該当箇所をコメントアウトするとエラーが消える。またその関数をStart関数から直接叩けば正しく文字が更新される。
作業内容の概要
呼び出し元の関数がメインスレッド以外のスレッドで実行されているため、そこからUI操作を呼び出すとUI操作もメインスレッド以外のスレッドで実行されようとしてエラーとなる。なので、「メインスレッドでこの処理を実行しろ」という命令を外部スレッドから実行する。
対応
こちらの記事に本当に助けていただいた。本当に本当に感謝申し上げる。
まず、何をどう調べていいのかすら分からず、最初に参考資料として頂いたのは以下の記事だった。
で、ここに出ている「Invoke」関数はUnity内には見当たらない、ということにたどり着くのに数時間。で、その後代替案を延々探し続けた。
どうやらMonoBehaviorの操作はメインスレッド以外はエラーになるらしい、と分かって、「Unity スレッド UI 操作」で検索したらやっといくつか事例が引っかかった。それまではUnity上での「Invoke」関数で、一定時間後に関数を実行する方法、というのばかりが引っかかったのだ。
最初の記事を参考に、スレッド番号を確認して、まずはメインスレッド以外から命令が出されていることを確認。記事の例はメインスレッド内で「Current」を取得しているので、自分のプログラム内で別スレッドに移行する前にメインスレッドを変数に保存する形で対応した。
using System.Threading;
~
SynchronizationContext MainThread;
MainThread = SynchronizationContext.Current;
これでメインスレッドを参照可能にした上で
MainThread.Post(__ =>
{
hoge();
}, null);
と関数を実行するとエラーなく実行された。
いやあ、本当に救われました。ありがとうございます。
補足
自分がやった処理は本当にやっつけで、本来はUnityの処理自体を考慮して行うべきだろう。
以下に備忘録として参考資料を2点上げておく。UniTaskとUniRxを使用するやり方に関しての記事だ。