見出し画像

SONYのToF ARを使ったアプリを一般公開してみた

#SONY #ToFAR #深度センサ #ToF #Unity #ハンドトラッキング #フェイストラッキング #ピストルバンバン #後出しジャンケン #ガキ使

概要

SONYのToF ARを使ったミニゲームをAppStoreで公開してみたので、ToF ARの使い方や注意点などをまとめておきます。想定する読者としては、Unityの基本機能を最低限使える方向けとなっていますのでご了承ください。
構成は、Mac mini + iPhone 13 Proです。ソースコードはGithubでみることができます。

TofARとは

SONYが公開しているスマートフォンの深度センサ(ToFやiPhoneのLiDARなど)を使ったAR・VR向けのSDKです。特徴として、ハンドトラッキング(指や関節の認識)機能が提供されていたり、Unityで開発できるようになっていたりするそうです。

作ったアプリ

ToF ARのハンドトラッキング機能を使ったアプリを作ろうと思い、アイデアを考えていたところ、年末のガキ使であった「ピストルバンバン」と「後出しジャンケン」のゲームが手も使っていて面白そうだったのでその2つを作ってみました。
「ピストルバンバン」はリズムに合わせて右手と左手の形を入れ替えていくゲームです。手の形はピストルとOKの2種類があって、右手がピストル、左手がOKの形だったら、音がなったタイミングで右手をOK、左手をピストルにし、次になったら右手をピストル、左手をOKにすると入った感じです。
リンクも一応貼っておくので説明がわかりにくかったらみてみてください。

「後出しじゃんけん」は、その名の通り相手が出した手に対して後出しで負けるように手を出すゲームです。こっちはわかりやすいと思いますが、一応リンク貼っておきます。

ToF ARを使った開発の流れ

ここからは開発の流れやToF AR周りの実装などをそれぞれ説明していこうと思います。

ダウンロード

ToF ARの公式サイトからいけます。バージョンは開発時で最新の1.1.0をダウンロードしました。

セットアップ

公式のUser Manualにある「3.1 ToF ARのセットアップとアップグレード」の通りでいけました。基本的にこの通りにやれば大丈夫だと思います。

デバッグサーバのセットアップ

これはやらなくても開発はできますが、個人的には絶対にやっておいた方がいいと思っています。そのくらい開発効率くらいが変わってきます。というのも、ToF ARはスマートフォンのセンサデータを取得するためシミュレータでのデバッグができず、毎回実機にビルドする必要があります。しかし、デバッグサーバを使えばEditor実行時に実機のセンサデータが取得できるようになり、ビルドせずにデバッグが可能になります。

設定は公式の「4.4.1. TofARServerのセットアップ」の通りで大丈夫ですが、注意点が2つあります。
1つめはPCとスマホが同じネットワークに接続されていることです。特にPCでVPNとかに接続しているとできないので注意です。
2つめは、おそらくMacユーザ限定ですが、初回実行時にライブラリの権限を設定する必要があることです。セットアップしてEditor実行すると以下のようなダイアログが表示されます。Macユーザの方は見慣れていると思いますが、設定のプライバシーとセキュリティからライブラリの使用を許可するようにしてください。複数回聞かれますが全て許可してください。

必要なコンポーネントの決定

ここから実際のアプリ作りの話になります。ToF ARでは機能(コンポーネント)ごとにManagerクラスが用意されており、使いたい機能に応じてManagerを追加する作業が必要です。コンポーネントの一覧はこのページにあります。

今回はカラー映像を表示するためにColorコンポーネントを、ハンドトラッキング機能を使うためにHandコンポーネントを、あと顔を隠せるような目隠し機能も欲しかったのでFaceコンポーネントの3つを使おうと決めました。

コンポーネントの追加

必要なコンポーネントが決まったら、そのコンポーネントをシーン内に追加していきます。追加方法は簡単で、各コンポーネントを管理するTofArxxxManagerをHierarchyに配置するだけです。公式チュートリアルの「5.1 Detph映像の表示」をみるとToF ARで共通に使われる機能を提供するTofArManagerと、デバイスとの接続を管理するTofArTofManagerのPrefabをHierarchyに追加しています。

今回はColor・Hand・Faceコンポーネントを使いたかったので、TofArColorManagerTofArHandManager・TofArFaceManagerの3つを追加しました。

Hierarchyに追加したManagerのPrefab

Managerの設定

次に追加したManagerの設定をします。各Managerのパラメータの詳細はAPI リファレンスで確認できます。気になる人は見てみてください。

ToF ARはデフォルトだとリアカメラが起動されます。今回はフロントカメラを使いたかったので、TofArTofManagerのInspectorからUser Front Camera As DefaultをTrueにします。

