メインスレッド以外から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を使用するやり方に関しての記事だ。
Unity Test Runner を使おうとして詰まってしまった3点
新しい仕事においてややコーディングよりの仕事をすることになったので、テスト駆動開発の方法くらいは知っておかないと、と思って泥縄で調べ始めたのだが、いやあ、敷居が高かった。
最初に検索して上位に来たいくつかの記事を読ませていただいた。例えば以下。
よくまとまっていて素晴らしい記事なのだが、テスト駆動開発の経験がない人間にはかなり難易度が高い記事であったことも事実だ。
ここで素人が疑問に思った点が以下の3点。
- PlayModeとEditModeはどう使い分ければいいのか
- テスト対象のアセンブリを設定する、とはどういうことなのか
- テストコードはどこにどうやって書くのか。
特に最後の疑問はなかなか情報が出てこなかった。おそらくみんなそんなことは大前提として知っているのだろうけど、コードの中身が記載されるばかりで、このコードはどのファイルに記載されているのか、という明示がないのだ。
これらの疑問に関して、以下の記事に本当に助けて頂いた。感謝申し上げる。
とりあえずは備忘録として。近々実際にテストを書いてみた際のことも記事にしたいとは思っているのだが、いつになるやら。
Houdiniでファイル書き出し時に「TypeError: 'newline' is an invalid keyword argument for this function houdini」と出た際の対処
前提
HoudiniからUnityで使用する「PointCashe」のファイルを書き出そうとした所、「TypeError: 'newline' is an invalid keyword argument for this function houdini」というエラーが出て書き出しができなくなった。
対応
Pythonのバージョンが3未満だとこのエラーが出る。Houdini自体をバージョンアップするか、使用しているPythonをバージョンアップする。
Pythonのバージョンアップに関しては以下のリンクを参考にさせていただいた。感謝申し上げる。
情報がなかなか見つからずに少々焦ったので、備忘録として。
Houdiniで作成したFlowMapの書き出しとUnityでの使用 その2
前提
前回の記事でHoudiniで作成したFlowmapを、Unityで表示するためのシェーダーを作成する。
ビルトインのシェーダーもあるようなのだが、大まかな構造だけでも把握するために、処理の流れを追ってみることとする。
作業内容の概要
Flowmap上の色情報を元に、メインのテクスチャ画像のUV画像を移動させることによって画像内に動きをもたらす。
そのままやるとループしないので、同一周期で開始値が違う2つの画像を用意して、その画像の色情報をLerpで混ぜることでループを実現する。
参考資料
Flow Map Shader for Unity3D. Used with Sprites. · GitHub
コードで記述されたシェーダー。これを元に以下の動画内でShaderGraphでの制作を行っている。
コードで記述されたシェーダーの、ShaderGraphへの移植。
Flowmapを表示するシェーダーの仕組みが分かりやすく解説されている。英語のみだが平易なので、画面を見ていれば内容は理解できると思う。
上記のリンクに非常に助けられた。この場を借りて感謝申し上げる。
注意事項
UV移動を、同一周期で開始値だけをずらした2つの値を用意して、その2種類のUVを適用した同一テクスチャ2枚をLerpで合成する、という流れ。その際にLerpの第2引数の値をループさせる必要があるが、それを以下のコードで実装している。
float phase0 = frac(_Time[1] * 0.5f + 0.5f);
<中略>
float flowLerp = abs((0.5f - phase0) / 0.5f);
これが全く思いつかなかった。参考にさせていただく。
Houdiniで作成したFlowMapの書き出しとUnityでの使用 その1
前提
HoudiniでFlowMapを作成し、必要なデータを書き出してUnityに読み込んで表示する。
作業内容の概要
- HoudiniでFlowMapを適用するためのジオメトリを作成。
- HoudiniでFlowMapを作成。
- FlowMapの画像を書き出し。
- ジオメトリをFBXで書き出し。
- 書き出したFBXをUnityにインポート。
- UnityでFlowMapを表示するためのシェーダーを作成し、読み込んだFBXに適用。
大体上記のような流れになる。
参考資料
以下の動画を参考にさせて頂いた。この場を借りて感謝申し上げる。
便宜上、前者の動画を「Youtubeの動画」、後者の動画を「SideFXの動画」と呼ばせていただく。
SideFXの動画の方が説明がゆっくりで分かりやすいが、Houdiniのバージョンが16.5とやや古く、その分Youtubeの動画の方に分がある。Youtubeの動画の方も、いちおう主要なノードはパラメータを表示してくれているので、一時停止しながらゆっくり追えば理解はそう難しくはない。ただ一部の主要ノードのパラメータが表示されない部分はあるので、そこは頑張って埋めるしかないだろう。
FlowMapを適用するためのジオメトリの作成。
正直、面倒くさければGridノード一個で済ませてしまって構わない。上記に挙げた動画は2つとも、地形を作成してそこを流れる河の水面のジオメトリを作成し、そこにFlowmapを設定しようとしている。このプロセスもまた非常に面白いのだが、FlowMapには直接関係がないので詳細は割愛する。興味のある方は、Youtubeの動画の方の冒頭部分を是非ご覧頂きたい。
基本となる地形の生成は上記の構成。HeightField系のノードを使用して凹凸のある平面を作成している。
河として使用する平面の作成は上記の構成。Addノードによるポイントの追加は後々での修正対応を考慮したやり方だそうで、Curveノードで代用しても問題ない。生成済みの基本地形の凹みの部分に合わせて曲線を設定してResampleしてポイント数を増やす。その後、その曲線と元の基本地形をRayノードで対応させた後に、Sweepノードで平面化している。
ここで大切なのは、曲線を設定した状態でUVTextureを設定すること。上手のように、V軸方向に一本伸びるように設定する。
その状態でSweepノードを使用して平面を作成すると、上図のようにUV平面がV軸方向に伸びるようになる。このUV平面を、縦横ともに1に収めるように設定を行う。
Sweepのもととなるラインを設定した際に、ポイントNoに対応した変数を設定する。ここでは「PT_num」という名前で生成した。
ポイント数の合計はResampleノードの「segs」パラメータから取得できる。「Input Max」の欄に「ch("../River_points/segs")」と入力して、変数「PT_num」をAttributeRemapノードで0~1の間に収めるように設定する。
AttributePromoteノードで、Vertexに設定されているuvをPointに移行した上で、AttributeWrangleノードで「@uv.y = @PT_num;」と入力する。これでUVが0~1に収まるようになる。
ここまで設定した上で、FlowMapの作成に入るが、作成自体は非常にシンプルな構成となっている。まずFlowMap生成のベースとなるジオメトリに接続する形で「Labs Flowmap」ノードを作成し、そこに「Labs Flowmap Guide」ノードを接続する。この「Labs Flowmap Guide」の第2接続に繋がれたジオメトリに沿ってFlowMapが生成される。今回は河の平面を作成する際に使用した曲線を接続したが、新たにガイドを作成しても問題ない。SideFxの動画では、Curbノードで新たにガイドとなるジオメトリを作成している。
障害物を置きたい場合は「Labs Flowmap Obstacle」ノードを生成し、第2接続に障害物のジオメトリをつなげる。次に「Labs Flowmap to Color」ノードでFlowMapを色に変換した後、「Labs Maps Baker」ノードで書き出せば完成だ。
使用したジオメトリはFileメニュー内のExportから別途FBXで書き出しておき、全てUnityに持っていく。この後UnityでFlowMapを使用するにはShaderGraphなどでカスタムシェーダを作成する必要がある。こちらもまた、非常に丁寧な動画を上げてくださっている方がいるのだが、これに関しては記事を分けたい。本稿はここまでとする。
HoudiniでFlowMapやVectorFieldsを作成してUnityに持っていきたい
※一部自分の認識にずれがあったので、本文やタイトルを直しました。
前提
Unityでのエフェクト制作において、外部の3Dソフトとの連携がほぼ必須になりつつあると感じている。数年前まではテクスチャ画像を After Effects や Substance Designer で作成してパーティクルで表示するのが主流だったが、その後ShaderGraphが普及してきたこともあって、3Dオブジェクトに表示したテクスチャをカスタムシェーダーで動かす作例が増え始めた。2020年に入ってから、そこから更に一歩踏み込んで、例えば3Dソフトで作成したFBXを読み込んでそこにテクスチャを表示するなどの動きが見られるようになり、そうこうしているうちに3Dソフトとの連携を含んだサンプルが散見されるようになってきた。おそらくはこの流れが今後も加速すると予想され、自分がエフェクト部分で仕事をしていこうと思ったらここを押さえないと未来がないだろうと考えている。よって、これからしばらく、自分が使用しているHoudiniとUnityの連携を探っていく。
HoudiniとUnityの連携は、主に以下の3パターンが挙げられるだろう。
- Houdiniで簡単なモデリングをし、それをUnityに持っていってカスタムシェーダーでエフェクト化する。
- Houdiniで3Dのエフェクトを作成し、それをエクスポートしてUnityに持っていく。
- HoudiniでFlowMapやVectorFieldsなど、モデリングプラスアルファの要素を作成し、それをUnityに持っていく。
1に関しては既に作例がいくつもあり、自分もいくつか作成してYoutubeなどに上げている。これは比較的難易度が低く、いままでのUnity上のエフェクト制作と地続きであると考えていいだろう。
問題は2と3で、これまでUnityでは扱う機会があまりなかった要素がいろいろと出てくる。まずはHoudiniでFlowmapを作成してUnityで表示するサンプルを2種、解析しながら自分でも制作をしていきたいと考えている。
ここまでの大きな流れ
最初にVectorFieldsという言葉を知ったのは以下の動画でだった。
この動画内で、爆発のエフェクトにVectorFieldsが使用されており、どうやらそれはHoudiniで作成されているらしい、という話が出た。
正直この段階では、VectorFieldsとは???というところで認識が止まっている状態だ。
その後、いろいろ調べてみると、Houdiniで炎や煙を作り、それをVectorFields込みで書き出す、という作成がいくつか見つかった。例えば以下。
残念ながら現状の自分の力ではこの動画の内容を理解することが出来ず、手元のHoudiniで再現できていない。
並行して、HoudiniでFlowmapなるものを作成してUnityに持っていくサンプルが見つかった。例えば以下。
これまた非常に高度な内容で、完全に自分の理解が及んでいるとは言い難い。ただ、どうやら画像素材としてのテクスチャ以外に、要素の動きを制御する画像を用意することが出来るらしい、ということは分かってきた。
上記の動画はかなり難易度が高いが、以下のものはもう少し理解しやすく組み立てられている。
当面の学習目標として、Unity上でVectorFieldsとFlowmapを使用できるようになることを挙げておきたい。次の記事から、一つずつ進めていく。
LoadAssetAsyncで動的に生成したPrefabのSetParentを実行してエラーが出る件
前提
「Addressables.LoadAssetAsync<GameObject>」関数で動的に生成したPrefabに対して「SetParent」関数を実行すると、
Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption.
とエラーが出る。
対処法
生成したPrefabをインスタンス化していないのが問題。
Instantiate関数でインスタンス化してからSetParent関数を実行すればエラーは出ない。
こちらの記事に助けていただいた。感謝申し上げる。