VRMモデルをカメラ目線にする

カメラ目線にしたい

Vtuberになって配信するときにカメラ目線にしたい、と言われ開発してみました。

VRMLookAtHeadスクリプトをいじる

UniVRMが入っている状態で、UnityにVRMをインポートする。

すると、VRMに自動で付いてくるVRMLookAtHeadスクリプト

f:id:yuma1217:20190523221759j:plain
VRMLookAtHead

これのTargetに、VRMに見てもらいたいオブジェクトをヒエラルキーからドラッグアンドドロップすれば、完成。

今回はカメラ目線にしたいので、Vtuberの前に置いて映すカメラを選択すればOK。

動的にVRMインポートを行いカメラ目線にする

今回、VRMを動的にインポートするように開発を行った。

なので、インポート段階では、↑のTargetは何もアタッチされていない。

ので、動的にアタッチする。

// VRMを動的にインポートした際に、このスクリプトをVRMにAddComponent
private void Awake()
{
    // VRMLookAtHeadスクリプトの参照を受け取る
    var lookAtHead = this.gameObject.GetComponent<VRMLookAtHead>();
    // Targetに目線を向けたいカメラをアタッチする
    lookAtHead.Target = GameObject.Find("VtuberCamera").transform;
}

結果

多分カメラ目線になった、気がする。

Typescript * ReactでFuntion Componentに引数を渡す

はじめに

少しばかり仕事でReactを使う機会があります。
Reactを触った事ないし、Typescriptも触った事なくて、ドキュメントも結構少ないため、苦戦しながら書いてます。。
でもLintが厳しく指導してくるのが癖になってきて、先生に教えられているみたい。
自分へのメモ兼Typescript*Reactを使う事になってしまった人のために書きます。

Function Component

Reactのチュートリアルやって、そもそもJavascriptにClassなんてあったんだ・・・!
なんて気持ちだったのに、最新版のReactではClassComponentではなく、FunctionComponentを使っていこうな感じらしい。

親側のコンポーネントから何か値を入れたい時の書き方をメモする。

import * as React from 'react';
const App : React.FC = () => {
    return(
        <>
            <Child1 name="yuma1217" />
            <Child2 name="yuma1217" />
        </>
    );
}

const Child1 : React.FC<{name:string}> = (prop) => {
    return(
        <div>
            {prop.name}
        </div>
    );
}

interface IAppInterface {
    name : string;
}

const Child2 : React.FC<IAppInterface> = (prop) => {
    return (
        <div>
            {prop.name}
        </div>
    );
}

二つのChildコンポーネントは同じことをしている。
Child2のInterfaceを使うやり方がTypescriptでは正しいんだと思うんだけど、最初の学習でInterface書くの少ししんどくて。。
そんなときはChild1のように書ける。
型指定しないと怒られるので、もし面倒だったら、とりあえずanyにして置けば大丈夫。(怒られないってだけで本番の開発ではしっかり型指定した方が良さそう)

UnityのGameObjectのアクティブ、非アクティブの切替え

はじめに

何かキー押したときに、GameObjectのアクティブ・非アクティブを切り替えたい時ってありますよね。
昔if文使って処理書いてたけど、もっときれいで楽な方法をどっかのリポジトリで見て使ってたけど、忘れてたので、ブログにメモとして残します。

実装

昔の実装

public GameObject target;

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        if (target.activeSelf)
        {
            target.SetActive(false);
        }
        else
        {
            target.SetActive(true);
        }
    }
}

正直ですね。
activeかどうか判断して、切り替える。

今の実装

public GameObject target;

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        target.SetActive(!target.activeSelf);
    }
}

シンプル~。笑
多分有名っぽい気がするので、みんな知ってそうですが、自分へのメモで残します。

OculusGoにアプリをデプロイできない問題

今までアプリをデプロイできていたのに、急に出来なくなってしまいました。
やってしまったこととしては、Androidの署名作業です。
署名時のパスワードを間違えているのかなんなのか分からないですが、こんなエラーが。。

