最近VR開発に興味があって、VR開発をやってみようかなと思いましたので簡単なゲームを作ってみました。 今回作るゲームは簡単なFPS(First Person Shooting)ゲームです。
開発に必要なものはQuest 2 HMDとWindowsパソコンです。
使っているソフトウェアはUnityとOculus Integrationのプラグインです。
環境準備
Unityをインストール
Unityは公式サイトからダウンロードできます。
Oculus Integrationパッケージ準備
UnityからOculusのHMDでゲームを実行できるようにOculusIntegrationパッケージが必要です。
このパッケージはUnity Asset Storeからダウンロードできます。
まずは以下のリンクでOculus Integrationを導入する。
https://assetstore.unity.com/packages/tools/integration/oculus-integration-82022
導入が終わったらUnityでインストール必要がありますので、Unityでのパッケージマネージャからインストールできる。
OculusのHMDと接続するためにOculus Softwareも必要なのでご確認ください。
CameraRigを追加
Oculus Integrationをインストールできたら、プロジェクトでCameraRigを追加しましょう。CameraRigはHMDのオブジェクトです。Playerのオブジェクトと考えるといいです。
CameraRigを追加するために、Assets > Oculus > VR > Prefabs
のフォルダの中からOVRCameraRig
をプロジェクトのHierarchyウィンドウに置いたら完成です。
コントローラー対応追加
OVRCameraRig
だけ追加したらまだコントローラを使えないので、OVRControllerPrefab
を追加しましょう。OVRControllerPrefab
はAssets > Oculus > VR > Prefabs
にあって、OVRControllerPrefab
のフォルダの中から使いたいコントローラーのPrefabを見つけて、右と左コントローラのPrefabをOVRCameraRig > LeftHandAnchor > LeftControllerAnchor
とOVRCameraRig > RightHandAchor > RightControllerAnchor
に追加します。
C#でのコントローラーの操作
コントローラーをゲームで使うためにC#のスクリプトを書くことが必要です。以下は基本の操作になります。
コントローラーの位置を取得
// Vector3
OVRInput.GetLocalControllerPosition(OVRInput.Controller.RTouch)
// Quarternion
OVRInput.GetLocalControllerRotation(OVRInput.Controller.RTouch)
コントローラーの操作の例
//A ボタンを押すと何かする
if(OVRInput.GetDown(OVRInput.RawButton.A)) {
//何かする
}
全ての参考公式のドキュメンテーションから見れます:
https://developer.oculus.com/documentation/unity/unity-ovrinput/
注意点
プレイヤーの下にPlaneがないとプレイヤーが落下してしまいますので、そうなった場合はプレイヤーの下にPlaneを追加してください。
ゲームの実装
物を掴む
銃を実装する前に物の掴むも実装しなければならないですね。
以上のOVRControllerPrefab
は、ものの掴みの実装がないため、ものを持てないことになります。なので今回はCustomHands
を使います。CustomHands
を使ったらものを掴めるようになります。
物を掴むことに必要なスクリプトはOVRGrabber
とOVRGrabbable
で、OVRGrabber
は手元に付けるものでOVRGrabbable
は掴みたい物に付けることです。
Oculus Integrationの中に実装されたOVRGrabbable付き手のサンプルがあって今回はそれを使っていきます。
現バージョンそのサンプルはOculus > SampleFramework > Core > CustomHands
にあるCustomHandLeftとCustomHandRightです。
CustomHandLeftとCustomHandRightをOVRPlayerControllerのLeftControllerAnchorとRightControllerAnchorに入れば完了です(すでにコントローラがある場合はコントローラを消すこと、Handはコントローラの一種なのでコントローラとして使います)。
以上の手の実装があったら、掴めるオブジェクトを作りましょう!
掴みたいオブジェクトに必要なのはOVRGrabbableスクリプトとRigidbodyコンポネントなので、この2つのコンポーネントを掴みたいオブジェクトに追加したら掴めることになります。
射撃の実装
VRの基本の操作が実装できたら弾の実装をしましょう。
まず、弾のprefabが必要です。prefabとは単にいうとテンプレートみたいなものです。
弾のprefabは簡単なキューブかスフィアでもいいので、今回はスフィアで作ります。
これでスフィアが作成されたが、まだprefabではないです。
prefabにする方法はHierarchyビューからスフィアを引っ張ってProjectビューに置くと完成です。
prefabを作成できたらHierarchyビューからスフィアを消してもいいです。
次はさっき作成したprefabを名前を”Bullet”と付けて、以下のコードをシーン内のGameObjectのスクリプトに書く。今回はGameControllerといういろいろなものを処理するオブジェクトを作成して使います。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameController : MonoBehaviour {
public GameObject projectile;
void Update() {
}
}
以上のようにpublic GameObject projectile;
を作って、ProjectビューからInspectorビューにあるGameControllerのスクリプトのProjectile欄にBulletを引っ張って置くとコードでこのprefabを使えます。
弾を撃つため、コードのUpdate()
内でキーの判定を書いて、以下のように実装する。
void Update() {
//テストのため今はキーボードのSpaceキーを使う
if(Input.GetKeyDown(KeyCode.Space)) {
GameObject bullet = Instantiate(projectile, transform.position, transform.rotation) as GameObject;
bullet.GetComponent<Rigidbody>().AddForce(transform.forward * 500);
}
}
以上のコードでテストのためまだVRのコントローラー使用しないが、キーボードのSpaceキーを使います。
以上のように実装すればゲーム内でSpaceキーを押すとGameControllerの位置から弾が出るはずです。
銃と使うために、銃のオブジェクトを作ってオブジェクトのスクリプトに以下のように書きます。
void Update() {
if (transform.GetComponent<OVRGrabbable>().isGrabbed && OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger)) {
GameObject launchPos = gameObject.transform.GetChild(0).gameObject;
GameObject bullet = Instantiate(projectile, launchPos.transform.position, launchPos.transform.rotation) as GameObject;
bullet.GetComponent<Rigidbody>().AddForce(launchPos.transform.forward * 500);
}
}
以上のif文では、「銃が掴められるとボタンを押される」とチェックしますので、そうすると弾が出ます。
敵の動きの再生成
敵が動かないと簡単すぎるでしょう。なので簡単な敵の動きを作りましょう。
まず、敵のスクリプトのUpdate()関数の中に以下を書きます。
void Update() {
transform.Translate(Vector3.back * Time.deltaTime);
}
敵はプレイヤーと同じ方向に向いているのでVector3.backを使います。しかし敵は逆向きになったらVector3.forwardを使います。
次は敵を倒したときにリスポーンするため、オブジェクトを破壊する直前新しいオブジェクトを再生するといいです。
private void OnCollisionEnter(Collision other) {
if(other.gameObject.tag == "Bullet") {
hp -= 25;
hpText.text = "" + hp;
}
if(hp == 0) {
float spawnX = Random.Range(-4, 4);
GameObject enemy = (GameObject) Instantiate (gameObject, new Vector3(spawnX,0,7), Quaternion.identity);
Destroy(gameObject);
}
}
Destroy(gameObject)
の前に
float spawnX = Random.Range(-4, 4);
GameObject enemy = (GameObject) Instantiate (gameObject, new Vector3(spawnX,0,7), Quaternion.identity);
を書いたら敵を倒したとき、X軸でリスポーンすることになります。