VRChatワールド開発　FireSpawnWelding

#概要
***ディナー***
https://twitter.com/hatsuca_vr/status/1069619612612841474
***タワーディフェンス***
https://twitter.com/hatsuca_vr/status/1069617279535505408

上記の動画で使用した、**FireSpawnWelding** と私が勝手に呼んでいる手法について解説します。
これを使う事で**接触した任意のオブジェクト同士の結合**や、**動的なターゲット指定**を実装する事が出来ます。
(※ある程度VRChatのギミック作成に慣れた人向けの記事となっております。)

#ExplosionFireAndDebris
中心となるのは *StandardsAssets* の *PerticleSystems* フォルダにある、
***ExplosionFireAndDebris***(と *ParticleSystemMultiplier* )というスクリプトです。
まずはこちらのスクリプトの内容を簡単に解説します。

*ExplosionFireAndDebris* は爆発の際の破片の生成と、それらや周囲に発生する炎を表現するスクリプトです。
コルーチンで実装されており、2フレームに渡って *DebrisPrefab* の生成と、*FirePrefab* の生成を行います。

また、コンポーネントとして同フォルダの *ParticleSystemMultiplier* を使用しており、
これの変数 *Multiplier* がPrefabの生成数や生成範囲に関わってきます。

*DebrisPrefab* を生成するコルーチン1フレーム目の処理を **DebrisSpawn**、
*FirePrefab* を生成する2フレーム目の処理を **FireSpawn** と呼ぶことにします。

- **DebrisSpawn**
オブジェクトから半径[3 × *Multiplier*]内のランダムな地点に *DebrisPrefab* を[*numDebrisPieces* × *Multiplier*]個生成します。
生成する *DebrisPrefab* はリストからランダムに選ばれます。PrefabのRotationはランダムです。


- **FireSpawn**
    - １．半径[10 × *Multiplier*]内の、最大[*NumFires*]数のコライダにオブジェクトからRayを飛ばして、接触点から0.5浮かした地点に *FirePrefab* を生成します。角度はIdentityです。
**この時生成された *FirePrefab* はRayが接触したオブジェクトの子になります。**

 - ２．*NumFires* の数よりコライダ数が少なかった場合、オブジェクトのY+1の位置から-Y側のランダムな方向にRayを発射、接触点に親無しの*FirePrefab*を生成します。
この処理一回毎にRayの距離を[*Multiplier*]だけ伸ばして、 *FirePrefab* を生成する度に減算される *NumFires* が無くなるか、10回処理を繰り返したら終了します。

イメージとしては1フレーム目でランダムに破片をばら撒いて、2フレーム目でそれらや周囲の地面に火を点ける感じですね。

#FireSpawnWelding
FireSpawnWeldingでは、*FirePrefab* がRayの接触したオブジェクトの子として生成されるのを利用します。
*FirePrefab* にHierarchy上のオブジェクトを指定する事で、生成元のオブジェクトと接触オブジェクトとの間を
*FirePrefab*で繋いでしまおうと言うのが**FireSpawnWelding(FireSpawn溶接)**の考え方です。

https://drive.google.com/open?id=1L8i_j-rrwUbtwgbgSJnfD8LlpXS3cpYZ
GoogleDriveにPrefabを用意したので、ここからはインポートして実際に構成を見ながらお読みください。
(別途StandardAssetsとVRCSDKのインポートが必要。Unity5.6.3p1で作成。)

このPrefabでは、
BodyとPartsのJunctionコライダが接触する事で、最終的にPartsが *FirePrefab* を経由してBody側のJunctionの子になります。
![2018-12-05.png](https://qiita-image-store.s3.amazonaws.com/0/266653/804e9c45-f7cd-e40a-e4a5-8c37d95fd031.png)

###1. *FirePrefab* の生成
Junctionコライダ同士が接触する事でSpawnEventがアクティブとなり *ExplosionFireAndDebris* が起動します。
*ExplosionFireAndDebris* はコルーチンのため、SendMessage関数を使ってStartを呼び出します。
*FirePrefab* が付着する範囲を最小限にするため、[*Multiplier* × 10] の値がコライダ半径と同じくらいになるように適当に設定します。
Partsに干渉できるように *FirePrefab* の生成元(Welding)はPartsと兄弟関係にしています。
![2018-12-05 (4).png](https://qiita-image-store.s3.amazonaws.com/0/266653/7057b05c-9bf1-33cd-022f-ce771c921c83.png)
###2.CallとCallback
*ExplosionFireAndDebris*実行の際、関係ないオブジェクトにも *FirePrefab* が付着してしまうので、正しいオブジェクトに付着したかどうかを確認するため生成された *FirePrefab* から親に信号を送り、それが帰ってこなければ削除するようにします。
確認信号にはLegacyのAnimationコンポーネントで使える階層の上方向への相対パスを利用します。
(参考：https://twitter.com/kaisya_no/status/1044894667622342658)

Parts側で生成された *FirePrefab* はBody側のJunctionの子になっているはずなので、『../CallEvent』と指定する事でBody側のCallEventを起動、同じ方法でParts側のCallbackEventまで信号を返します。
この際、生成された *FirePrefab* の名前には最後に(Clone)が付く事に注意します。
![2018-12-05 (2).png](https://qiita-image-store.s3.amazonaws.com/0/266653/fbfb6786-352d-793e-9846-7595a1929925.png)
###3.オブジェクトの接続
Callbackが送られてきたら削除用スクリプトである *TimedObjectDestructor* の削除タイマーを*CancelInvoke*関数で止め、Parts本体を*SetParent*関数で *FirePrefab* の子に設定します。
これで  **Body - Junction - *FirePrefab* - Parts** という形でBodyとPartsを親子関係で繋ぐことが出来ました。
ディナーではフォークやナイフがBody、フルーツがPartsの役割です。
タワーディフェンスでは応用してタワーの射程内のイーサンに *FirePrefab* を付着させ、それをターゲットに射撃を行っています。
![2018-12-05 (3).png](https://qiita-image-store.s3.amazonaws.com/0/266653/db788e79-91ae-d800-4674-3c7461ecb458.png)

###取り合えずの課題
当然それぞれのローカル上でしか *FirePrefab* は発生しないので同期取るのがくっそ難しいです。
出来ればMasterとか、一人のプレイヤー上だけで処理したいです。

そういう訳にもいかない場合は…頑張るしかないですね。(白目)

#おわりに
扱いの難しさはあれど、これまでのVRChatギミックでは困難だった動的なオブジェクト取得がある程度可能になるので、出来る事の幅が大きく広がる技術だと思います。
この構成を叩き台にして、新たな楽しいワールドが生まれてくれることを願っています。

さぁ、これを読んだあなたも今日からLet's溶接！

PS. uGUIイベント弄る時のストレスが半減するので、ギミック作るなら絶対これ使った方が良いです。
https://twitter.com/naqtn/status/1059162082434662400

VRChat 技術メモ帳
http://vrcworld.wiki.fc2.com/