CommandInvokationFailure: Unable to list keys in the keystore. Please make sure the location and password of the keystore is correct. 
C:\Program Files\Unity\Hub\Editor\2018.3.7f1\Editor\Data\PlaybackEngines\AndroidPlayer/Tools\OpenJDK\Windows\bin\java.exe -Xmx4096M -Dcom.android.sdkmanager.toolsdir="C:/Users/yuma1217/AppData/Local/Android/Sdk\tools" -Dfile.encoding=UTF8 -jar "C:\Program Files\Unity\Hub\Editor\2018.3.7f1\Editor\Data\PlaybackEngines\AndroidPlayer/Tools\sdktools.jar" -

パスワード系は気をつけないと。。

やったこと

本当にどうしようも無くなったので、署名をする前のプロジェクトをGitHubからクローンし直して、再度build & run.

上のエラーは消えたけど

CommandInvokationFailure: Unable to install APK to device. Please make sure the Android SDK is installed and is properly configured in the Editor. See the Console for more details. 
C:/Users/yuma1217/AppData/Local/Android/Sdk\platform-tools\adb.exe -s "1KWPH804MG8256" install -r "C:\Unity\Project\Apk\main.apk"

stderr[
adb: failed to install C:\Unity\Project\Apk\main.apk: Failure [INSTALL_FAILED_VERSION_DOWNGRADE]
]
stdout[

]

前入れたアプリよりバージョンが下がってるよ!って怒られたよう。
Unityプロジェクトの名前を変更したから大丈夫って思っていたけど、違うよう。
初めて知ったけど、
BuildSettings > OtherSettings > Identification > PackageName
ここがアプリとしてのidになっているみたい。

UnityでAndroidアプリをビルドするには - フレームシンセシス

ここを変更した。
この際、Unity側での保存や、設定が上手くいってなかったのか分からないけど、OculusGoに同じアプリとして認識されて上手くいかなかったので、OculusGoをUSBで挿してadbコマンドを試した。(上エラー分に書いてあるコマンド)

adb install -r main.apk
一瞬でデプロイに成功。

Deploy後のエラー

Deploy自体はできるけど、エラーが2つ。2つ目のエラーは、1つ目から起因するものっぽい。

DeploymentOperationFailedException: No activity in the manifest with action MAIN and category LAUNCHER. Try launching the application manually on the device.
UnityException: Could not find any valid targets to launch on for Android

このエラーはAndroidのManifestっていう設定ファイルが何個か存在すると起こりうるよう.
Plugins > Android下にある場合は削除。 また、OculusのPluginによって、Unityのメニューからも削除が可能。

f:id:yuma1217:20190326000957p:plain
Unityのメニュー

デプロイやビルド系のエラーは精神がやられますね。 誰かの助けになれれば幸いです。

OVRPlayerControllerを使ってカメラが足元にめり込む問題の解決

問題

Unity上でOVRPlayerControllerを使って再生すると、 VR上でのカメラ位置を表すOVRCameraRigのy座標が-1になる。
つまり、頭の位置が-1下がってしまう問題が発生。
OVRPlayerControllerにはカプセルコライダーが付いているため、コライダーの一番底くらいにカメラが配置され、足元にめり込む現象が起きて困った。
ちょっと調べてみた。

調査

forums.oculusvr.com

まず、学びとして、OculusのForumで検索をかけてみるのが早いなって思った。
同じ問題が掲載されていた。

解決方法

OVRPlayerControllerにはUserProfileDataという設定項目がある。 f:id:yuma1217:20190316163015p:plain ここがデフォルトではTrueになっているが、Falseに変える事で、上記の問題を無くすことが出来る。

理由

なにが起こってるかちょっと見てみると、UserProfileDataはOVRPlayerController.csのUpdateControllerメソッドで使われていて、

if (useProfileData)
{
    // 一部抜粋
    var p = CameraRig.transform.localPosition;
    // OVRCameraRigに付いているOVRManagerの設定項目
    // デフォルトだとEyeLevelに設定
    if (OVRManager.instance.trackingOriginType == OVRManager.TrackingOrigin.EyeLevel)
    {
    // この文が現象の胆だった、コメントアウトすると問題が消える
        p.y = OVRManager.profile.eyeHeight - (0.5f * Controller.height) + Controller.center.y;
    }
    CameraRig.transform.localPosition = p;
}

CameraRigのlocalPositionを変更しているpというパラメータを追っていくと、
p.y = OVRManager.profile.eyeHeight - (0.5f * Controller.height) + Controller.center.y;
ここでy座標をがっつりいじってる。 デバッグをしてみると、

