テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】IJobParallelForFilterというAPIについて

f:id:tsubaki_t1:20190212235334j:plain

UnityPackageのJobについてくる2つのAPIの内の一つIJobParallelForFilterとは何ぞや? という事を追ってみました。

オチ

時間の無駄でした。

Jobsパッケージの2つのAPI

UnityPackageでECSを導入するときによく目につくパッケージに「Jobs」というものがあります。説明文では「IJobParallelForBatchIjobParallelForFilterを追加する」としか書いておらず、またマニュアルにも「このパッケージのドキュメントは無い」と堂々と記述するくらいに圧倒的なパワー力を溜めている感じのパッケージです。

f:id:tsubaki_t1:20190212235807j:plain
「マニュアルを読もう。マニュアルは何だ?」「あ?ねぇよんなもん」というノリ

IJobParallelForBatchに関しては他のマニュアル(Custom job types)で例として挙げられていたりと、用途が無い訳では無いのですが、一方でIjobParallelForFilterは割と探してもノーヒント。

もしかしたら役に立つかもしれないので、ちょっと追ってみました。

ScheduleAppendで要素をフィルタリングしてScheduleFilterで実行する程度の能力

IJobParallelForFilterを継承したジョブを実装すると、なんと2つのAPIが付いてきます。ScheduleAppend(...)ScheduleFilter(...) です。

JobHandle ScheduleAppend( ...)は、一言で言うなら要素をフィルタリングするAPIです。
bool Execute(int index) の戻りがフィルターとなります。結果は NativeList<int> indices に格納します。

一方でScheduleFilter(...)フィルタリングした要素だけを処理するAPIです。
ScheduleAppend(...)の結果 true判定されている要素だけを処理してくれます。

つまり、例えば当たり判定等を考えたときに ScheduleAppend(...) でプレイヤーの近くの敵や弾をフィルタリングし、 ScheduleFilter(...)実際に接触している敵や弾に対してのみ処理を行う…とかに使えそうです。

f:id:tsubaki_t1:20190213001145g:plain
Appendで距離を判定し、Filterで接触中の要素だけ処理する

なお複数のAppendを使用する事は出来ませんでした。なので複数の条件を定義してジョブを連結して組み合わせるといった事をせず、1つの ScheduleAppend(...) に全判定を突っ込む必要があります。

また ScheduleAppend(...)ScheduleFilter(...) も、どちらも同じbool Execute(int index) を呼びます。つまり一つのジョブでフィルタと制御を記述することは出来ません。減点1

IJob「ParallelFor」Filterだから並列処理に…ならない

上のような面倒臭さを超えても、素晴らしそうに見える理由があります。IJobParallelForFilterは「IJobParallelFor」…つまり並列処理が期待出来る点です。並列判定(書込)・並列処理(読込)は面倒くさいので、これが実現出来るなら実に素晴らしい事です。

と思って実行してみると、なんということでしょう。そこには一本のスレッド上で動作するIJobParallelForFilter様がおられるではありませんか。

あれ? 処理が全くスレッドに分散されてないヨ?

f:id:tsubaki_t1:20190213002039j:plain
まるでスレッドに分散してない…

いやいやおかしいと色々試してみても特におかしな所は無かったのでソースコードを確認してみた所、なんと JobsUtility.Schedule(...) を使用しているではありませんか。待ち給え、そのAPIIJob用だ!(IJobParallelFor用はJobsUtility.ScheduleParallelFor(...)

つまりアレです。IJobParallelFor使ってへんのかーい!

f:id:tsubaki_t1:20190213002226j:plain
JobsUtility.Scheduleを呼び出している

この内容なら、NativeQueue.Concurrent とかで並列書込してジョブで一気に処理とか、なんか他にもっと色々とやり方がありそうな気がしなくもないです。

結論

IJobParallelForFilterなんて無かった。いいね?

(強いて言うならサンプル)

サンプルコード

PlayerがEnemyに接触したらパーティクルを1つEmitする例

gist.github.com