[STYLY]コンテンツによるユーザ移動方法を整理する

こちらは[STYLYアドベントカレンダー](https://qiita.com/advent-calendar/2021/styly)の6日目の記事です。
# 初めに
今年も無事[NEWVIEW School 2021](https://newview.design/school/2021) が終わりました。
NEWVIEW SchoolとはSTYLYを使ってVR/AR作品の考え方や作り方について学べる場所で、
私はテクニカルスタッフとしてJoinさせていただきました。
今年で3年目になりますが、毎年聞かれる質問があります。

それは__STYLYでユーザを移動したい__ です。

STYLYを触った誰しもがコンテンツ上の別の場所にユーザを強制テレポートなどしたくなるものです。
来年もやってくるであろうこの質問に向けて、
今のうちに回答をまとめておこうと思います。

# 結論

* __WebとVRでのみ__移動制御ができます。
* 基本的に実装はPlayMakerで行います。
* 実装はユーザオブジェクトを__①取得__し__②移動__することで実現できます。

__①ユーザオブジェクトの取得__
取得方法は環境ごとに違います。

* Webの場合、[GetMainCamera](https://hutonggames.fogbugz.com/default.asp?W569) で取得します
* VRの場合、`[VRTK]_VIVE`を[Find Game Object](https://hutonggames.fogbugz.com/default.asp?W51)して取得します

WebとVRの両方で対応できるユーザオブジェクトの取得方法がこちらです。

https://twitter.com/noria901/status/1235611398144544771/photo/1

__②ユーザオブジェクトの移動__
ユーザオブジェクトを移動する際は、[Set Position](https://hutonggames.fogbugz.com/default.asp?W102)や[iTween Move To](https://hutonggames.fogbugz.com/default.asp?W601)を使います。
実装方法はこちらをご覧ください
[【Unity/Playmaker】STYLY Studioで使用できるワープシステムの作り方](https://styly.cc/ja/tips/warpsystem01/#Playmaker)
[Unity上でカメラが移動するアニメーションをPlayMakerで実装する](https://styly.cc/ja/tips/movecamera/)

## サンプル
こちらにUnityPackageがあるので、お手元の環境にImportしてご確認ください

* DL先
    * [Github](https://github.com/noria901/STYLY-Workspace/blob/master/Sample/UserTeleportSamples.unitypackage)
* Importしたら以下Sceneをご覧ください
    * Assets/_noria901/UserTeleport/ByPlayMaker/ByPlayMakerSample.unity
* 制作環境
    * Unity 2019.3.6
    * PlayMaker 1.9.1
    * STYLY Plugin 1.3.0
    * バージョンが合わない場合、正常に動作しない可能性があるのでご注意ください
* その他
    * このUnityPackageに後述のサンプルもあるので、今のうちにぜひImportしてください

... 無事、動きましたか？
もしも動かない場合、VRやWeb以外の環境であったり、
移動以外に回転しようとしているのではないですか？

そんな方にはちょっと説明が必要なので、引き続きお楽しみください。


# STYLYのコトを知ろう
そもそもユーザとは何者でしょうか？
Unity上では視覚となるCameraのことです。STYLY上でも同じようにCameraが居て、
コントローラなどの操作に合わせて移動するための機能を付けて、ユーザとして仕立てているわけですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197699/fa0d4d6e-2da2-200f-e2c2-93116b84fd48.png)

コントローラについてもう少し考えてみましょう。
Webならマウスとキーボード、VRなら両手or片手のコントローラ、スマートフォンはタッチパネルですね。
このように各デバイスごとに付コントローラが違うので、
STYLYの移動機能はそれぞれの環境に合わるために実装されています。
最終的に[VRTK]_VIVEが足元、Controllerが両手でMainCameraが頭として実装されました。
STYLY Sessionのアバターで表現するならこんなイメージ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197699/38c3822b-4e08-bc6f-0bd6-d3cc62d5800b.png)

こうしてユーザがSTYLY上で管理されています。
が、各デバイスや動作モードによってこれらオブジェクト有無がわかれています。
STYLYのユーザオブジェクト有無をまとめるとこうなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197699/db48c991-867e-f396-a98c-bb7b18ce9de0.png)
ここで注意が必要なのが、各ユーザオブジェクトがある＝移動制御を受け付ける ではないということ。
STYLYのユーザオブジェクトの移動/回転制御をまとめるとこうなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197699/8e6156c6-e963-d77c-a9ed-b4af38c76c0a.png)
如何でしょうか。
__結論、WebとVRでのみ移動制御ができる(それ以外は何もできない)__のです。

余談ですが、この仕様を公式がまとめているのが[STYLY Document](https://document.styly.cc/doc/docs/en/creator/playmaker/creator_playmaker_player_position/)です。
STYLYは目まぐるしく更新されるので、最新の情報はこちらをご確認ください。

# ユーザ移動方法、回避策を考える

ユーザ自体は移動できない場合どうしたら良いのか。
答えは、周りの空間が動けばいいのです。
周りの空間が動くと自分が動いたように勘違いする。
専門用語でベクション効果といいます。

方向性が決まったら、実現方法を考えるだけですね。
ここでは2つ＋αの実現方法を紹介します。

## アニメーションで実現する
これが一番簡単な実現方法かと思います。
周りの空間は所詮何かしらのオブジェクトなので、
アニメーションやTimelineで動かすことが出来ます。
実装例としてはこちらを参照ください。
[タイムラインを利用して、疑似的に動くカメラを作る【Unity】](https://styly.cc/ja/tips/timeline_unity_camera/)

サンプルのUnityPackageにも同等の実装があるので参照ください。

* [Assets/_noria901/UserTeleport/ByTimeline/ByTimelineSample.unity](https://github.com/noria901/STYLY-Workspace/blob/master/Sample/UserTeleportSamples.unitypackage)

あとは特定の位置に来たら、何かに触ったらなどのインタラクションに連動して
アニメーションやTimelineがActiveになるよう実装するだけですね。

ですがこれにも欠点があります。
例えばユーザの直前の状態に合わせて表示する向きを変えるなど、
動的なテレポートを実現したい場合です。
次の方法はこれを解決できます。

## PlayMakerで実現する
これが一番自由度が高く、一番難しい方法です。
アニメーションでやっていた空間の移動を、算出して実現する方法です。
算出方法を考えてみると、
`空間原点座標 + 移動先座標 - ユーザ現在座標 = 空間移動先座標`
なのでそれぞれのGameObjectを取得して[GetPosition](https://hutonggames.fogbugz.com/default.asp?W235)したら[Vector3 Operator](https://hutonggames.fogbugz.com/default.asp?W690)で算出する、という流れですね。

ですが回転のことを考慮したりプラットフォーム毎に身長が違うなど、
細かい算出をしていくうちにPlayMakerの実装がパンクします。。。[^1]
[^1]: Wev/VR/Mobile全プラットフォーム対応の実装を[以前](https://twitter.com/styly_vr/status/1364871521420808194)作ったが、到底この場で説明できる状態にないスパゲッティFSMになりました。許して。

  
代わりにもう少し簡単な例として、VRでユーザの大きさを変える変えるサンプルを示します。
VR内でクマの杖をもって使うと、
杖を向けた先にユーザが小さくなって世界が変わって見える作品です。

https://gallery.styly.cc/scene/0cb528a8-53dd-11ea-834c-06540631ffe6

クマの杖Prefabがサンプルに含まれているのでご覧ください。

* [Assets/_noria901/UserTeleport/BearCane/BearCane.prefab](https://github.com/noria901/STYLY-Workspace/blob/master/Sample/UserTeleportSamples.unitypackage)

この実現方法の難しさは実装以上に、
STYLYの仕様変更の影響を大きく受けることにあります。
これに悩んだ結果、私は次の答えに行きつきました。

## 別のカメラを移動して見せる
これが私の現状での現実解です。
空間自体を移動すのではなく、
空間内に2つ目のカメラを置いて、ユーザにその映像を見せればいいのでは？という発想です。
これを実現したSTYLYサンプルがこちらです。AR向けなのでMobileで体験どうぞ。

https://gallery.styly.cc/scene/d4171c41-9881-4bf0-9acb-a44077476290

この方法を説明する前に、表現として移動するということは何なのかを考えてみましょう。
ユーザがVRやARグラスをかけた場合、主観的なコンテンツを体験しますが、
多くのユーザはWebやMobile環境で体験します。
特にVRコンテンツであった場合はWebやMobileのモニターによってフレーミングされた誰かの視点を見る。
これは客観的なコンテンツを体験になります。
いうなれば__自身が移動した__という体験ではなく__別の人の視点になった__ 体験なのかもしれません。
インタラクティブな動画を見てるのとそう変わりません。
であれば、ただの板に動画を映すことでも表現出来るのでは？
その答えがこの実現方法です。

Unityで新たにCameraを配置し、Rendere textureというObjectを作ると、
一般的なTexture同様に好きな場所にCamera映像を映せます。
それをユーザのMainCameraに見せればよいのです。
作り方は以下の通り

* CameraとRendere texture作成と設定
    * これはこちらの記事を読むとできます。
    * [【Unity / Render Texture】カメラの映像をオブジェクトに反映する方法](https://styly.cc/ja/tips/unity-rendertexture-camera/)
* 映す先となるスクリーンを作成する
    * uGUI/CanvasとRawImageを作成し、Rendere textureを表示します。
    * VRの際はCanvasのRender ModeをWorld Spaceにし、MainCameraの目の前に配置します。
    * WebやMobileの場合はCanvasのRender ModeをScreen Space – Overlayで全画面に表示します。
* Cameraを自由に動かす
    * これはTimelineなど好きな方法を使いましょう。

こちらのサンプルUnityPackageは以下になります。

* [Assets/_noria901/UserTeleport/ByuGUI/ByuGUISample.unity](https://github.com/noria901/STYLY-Workspace/blob/master/Sample/UserTeleportSamples.unitypackage)

特にMobileやWebがメインでVR HMDにも対応するわがままなオーダーに対して絶大な効果を発揮しますが、
Mobile ARでVR→ARトランジションする際などにも便利なので最近はこれをよく使ってます。


# おわりに
私がSTYLYを始めた2019年から悩み続けていたユーザの移動方法について、現状の私の回答をまとめてみました。
ここまでまとめてきましたが、この課題に対してベストプラクティスはありません。
なので皆様にとって一番実現が早そうな方法を選択してトライしてください。
もしも画一的な実現方法にたどり着いたならSTYLY Magazineにまとめてお知らせします。[^2]

さいごに、よろしければ有識者の皆様、このほかにいい方法ありましたらコメントください。
(そしてSTYLYがユーザ移動CsutomActionを作ってくれることを切に願います。)
[^2]: 画一的な方法だったら、初めからQiitaに書かずにSTYLY Magazineに書きたかったのよな。