f:id:yuma1217:20190316165118p:plain

常にこの値が表示された。上の式に代入してみれば-1なので、問題はここだったんだろう。
OVRManager.profileを見てみると

Gets the current profile, which contains information about the user's settings and body dimensions.

Oculusユーザーの身体情報を取って使うデータのよう。
Oculusを始める際、身長などを入れたと思うけど、そのデータを使って、見え方を最適化できるものっぽい。
それがUnity側には存在しないから、今回のような問題が起こったのかなぁって思った。

まとめ

Unity側では常にUserProfileDataの項目は切っておいて良さそう。

OculusPlatformのSettingについて

概要

この記事では、OculusPlatformの設定方法について説明します。
OculusPlatformについて知りたい場合は、前回の投稿を是非読んでみてください。

なお、今回はsuinさんのこちらの記事を参考にして

https://twitter.com/suin/status/976704532163039232

HackMDを使って英語で書いてみてDev.toにPostしてから、記事を書いてみました。凄い画期的な方法だなと思ったので是非!(Google翻訳の日本語になってるので少々おかしいかも)

dev.to

設定

OculusDashBoard

  1. Oculus DashBoardに行く
  2. 新しいアプリケーションを作成します。
    アプリIDを取得できるので、メモしてください。


ここからは、OculusPlatformでマルチプレイ機能をテストするならば、読んでみてください。
テストユーザーを追加します。

  1. 左側のサイドバーで、[管理]> [ユーザー名]> [設定]> [ユーザーのテスト]の順に選択します。

  2. テストユーザーを追加します。パスワードと暗証番号を入力してください。送信します。
    後でパスワードを使用するのでメモ。

  3. テストユーザーが追加されました、Oculus IDとメールアドレスを書き留めてください。

  1. (重要)作成したテストユーザーを開発者として登録します。
    これを行わないと、テストユーザーに作成したアプリの資格が付きません。

  2. 設定 > メンバーを選択してから、「開発者を追加」を選択します。

  3. 作成したテストユーザーのOculus IDを入力し、追加を選択します。

これでテストユーザーが登録されました。

OculusPlatformの設定は完了です。


UnitySetting

  1. UnityのAssetStoreで、Oculus Integrationを入力してインポートします。

  2. トップメニューで、OculuPlatform> EditSettingsを選択。

  3. アプリIDを入力します。さっき取得したOculusDashBoardでの設定時の16桁の数字です。

    OculusDashBoardでOculusRift Appsを作成した場合は、OculusRiftAppId行にappIDを入力します。
    OculusDashBoardでGearVR / OculusGoアプリを作成した場合は、GearVRアプリID行に入力します。

  4. 次に、appId設定の下のUnityIditorSettingsを開き、テストユーザーのアドレスとパスワードを入力し、[ログイン]をクリックします。

    テストユーザーを使用しない場合は、自分のOculusアカウントのメールアドレスとパスワードを使用してください。
    またはUse Stand Platformチェックボックスをオフにします。チェックマークをはずすと、UnityはOculusApplicationを見つけて、アプリケーションにログインしているOculusユーザーの情報を使います。


設定が完了しました。

次回は、実際にUnityでのOculusPlatformAPIの使い方を書きます。

 
 

OculusPlatformについて

この記事はNotionというアプリを使って書いたものをコピーしています。
一押しのMarkdownクラウドメモサービスです。是非使ってみて欲しいです!!

www.notion.so

前回

OculusRoomsみたいなアプリを作りたくて、OculusPlatformを使ってみようとなったいきさつを書きました。

OculusRoomみたいなアプリを作りたい - 海外で生活したいXRエンジニアのブログ

今回はOculuPlatformのことはじめを書いていこうと思います。

概要

Introduction to the Platform SDK

The Platform SDK contains features that you can use to create engaging and social game experiences.

ドキュメントによると、OculusPlatformにはソーシャルゲームを作る事が出来る機能が含まれているようです。

使ってみての私の理解は、 OculusPlatformはBaaS(Backend as a Service)だと思っています。

本来サーバーが必要な部分をOculus側が用意してくれていて、機能ごとにAPIが用意されていて、

開発者は必要なAPIを利用するコードを書くだけで、ソーシャルVRを実現できる。

そのAPIを叩きやすくしたものが、OculusPlatformSDK。という認識をしています。

OculusPlatformの機能

