STYLYでのAR/VR両対応シーンの作り方

# はじめに
これは[【STYLY主催】XR Advent Calendar 2022](https://qiita.com/advent-calendar/2022/styly)の16日目の記事です。
今回はSTYLYでAR/VR両対応シーンの作り方について説明します。


# 最近作ったもの
埼玉県飯能市吾野で行われた[ネオニッポン市](https://www.ava-jp.com/neonippon)というイベントに、STYLY上で動くAR作品を出展しました。
[A.G.N-SoundSpirit](https://gallery.styly.cc/scene/a69d5ca4-7260-49c9-a3be-88d14b1fbce9)
※WebPlayerだと一部演出(AudioReactive)が効いてないので、VRかスマホで見てね
この作品は**再生するごとにパターンが異なる半自動生成のエンドレス音楽**と、**それに呼応して動くモニュメント**というARコンテンツです。

実際の展示の様子はこんな感じでした。

<iframe width="560" height="315" src="https://www.youtube.com/embed/-QNzINyhe64" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


# AR/VR両対応したい
冒頭のAR作品は当日、会場での展示用に作成したものです。
しかしせっかく作ったので後日、どこでも見られるように調整して公開しました。
ただAR用に作るシーンって、基本的にメインのコンテンツだけしかシーン上にないんですね。
WebやVRでもこのAR用シーンは見られるけど、真っ暗な空間にコンテンツだけがあるのってちょっと寂しいんですよね。

![スクリーンショット 2022-12-16 19.07.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/66355bfb-1698-41e5-5c3a-6555ca0e26c1.png "こんな感じだとちょっぴり寂しい")


今までは、会場の雰囲気を再現するためスキャンしたデータを置いたり、Skyboxを設定したりしたシーンを別途作ってこちらはVR用です、みたいに分けたりしていました。
ただこうなるとコンテンツに変更があった場合に、AR用シーンとVR用シーンの両方に更新が必要になったり、どっちのシーンをシェアすべきなのかとか色々取り回しが悪いのが気になってました。
なので冒頭に紹介したシーンでは**１つのシーンでAR/VR両対応する**というのをやってみました。


# これを使えばできるよ
いきなりですが便利なアセットを作ったので、これを使って貰えれば簡単にできます！
但しちょっとハック的なことをやっているので、動作を保証しないものだと思ってください。
理由は後述します。
[ARVR_EnvSwitcher.unitypackage](https://drive.google.com/file/d/1Thw6vJzDzkNgwfbbTZiTPwXgWpoHM2hQ/view?usp=share_link)
※別途[STYLY Plugin for Unity](https://styly.cc/ja/download/)と[Playmaker](https://assetstore.unity.com/packages/tools/visual-scripting/playmaker-368?locale=ja-JP)が必要です


## 使い方説明
ARVR_EnvSwitcher.unitypackageをimportすると、Assets直下に**ARVR_EnvSwitcher**というフォルダがありますね。
![スクリーンショット 2022-12-16 19.15.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/5f59f044-fa98-65cf-dba9-4f04476b8f68.png)

このうち、**ARContent**はただ回転しているだけのCubeなので特に気にしなくてもいいです。
メインは**ARVR_EnvSwithcer.prefab**です。
こいつに付いてるPlayMakerFSMのInputsを見てください。
**model**は、非AR環境で表示したいGameObjectを指しています。
Hierarchyの**ARVR_EnvSwithcer/Model**以下に自分の表示したいものをぶら下げたりすると良いでしょう。
**skybox**は、非AR環境でセットしたいスカイボックスのmaterialを指しています。
これも自分の表示したいスカイボックスのアセットに差し替えると良いでしょう。

このprefabをSTYLYにアップロードしてARシーンに置くだけで、そのシーンがAR/VR両対応になります。
※シーン作成時にAR Scene Templateを選択してください。
※シーンにSkyboxのアセットを置いたりするとAR表示できなくなります(後述)
※STYLY Studio上ではスカイボックスはセットされません(後述)

ARContentとARVR_EnvSwitcherを置いたシーンが[こちら](https://gallery.styly.cc/scene/569ef0f5-7e51-4625-b60d-518906898619)です。

ARでは、回転するでけえCubeだけが表示されています。
![IMG_4727.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/6c515a3f-f3da-0bde-a9cd-98691e0399cd.jpeg)

VRモードや他プラットフォームでは、適当に並べた柱が表示され、スカイボックスもセットされています。
![IMG_4729.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/6985ee7f-7a47-9943-a02c-fd950431736c.jpeg)


# 仕組み
ここから先は細かい話になるので、読み飛ばしてもらってもOK！

STYLYのmobileアプリの仕様として、シーン再生前にそのシーンがAR表示用シーンでなければVRシーンとして扱う、というものがあります。
具体的にはスカイボックスが設定されたシーンはVR向けシーンである、と判断して勝手にVR表示モードでシーンが再生されます。
この判定は、下記スクショのこの部分のAR指定よりも優先されます。
![IMG_4724.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/6e384f9a-3fcd-643c-e960-60d8e77bcedb.jpeg)

そこで前述のアセットでは下記のようなことをやっています。
* IsAppType系カスタムアクションを使って、再生環境を判別
* AR環境ではmodelを非表示
* 非AR環境ではmodelを表示
* 非AR環境でSTYLY StudioじゃなければSkyboxをセット(後述)

## 再生環境の判別

STYLY Pluginに含まれるIsAppType系カスタムアクションを使うと、実行しているSTYLYがどのプラットフォームのものかを判別できます。
但しこの処理の中に、一点補足が必要な部分があります。

下記スクショはARVR_EnvSwitcherのFSMの処理です。
![スクリーンショット 2022-12-16 19.46.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/20a6e7d8-eefa-8e12-9f4b-e68abc22d544.png)
おやおや、MissingActionになっている箇所がありますね。
この箇所を削除してしまうと再importするしか復旧できなくなるので、注意してください。
ここは実は未公開の**IsMobileVR**というカスタムアクションが指定されています。
このアクションを使うとmobileアプリのVR指定再生かどうかを判別できます。
リリース済みのアプリには既に組み込まれているものの、配布しているSTYLY Pluginに含まれていないのでMissingActionになっています。
参照自体はあるならこのままアップロードしても動くはず、で試してみて動いたので配布することにしましたが、動作は保証しないものだと思ってください。
**IsMobileVR**カスタムアクション自体は、近くSTYLY Pluginのアップデートで使えるようになります。 
【追記】[STYLY Plugin for Unity v1.7.2](https://styly.cc/ja/download/)で使えるようになりました。

## Skyboxのセット

![スクリーンショット 2022-12-16 19.51.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5671/28a48436-4ab2-8323-1622-4af61253cb2a.png)
SetSkyboxアクションの手前で、**IsEditing**カスタムアクションを用いてTrueの場合にSetSkyboxアクションを実行しないようにしています。
これは次のような理由から必要な処理です。

前述のように、STYLY mobileアプリではSkyboxの有無によりそのシーンがVR用シーンであるかどうかを判断しています。
このSkyboxの有無というのは単純にSTYLYのSkyboxアセットを置いているかどうかだけではなく、シーンの保存タイミングでSkyboxがセットされているかどうかによってシーン自体のメタデータとして記録されます。
そのためSTYLY Studioでのアセット配置時にSkyboxをセットする処理が動いてしまうと、シーン保存時にSkybox有りのシーンとして記録されてしまい、mobileアプリでAR実行しようとしてもVR表示になってしまいます。
これを回避するために、実行環境がSTYLY Studioかどうかを判別できる**IsEditing**カスタムアクションを用いています。

## なにそれややこしい

何故そんなややこしい仕様なんだ、と疑問に思うのも無理はないでしょう。
私もそう思いますし、実際この記事のためにサンプルを作りながら「しまった、こういう挙動になるんだった」と思い出して回避策を入れるということをやっています。
これは、後方互換性を維持しながら色んなデバイスに対応し続けるという、かなり難度の高いSTYLY開発の歴史から生まれてしまった闇のようなものなのです。
初めから全てを見通せていれば当然そんな仕様にならないようにできるのですが、現実は移動する時間軸の上にあるのでなかなか理想通りに全ての条件が見えてなかったりもするんですよね。

個人的にもプラットフォーム開発は、単発のゲーム開発とは異なる難しさの連続だなぁと感じています。
特に設計に関しては非常にセンスを問われるなぁ、と痛感しています。
その困難さを俺ならスマートに解決してみせるぜ！という腕利きのエンジニアはいらっしゃいませんか……？

# 正社員エンジニア絶賛募集中！！
Psychic VR Labでは新メンバーを募集しています！
とりあえずどんな職場か話を聞いてみたいな興味あるな、って人はカジュアル面談とかもやってるのでお気軽にどうぞ！
https://psychic-vr-lab.com/recruit/