TofArTofManagerのInspector

続いてTofArHandManagerの設定です。ハンドトラッキングはいくつかモードがあるらしくフロントカメラを使用する(カメラと向き合って使う)場合は、RecogModeFace2Faceにします。また、後述するサングラスの表示・非表示を指パッチンのジェスチャでやろうと思い、Auto Start Gesture EstimationをTrueにします。

TofArHandManagerのInspector

カラー映像の表示

これはチュートリアルの「Depth映像の表示」と同じことをColorで行うことでできました。具体的には、TofArColorManagerを追加したのち、ColorViewRawImageをCanvas内に配置すれば画面サイズに合わせたカラー映像が表示されました。

ただ、カラー映像を画面からはみ出さないように拡大しているせいか、上下が黒くなってしまいました。このままだと、見栄えが悪いのでどうにかして治す方法を探しました。

ColorViewRawImageにはTextureMapperRawImageというスクリプトがアタッチされており、おそらくこいつがカラー映像を管理しているのだろうと推測しコードをみたところ、それっぽい記述があったのでこいつを編集することにします。結論から言ってしまうと、ColorViewRawImageにはMaxmizeというプロパティがあり、こいつがTrueだと拡大処理が入るのですがそこの処理を少し変えることで画面からはみ出している箇所はあるものの、画面全体に拡大できるようになりました。
一応コードも載せておきます。コメントアウトしているところが元の記述です。

var parentAspectRatio = parentWidth / parentHeight;
if (parentAspectRatio > AspectRatio)
{
        //width = height * AspectRatio;
      height = width / AspectRatio;
}
else
{
        //height = width / AspectRatio;
    width = height * AspectRatio;
}

ハンドトラッキング結果の取得

次にハンドトラッキング結果の取得の方法についてみていきます。データの取得方法についてはユーザマニュアルの「4.1. ストリームデータアクセス」に詳細が書かれています。


ここを見ると「Managerごとにスクリプト経由で取得できるインターフェイスが用意されている」という記述があり、各コンポーネントのManagerがそれぞれデータを持っており、開発者はManagerを参照してデータを持ってくるのが主な流れのようです。下に公式が載せていた構成図も貼っておきます。

ToF ARのデータ構成図(公式サイトから引用)

また、「ManagerクラスはManager Prefabがシーンにインスタンス化されていれば、どのユーザースクリプトからもManagerクラスのInstanceフィールドより参照可能である。」と書かれており、HierarchyにManagerが追加されていれば以下のコードでManagerを参照できます。

var instance = TofArTofManager.Instance;

なので、Handコンポーネントのデータを取得したい時はTofArHandManagerのHandDataがデータを保持しているはずなので、以下のようなコードでデータを取得できます。

var handData = TofArHandManager.Instance.HandData;

HandDataの詳細については、APIリファレンスで調べると出てくるのでそこを見ながら必要なデータを探します。今回作るゲームで必要なのは、手の形の認識結果になります。ピストルバンバンでは「ピストル」と「OK」、後出しジャンケンでは「グー」「チョキ」「パー」の形を認識したいので、それらしい情報がないか探すと、GetPoseIndex(out PoseIndex, out PoseIndex)という手の形(PoseIndex)を取得できるメソッドが見つかりました。

ということで、実際に書いたコード載せておきます。注意する点としては、Managerのnamaspace(TofAr.V0.xxx)を書いておくことと、HandDataがnullになる時(手が映っていない時)を考慮することだと思います。

// AppController.cs

// TofArHandManagerのnamespace
using TofAr.V0.Hand;

private PoseIndex leftPose;
private PoseIndex rightPose;

void Update()
{
    // HandDataが存在しない(手が映っていない)時
    if (TofArHandManager.Instance.HandData?.Data == null) {
        leftPose = PoseIndex.None;
        rightPose = PoseIndex.None;
        return;
    }
    // 右手と左手の形(PoseIndex)を取得
    TofArHandManager.Instance.HandData.GetPoseIndex(out leftPose, out rightPose);
}

サングラス(目隠し)の追加

シャイな人向けというか顔バレしたくないので、顔認識を使って目の位置にサングラスを表示させる機能も作ってみました。基本的には、取得した顔認識の結果から目の特徴点を探してそれをUI上の座標に変換して、左目と右目の中点とかに画像を表示させる感じです。顔認識の結果の取得方法はHandと同じなので割愛しますが、目の位置をUI上の座標に変換するの結構苦労したのでその部分を説明しようと思います。
Unityでは3次元座標(ワールド座標)から2次元座標(スクリーン座標)を変換するにあたって、以下の方法がよく使われると思います。