OculusGo、特にOculus Rooms をイメージすると分かりやすいかもです。

  • ユーザーはアプリ内で写真を撮りFacebookに投稿することが出来る

    Sharing

  • ユーザーはアプリ内で友人を招待し一緒にゲームをプレイできる

    Rooms

  • ユーザーはアプリ内で音声通話をすることが出来る

    Voice Chat (VoIP)

  • ユーザーはアプリ内で世界中のユーザーとマッチングし一緒に遊ぶことが出来る(Dead and Buriedや、Bigscreen、Oculus Venueをイメージすると良いかもです)

    Matchmaking

これ以外にもOculusが用意してくれている機能をふんだんに使えます!

もちろん、OculusPlatformの機能を使用しなくても出来る機能はあると思います。

OculusPlatformの良いところ

通信をする相手を指定する際にOculusアカウントを簡単に使えるのは便利です。

多分他のネットワークライブラリなどを使う際には、通信する相手を指定するのはちょっと面倒、か、ユーザーにIDを打たせるなどのUIを与えてしまいそうな気がしました。

OculusPlatformの注意点

Oculusデバイスのみしか使えません。(OculusRift, GearVR, OculusGo, OculusQuest...)

Oculusデバイスのみを考えれば良くなるので私は良いな。と思ってました。

が、既存のマルチプレイのゲームをVR化したい、なんて時は使いづらいというかいらないですよね。

でも、そんな人でもOculusStoreに出したいなら、

絶対にやらなきゃいけない事があります。

Entitlement Checkは必須

https://developer.oculus.com/documentation/platform/latest/concepts/pgsg-unity-gsg/

Verifying that the user is entitled to your app is required to sell an app on the Oculus Store.

OculusStoreにアプリを売り出すには、提出するアプリのユーザーが、そのアプリを使う・持つ資格があるか(entitled)を検証する必要があるそうです。

なので、OculusPlatformの機能を使わなくとも、Storeに出したいなら EntitlementCheckは必須です。

OculusPlatform使ってみる

最初のセットアップは今回ざっくり書きます。

まずはOculusのOculus Developer Dashboardからアプリを作成します。

作った際のappIDというものを控えておく。

参考

https://developer.oculus.com/distribute/latest/concepts/publish-create-app/

↑のCreating an App Page参照

次に、Unityのセットアップをします。

UnityのAssetStoreからOculusIntegrationをインポート。

Unityの上のメニューから、OculusDashboardで作ったアプリのappIDを記述する。

参考

https://developer.oculus.com/documentation/platform/latest/concepts/pgsg-unity-gsg/

↑のConfigure Your Development Environment

かなり飛ばしましたが、これでOculusPlatformを使う下地は整いました。

EntitlementCheck

何か一つGameObjectを作成し、Startなどに下のメソッドを書きましょう。

using Oculus.Platform;

Start()
{
        // Oculus.Platform.Entitlementsのメソッドを呼ぶ
        Entitlements.IsUserEntitledToApplication().OnComplete(IsEntitledCallback);
}

Entitlements.IsUserEntitledToApplicationで、↑で設定したappIDに紐づいたアプリに資格があるかを検証します。

そして検証した結果が、IsEntitledCallback にMessageとして渡されます。

このCallbackは自分で記述する必要があります。

void IsEntitledCallback(Message msg)
{
    if (msg.IsError)
    {
        // EntitlementCheckに失敗した場合、msg.IsErrorがTrueになる
       // ①
        return;
    }
        // EntitlementCheckが成功
        // ②
}

1番について。

このエラーをハンドリングするようドキュメントにはあります。

また、アプリを終了するよう促す文言もありました。

You may not allow the user to proceed in your app after a failed entitlement check.

Sampleシーンでは、メソッドを呼び、そこでUnityEngine.Application.Quit()を実行し、アプリを強制終了していました。

2番について。

OculusPlatformを使って実行したいような処理を記述します。

例えば、アプリを使っているユーザーの情報を取得するメソッド

Users.GetLoggedInUser().OnComplete(GetLoggedInUserCallback);

ここのCallbackは割愛しますが、さっきと同じようにMessageをハンドルする処理を書くのみです。

次回

今回はここまでとします。

次回は、私はこれでも失敗したので、OculusDashboardの設定や、Rooms機能について書いていこうと思います。