UnityのCreator Kit: PuzzleをVR空間内でPuzzleの配置ができるようにする

# はじめに
前回記事 [UnityのCreator Kit: PuzzleをOculusQuestで動かしてみる]
(https://qiita.com/Satoshi_Takahama/items/a68309c1eb54af24030f) の続きです。

前回記事では、既存のPuzzleが配置されたシーンを動かすところまででしたが、今回、Puzzleのオブジェクトを自由に配置できるようにしました。
元のコードではPuzzleを配置する機能はエディター拡張機能を使用して実装されているのですが、VR空間内で動作させるためにはエディター拡張機能は使用できないため、この部分のコードは新規に作成する必要がありました。
ソースコードはGitHubの[CreatorKitPuzzle](https://github.com/SatoshiTakahama/CreatorKitPuzzle)に置いています(前回記事から更新しました)。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">UnityのCreator Kit: PuzzleをOculusQuestで動かせるように改造しました <a href="https://t.co/KpULf8AguD">pic.twitter.com/KpULf8AguD</a></p>&mdash; 高浜 (@SatoshiTakahama) <a href="https://twitter.com/SatoshiTakahama/status/1181890615526559747?ref_src=twsrc%5Etfw">October 9, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#開発環境
Unity 2019.2.4f1
Creator Kit: Puzzle - 1.1
Oculus Integration for Unity - 1.40
VrGrabber-v0.0.3 (hecomi氏作)
DotNetZip-v1.9

#ビルド手順
1. GitHubの[CreatorKitPuzzle](https://github.com/SatoshiTakahama/CreatorKitPuzzle) からcloneでファイルをローカルに持ってきます。
2. 持ってきたファイルを指定してUnityでプロジェクトを開きます。
3. Build Settings から Platform を Android に Switch Platformします。
4. Oculus Integrationをアセットストアからダウンロードし、インポートします。
5. Creator Kit: Puzzleをアセットストアからダウンロードし、インポートします。インポートする際、すべてインポートしてしまうとプロジェクトの設定が上書きされてしまうため、ProjectSettingsのチェックを外します。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/dd5ea41c-6dc5-58ab-d109-458c88694282.png)
6. オブジェクトをつかむためのアセットVrGrabberを[VrGrabber](https://github.com/hecomi/VrGrabber) からダウンロードし、インポートします。自分がダウンロードしたファイルは"VrGrabber-v0.0.3.unitypackage"ですが、数字の部分はバージョンにより変わります。
7. OculusQuest向けに変更したシーンLevelFreeを開きます。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/bcfeed2c-28e8-2db4-7dff-3db2b2b57d30.png)
8. OVRCameraRigのTarget DevicesをQuestに変更します。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/40042519-de1d-b4f6-c256-77472fe9bd36.png)
9. ビルドするシーンを選択し、Build Settings からビルドします。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/47c6cb85-f581-6071-0814-7ac2cc92a44e.png)


#操作方法
・左側コントローラー
スティック 前後移動、左右方向転換

・右側コントローラー
スティック つかんだPuzzleのオブジェクトの操作
スティック押下 オブジェクトを消去(オブジェクトをつかんでいる状態の時)
B UIの表示切り替え
A ハンマー、フリッパー等稼働するオブジェクトの操作/UI上での選択
IndexTrigger オブジェクトの生成
HandTrigger オブジェクトをつかむ

#改造方法の検討
Puzzleを配置する機能を実現するために、以下のことができればよさそうと考えました。
・配置できるPuzzleを表示し、選択する機能
・選択したPuzzleを生成し、自由に配置する機能
・配置をセーブ・ロードする機能

さらに各機能について以下のように検討しました。
### 配置できるPuzzleを表示し、選択する機能
元のプロジェクトにPuzzleのPrefabが含まれているので、それをシーンに一度配置して、後で検索できるようにタグをつけます。FindGameObjectsWithTagで検索したオブジェクトをリスト化してUIに表示し、選択できるようにします。(グラフィカルなプレビュー画面があるほうがわかりやすいのですが、とりあえずはオブジェクトの名前のテキストで表示します)
リスト化が終わったら、シーンに配置したオブジェクトを無効化して見えなくします(最初は有効にしておかないと検索できないので)。

### 選択したPuzzleを生成し、自由に配置する機能
選択したPuzzleをInstantiateでインスタンス化します。配置するときは勝手に動いてほしくないので、RigidbodyのuseGravity=falseに、isKinetic=trueに設定しておきます。
インスタンス化した後、Puzzleを動かす方法はVrGrabberの機能をそのまま使用します。このためにPuzzleのPrefabにはあらかじめVrgGrabbableコンポーネントをつけておきます。また、消去する機能を実現するために、VrgGrabbableコンポーネントのOn Grab Clickedには消去するためのスクリプトを設定しておきます。

### 配置をセーブ・ロードする機能
Unityでデータを保存する機能としてはPlayerPrefsがありますが、今回のように保存するデータが大きくなる可能性がある用途には向かなさそうでした。手順としてはC#オブジェクトからJsonに変換し、System.IO等でファイルに保存すればよさそうということがわかりましたが、色々調べたところ、@tricrow氏の記事[Unityでユーザーデータをローカルにファイルとして保存する](https://qiita.com/tricrow/items/ff51bce01ae2139a6fcd)が私にはわかりやすかったため使用させていただくことにしました(自分の使い方ではコードを変更する箇所がなく、全くのコピペです)。
1点、現在では圧縮に使用しているライブラリDotNetZipのファイルを入手する方法が難しく(わかりにくく)なっており、[CodePlexからバイナリを入手](https://plaza.rakuten.co.jp/satocchia/diary/201807270000/)を参考にさせていただきました。DotNetZipのライセンスはMicrosoft Public License (MS-PL)となっています。また、バイナリをAssets\Plugins\DotNetZipに置いています。
Jsonへの変換にはJsonUtilityを使用していますが、変換可能なオブジェクトには色々と制約があります。最初知らずにPuzzleのオブジェクトを突っ込んでいたらうまくいかず、結構はまりました。最終的には、オブジェクトの名前と位置だけを保存し、復元するときはそれをもとに再度インスタンス化するようにしました。この辺りは実機上でデバッグするのは難しく、PC上で実行して生成されたJsonを確認(自分はVisual Studio Codeを使いました)を繰り返しました。

#GitHubに置いたソースコードについて
DebugUIBuilderHand.csは、Oculus Integrationに含まれているDebugUIBuilder.csをもとに改変したもののため、ライセンスはOculus Integrationに従います(Assets\Oculus\SampleFramework\license.txtをご確認ください)。
IDataFile.cs, DataFilePlain.csは、@tricrow氏の記事[Unityでユーザーデータをローカルにファイルとして保存する](https://qiita.com/tricrow/items/ff51bce01ae2139a6fcd)中に記載されているコードをそのまま持ってきたものです。
そのほかのソースコードはCC0としますので好きなようにご使用ください。
PlayUI.csは、Puzzleの操作に関するUIを構築するためのもので、Puzzleを配置する機能はほとんどこのコードで実装しています。DebugUI.csは、デバッグ用の情報を表示するためのUIを構築するためのもので、DebugMover.csは、キャラクターを操作するためのものです。

#制限事項
以下既知の問題があります。
・オブジェクトを消去すると、アプリが落ちることがあります。
・複数のパーツで構成されたオブジェクトを移動するとバラバラになることがあります。
・ボールに映る映像には配置したオブジェクトは反映されません。
・Restartすると配置モードに戻ります。
・Restartするとログが更新されなくなります。