【Unity(C#)】unityroomにカメラを使ったゲームを公開する

##はじめに
Unity1Weekという1週間ゲームジャムに参加し、
カメラを使ったゲームを開発しました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">一応、遊べるレベルまで完成しましたが、他の環境でもちゃんと動くのかが心配です😂<a href="https://twitter.com/hashtag/unityroom?src=hash&amp;ref_src=twsrc%5Etfw">#unityroom</a> <a href="https://twitter.com/hashtag/unity1week?src=hash&amp;ref_src=twsrc%5Etfw">#unity1week</a> <a href="https://t.co/1gwXC7Bf2M">pic.twitter.com/1gwXC7Bf2M</a></p>&mdash; KENTO⚽️XRエンジニア😎Shader100記事マラソン挑戦中36/100 (@okprogramming) <a href="https://twitter.com/okprogramming/status/1389498723550388225?ref_src=twsrc%5Etfw">May 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

口を開けるとオジサンが笑いながらジャンプします。

前例が無い中、いろいろと問題を解決してようやく公開できたので
同じ苦労する人が減ることを願いメモします。

##カメラへのアクセス

unityroomでカメラを使ったゲームを公開できるかどうかの調査から行いました。

カメラのアクセスを試みるにあたって、
jsのプラグインを作ったりといろいろ検証してました。

結論、iframeで画面にWebGLを埋め込んでいる現時点(2021/06)の形式では
**こちらから何かしら手を加えてカメラにアクセスすることはできなかった**です。

なので、運営の方に直接問い合わせて許可をいただき、
埋め込まれたリンクを直指定してWebGLを開く形式で対応しました。
概要欄に「ここから遊んでね！」みたいなのを書くというやり方です。

## jsプラグイン

カメラへのパーミッション要求はWebCamTextureを呼び出すなど、
カメラを必要とする関数に紐づいて勝手に要求処理が走るのですが、
せっかくなので勉強がてらjsのプラグインを作成してパーミッション要求を行ってみました。

以下C#側からjsを呼び出すサンプルです。

まず`.jslib`という拡張子のファイルを作成し、そこにjsの処理を記述します。
保存先ディレクトリは`Assets/Plugins`配下です。

```js:カメラのパーミッション要求
mergeInto(LibraryManager.library, {

    CameraRequest: function () {
        var constraints = { audio: false, video: { width: 640, height: 480 } };

        navigator.mediaDevices.getUserMedia(constraints)
            .then(function (mediaStream) {
                var video = document.querySelector('video');
                video.srcObject = mediaStream;
                video.onloadedmetadata = function (e) {
                    video.play();
                };
            })
            .catch(function (err) { console.log(err.name + ": " + err.message); });
    },
});
```

次にC#側のコードです。クラス名がjsと一致していればOKです。
任意の箇所で呼び出し可能です。私は` CameraRequest()`をボタン押下時に呼び出すよう実装しました。

```c#
 [DllImport("__Internal")]
 private static extern void CameraRequest();
```

【参考リンク】：[Unity2020でWebGLを試してみた！ ～基本編～](https://gaprot.jp/2020/10/26/unity2020-webgl-01/)

## OpenCV
カメラの問題は片付きました。
次に、[OpenCV](https://assetstore.unity.com/packages/tools/integration/opencv-for-unity-21088)で顔の認識を行うサンプルがWebGLで問題なく動作するか検証しました。

下記のサンプルをほぼそのまま流用しました。
【参考リンク】：[FaceTracker Example](https://github.com/EnoxSoftware/FaceTrackerExample)

ただ、結論から言うとFPSが安定しませんでした。
何か削れる処理や軽量化に繋がる画期的方法が無いか模索しましたが、
サンプルそのままでもさほど安定していなかったので限界と判断し早急に諦めました。



### model及びcascadeの読み込み
OpenCVではモデルデータと識別器(cascade)の読み込みが必要で、
WebGLの場合はStreamingAssetsとしての利用が可能な実装になっていました。

現時点でunityroomにおいてStreamingAssetsは使用できないため、
Gistにプライベート(リンクを知っている人のみアクセス可)で公開し、参照するようにしました。

【参考リンク】：[UnityroomではStreaming Assetsは使えないのでしょうか？](https://question.unityroom.com/?qa=225/unityroom%E3%81%A7%E3%81%AFstreaming-assets%E3%81%AF%E4%BD%BF%E3%81%88%E3%81%AA%E3%81%84%E3%81%AE%E3%81%A7%E3%81%97%E3%82%87%E3%81%86%E3%81%8B%EF%BC%9F)

##工夫した点
顔の認識が確認できた以降の実装では
キャラクターがジャンプする処理などを追加して挙動を確認していきました。

ステージがクリア不可能でないか等を動かしてデバッグする際に
カメラを付けて口をパクパクして...というのが面倒だったので
入力機能をモジュール化してキー入力と顔入力を切り替えれるようにしました。

![FaceTrackInputModule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/64ae4f4a-2294-4867-0c8b-fc56c994f416.png)

DIライブラリには[VContainer](https://github.com/hadashiA/VContainer)を使用しました。

## おわりに
unity1weekはエンジニアになって以来、
毎回新しいことに取り組む場として活用させていただいています。
1週間の中盤～終盤にかけて参加したことを後悔する(己の実装力に心が折れる)瞬間もありますが、
他の方の頑張りをSNS等で拝見して自分に鞭を打ちながら毎回走り切れています。(ほぼ遅刻)
運営の方はもちろんのこと、参加者の方、遊んでくれた方、みなさんありがとうございます。
次回も楽しみにしています。

##参考リンク
[「OpenCV for Unity」AssetのWebGL対応がやって来たヤァ!ヤァ!ヤァ!](https://qiita.com/utibenkei/items/6e6e603c43f9e48ab81c)