var screenPoint = Camera.main.WorldToScreenPoint(point);

しかし、この方法で実装してみると表示されている顔の位置にサングラスは正しく表示されませんでした。色々試行錯誤した結果、以下の方法で位置合わせすることができたので、コードと一緒に載せておきます。

  1. 座標変換用に別のCameraを用意して、VirutualToFCamera.csをアタッチ

  2. 1で作成したカメラに対して目の位置をViewport座標に変換

  3. 算出した座標をColorViewRawImage上の座標に変換

  4. ColorRawImage上の座標からCanvas上の座標に変換

// SunGlass.cs

private Vector2 CalcCanvasPointFrom(Vector3 facePoint)
{
    // 2. ColorCamera上の座標に変換
    var viewPoint = colorCamera.WorldToViewportPoint(facePoint);
    
    // 3. ColorViewRawImage上の座標に変換
    var colorImagePoint = new Vector3(
        viewPoint.x * colorImageRect.sizeDelta.x,
        viewPoint.y * colorImageRect.sizeDelta.y,
        viewPoint.z);

    // 4. Canvas上の座標に変換
    var marginX = (colorImageRect.sizeDelta.x - canvasRect.sizeDelta.x) / 2;
    var marginY = (colorImageRect.sizeDelta.y - canvasRect.sizeDelta.y) / 2;
    var x = colorImagePoint.x - marginX;
    var y = colorImagePoint.y - marginY;
    return new Vector2(x, y);
}

ざっくりした解説になってしまうのですが、「CameraにアタッチするとColorカメラの内部パラメータに基づきプロジェクションを調整する」VirtualToFCameraというスクリプトを見つけたので、それをMainCameraとは別のCamera(ColorCameraという名前にしている)を用意してそこにアタッチします。

そうすることでワールド座標の変換が行えるのですが、カメラ映像はColorViewRawImageが管理しており画面に合わせて拡大もしているので、Screen座標に変換してもColorViewRawImage上の座標にはならず位置合わせができません。
ただ、ColorViewRawImageはColorCameraの映像を映しているはずなので、Screen座標ではなくViewport座標を算出し、それにColorViewRawImageのWidthとHeightをかけることでColorViewRawImage上の座標を求めます。
あとは、CanvasのWidthとHeightを考慮することで、ColorViewRawImage上の座標をCanvas座標に変換する、という寸法です。

サングラスの表示・非表示

サングラスは、指パッチンをすることで表示の切り替えができます。ToF ARのハンドコンポーネントでは、指パッチンはジェスチャというカテゴリに分類されます。APIリファレンスにジェスチャの一覧があったので、気になる人は見てみてください。このページを見ると指パッチンはSnapFingerという名前で扱われています。

このジェスチャの結果を取得する方法ですが、APIリファレンスのTofArHandManagerのページにOnGestureEstimatedというイベントが用意されていることが確認できます。これを使って書いたコードを以下に載せておきます。

// AppController.cs

void Start()
{
    TofArHandManager.OnGestureEstimated += OnGestureEstimated; 
}

public  void OnGestureEstimated(object sender, GestureResultProperty result)
{
    if (result.gestureIndex == GestureIndex.SnapFinger)
    {
        sunglassImage.SetActive(!sunglassImage.activeSelf);
    }
}

AppStoreに登録&サポートに連絡

ここからはアプリをAppStoreに登録する話です。基本的な流れは他のアプリと変わらないのですが、注意点が二つあります。
一つめは、アップロードするときにUpload your app's symbolsのチェックを外しておくことです。

AppStoreConnectへのアップロード

二つめは、プライバシーポリシーにTrueDepth(デプスセンサ)を使用してデータを収集する旨を書いておくことです。自分は公式が出しているサンプルアプリ(ToF AR Samples Basic)のプライバシーポリシーを真似して書きました。

また、ToF ARのEULAには「2.3 You agree to notify SONY when you license, sell, offer for sell or otherwise distribute the Application whether such Application is chargeable or not, on any application stores including, but not limited to App Store and Google Play by sending an e-mail to the e-mail address below.」とあるので、アプリを公開する時はサポートに連絡する必要があります。sss-support-tofar@sony.com宛にメールを送るだけでよさそうなので、忘れずにやっておきましょう。

まとめ

ToF ARの使い方とそれを使った実装について、できるだけわかりやすくなるようにまとめてみました。わかりづらい箇所などあったら是非コメントしてもらえると助かります。また、ToF ARについての質問もわかる範囲でお答えするので是非聞いてください。

この記事が気に入ったらサポートをしてみませんか?