VRChatワールド制作から始めるスタンドアロンVRゲーム開発【導入/入力の取得編】
この記事は「ョョョねこ Advent Calendar 2020」4日目の記事です。
こんばんねこ!私はゲーム制作サークル「sep-neko-ya」でプログラムをしているねこです。
今回は、VRChatワールドをSDK3で作っている人が「スタンドアロンVRゲームを作りたいな〜」となったときに、VRCSDKを使わなくてもスタンドアロンで動作するゲームの基本的な部分を作る、最初の一歩を踏み出すためのお手伝いをできればと思い、この記事を書きました。
対象読者
・C#をある程度分かっている (未確認飛行C / C#によるプログラミング入門 の表面的な部分くらい?私もわかっているかあやしいけど...)
・Unityをある程度扱える(ゲームやVRChatワールドなどを自分なりに完成させたことがあるなら心配ないかも)
準備物
・Windows PC (お手持ちのHMDの動作スペックを満たすもの)
・HMD (HTC VIVEシリーズ, Valve Index, Oculus Riftシリーズ, Oculus Questシリーズ + Oculus Link)
※この解説記事で使っているデバイスは Oculus Quest + Oculus Linkです
・Steam, SteamVRのアプリ
・Unity 2019.4.9f1
自己紹介
最初に私は何をした人(人?)かを書いておきます。
VRChatで作ったゲームワールドをSteam VR Pluginを用いてWindows向けのスタンドアロンゲームに移植しました。ゲームはVR / Desktop両対応となっています。内容は以下から確認できます。
VRChatワールドをスタンドアロンのゲームとして頒布するために、VRCSDKに依存しないアプリとしての作り直しを行いました。
今回はVRリズムゲームに焦点を当てた話ではなく、特にどのVRゲームでも必要そうなところを解説します。
VRChatが提供してくれる機能の確認
VRChatはVR開発のための色々な機能を提供してくれています。ぱっと思いつくところで言えば...。
1. HMDごとの差異を吸収したトラッキングや入力(VIVE, Index, Oculusとかをある程度一緒に扱える)
2. 自動キャリブレーション
3. 移動と回転システム
4. アバターシステム(好きなアバターを着ることができる)
5. ワールドに関わらず共通で使えるUIパネル(コントローラ入力との連携)
6. ピックアップシステム
7. ネットワーク
など...
VRChatプレイヤーにとっては、これらの機能は提供されているのが当たり前すぎて、自分でゲームを作るとき、実装する必要性に気づかないくらいではないでしょうか。
上記の2以降はゲームによって必要かどうかが分かれるため、今回の記事では1までの実装を考えていきます。
今回の記事で重要なのは、VRゲーム制作は(VRCSDKに依存しなくても)意外ととっつきやすいと感じていただくことではないかと思います。
開発環境
普通に開発するときは、開発環境の検討から始めるとよいと言われていますが、VRChatワールド開発環境がUnityなので、その流れで今回のゲームもUnityを使って制作するとします。
HMDごとの差異を吸収する(Steam VR Pluginの導入)
Steam VR Pluginとは
Unityを使ってゲーム制作するとき、どのようにVR機器を扱うかというと、手っ取り早いのは「Steam VR プラグイン」を使うことです。(観測範囲なので裏は取ってませんが)デフォルトでOculus, VIVE, Indexがサポートされています。少なくとも自分のゲームではハードウェアごとの分岐を書きまくることはありませんでした。
他にも、最初から用意されているPrefabを置くだけでHMD, ハンドコントローラのトラッキングをやってくれたりするといった、非常にありがたい機能も提供されています。
入手方法
このプラグインはUnity Asset Storeから入手します。ここからはUnity 2019.4.9f1 準拠で解説します。VRChatの開発をするバージョンのUnityは少し古いため、新しいものを使っていきましょう。
以下、Pluginの導入手順です。
1. 3Dで新しくプロジェクトをつくる
2. UnityのAsset Storeタブを開き、「steam vr」で検索
3. ダウンロードしてインポート
Unityプロジェクトの初期設定
上の画像のようなウインドウが出てくるので、最下部のボタン「Accept All」を押します。
後でこのおすすめ設定の中のいくつかには逆らいたくなるかもしれませんが、特にこだわりのない部分は最初に一括で設定にしてもらいましょう。
HMDとコントローラの初期設定
まずは、Unityを起動したときに生成されている「SampleScene」で、SteamVRプラグインを使ったトラッキングを確認してみましょう。
1. あらかじめ、PCに自分のHMDを接続し、SteamVRを起動しておく
2. Assets/SteamVR/Prefabs/[CameraRig] をシーン「SampleScene」のヒエラルキーにドラッグ&ドロップ
3. Play ▶ ボタンを押す(以下の初期設定ができていないなら、まだHMDを被る必要はないです。設定できているなら被ってください)
--ここからは最初に[CameraRig]をシーンに入れてPlayボタンを押したときの設定です--
i. ダイアログが出てくるので(3, 4回くらい?) Yesを押す。
ii. Steam VR Inputウインドウが出てくる(出てこなければ、メニューバーの Window > Steam VR Inputから開ける)
iii. Steam VR Inputウインドウ中央下の「Save and generate」を押す(シーンのセーブとリロードを指示されるので、それ同時に行います)
※ ii, iiiの手順はSteamVR Plugin経由でそれぞれの機種のコントローラの入力(どのボタンが押されたかなど)を取れるようにしてくれています。
iv. 今度はHMDを被って、再度Play ▶ ボタンを押す(Steam VRのメニューが出てたら消してください)
--最初に[CameraRig]をシーンに入れてPlayボタンを押したときの設定終わり--
5. HMD, コントローラのトラッキングが取れていることが確認できる
トラッキングが反映されているゲームオブジェクト
どのゲームオブジェクトにトラッキングが反映されているのかを確認しておきましょう。
Steam VR Pluginの[CameraRig]以下のゲームオブジェクトを使うと、Udonの関数であるNetworking.LocalPlayer.GetTrackingData() を使ってプレイヤーの座標を取るみたいなことをやらなくても、勝手に毎フレームトラッキングをしてくれます。
・HMDの位置
[CameraRig]/Camera が対応します。Cameraコンポーネント最下部の「Target Eye」が「None (Main Display)」以外の時に、自身の位置と回転をHMDの頭と同様の値に更新してくれているようです。
・両手の位置
[CameraRig]/Controller (right) もしくは Controller (left) が対応します。こちらは「Steam VR_Behaviour_(なんとか)」というスクリプトによりトラッキングされているようです。
ここでは「Steam VR_Behaviour_Pose」スクリプトの、Input Sourceに指定された場所がトラッキングされています。
Main Cameraの削除
[CameraRig]/CameraにはAudio Listenerコンポーネントがついています(Audio Sourceから発生する、ゲーム中のBGMや効果音を聴けるやつ)
そして、シーンにデフォルトで配置されているMain Cameraにも同じコンポーネントがついています。
同一シーンにAudio Listenerが2つある場合、コンソールにずっと警告が表示されて邪魔です。
また、(この記事の内容の範囲においては)同じシーンにカメラが2つあっても使わないので、シーンにデフォルトで置いてあるMain Cameraの方をHierarchyから右クリック > Deleteで消します。(Audio Listenerごと消えてくれます)
※ (わけあってCameraを2つ以上使いたいときとかは、Main CameraにアタッチされているAudio Listenerコンポーネントだけを消してもいいと思います)
コントローラから入力を受ける
ゲームを作る場合、プレイヤーのボタン入力などを受けたくなることがややあります。
キーボードやゲームパッドからの入力を取る分には、UnityEngine.InputのStatic関数(GetKeyDownとか)を呼べばいいわけですが、VR機器のコントローラの入力を受ける場合、(ここでは)Steam VR Pluginに用意されているものを使います。
関数は、先述したUnityEngine.Input.GetKeyDown関数みたいな雰囲気で使えてしまいます。
トリガーが引かれたことを読み取るプログラム
今回はコントローラのトリガーが引かれたらUnityのConsoleにログを出すプログラムを書いてみましょう。
1. シーンにGameObject「SteamVRInputManager」を作成
2. C#スクリプトとして「SteamVRInputManager.cs」を作ってアタッチする
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SteamVRInputManager : MonoBehaviour
{
public Valve.VR.SteamVR_Input_Sources anyController;
void Update()
{
if (Valve.VR.SteamVR_Input.GetStateDown("default", "InteractUI", anyController))
{
Debug.Log("ョョョねこがAnyControllerのトリガーが引かれたことをお知らせしますねこ!!!");
}
}
}
3. 上記スクリプトを入力して保存。
4. Inspectorから、GameObject「SteamVRInputManager」のAny Controllerを「Any」に指定
5. HMDを被ってPlay ▶ ボタンを押す
6. ゲームのPlay中にどちらかの手のトリガーを何回か引く
7. トリガーを引くたびにConsoleに指定した文字列が出現していることを確かめる(余談ですがアドベントカレンダーのテーマは回収しました!)
Valve.VR.SteamVR_Input.GetStateDown関数の解説
UnityEngine.Input.GetKeyDown関数と同じような考え方で利用できます(ボタンがOFF -> ONになった瞬間にtrueを返し、それ以外の時はfalseを返す)
やや特殊なのは引数なので、そちらを解説します。
第1引数: string actionSet / 今回は"default"
Steam VR Inputには、Action Setsという考え方があります。いくつかのコントローラの入力をまとめたグループみたいな認識でいいと思います。
今回は初期設定のときに生成された"default" Action Setを参照したいと思いますので、文字列で"default"を指定します。
※ "default" Action Setの他の中身はSteam VR Inputウインドウから確認できます。
第2引数: string action / 今回は "InteractUI"
初期設定のときに生成された"default" Action Setでは、だいたいのコントローラにおけるトリガー部分を押す操作が"InteractUI"に設定されています。
今回は、トリガーの入力を取りたいので、文字列で"InteractUI"を指定します。
※ 独自に設定したい場合は、Steam VR Inputウインドウの「Open binding UI」から設定してください。詳しい設定方法は以下の記事の #コントローラーの入力について が参考になります。
第3引数: Valve.VR.SteamVR_Input_Sources inputSource / 今回は anyController
どのコントローラからの入力を受け付けるか指定します。
事前にインスペクタから、変数anyControllerを「どのコントローラからの入力も受け付ける」Anyという設定にしました。
そのため、今回は左右どちらのコントローラのトリガーを引いても、そのフレームにはGetStateDown関数からtrueが返ってきます。
※ 試しに、シーンにあるSteamVRInputManagerがアタッチされているオブジェクトをクリックして、インスペクタからAnyControllerをLeft Handとかに変更してゲームを実行してみると、左手からしかトリガー入力を受けなくなります。
ということでトリガー入力が受けられました!
いかがでしたか?
VR機器への映像出力や、VR機器からの入力を受けることは、Steam VR Pluginによって簡単にできるようになっています。
VRChatワールド制作でUnityを触っている方は、スタンドアロンVRアプリ開発の準備ができているようなものだと思うので、この機会に試してみると楽しいかもしれません。
参考記事
【導入編】
今回の記事では、ほとんど上記記事のリプレイみたいなことをやっています...。基本的なことから、コントローラを使った移動などの応用例も掲載されているので、こちらのチュートリアルもおすすめです。
サンプルシーンで遊んでみるための説明が参考になります(Steam VR Pluginにはデフォルトで遊べるミニゲームっぽいものが付いています)
【入力の取得編】
この記事で提示した入力の取得方法と別の方法が記載されています。気に入ったアプローチで取得すればいいかと思われます。