Skip to content

103 1 力学テレポーター

miuccie-miurror edited this page Jul 26, 2017 · 2 revisions

002 - VRテレポーター

ここからはVR空間における効果的な移動方法をいくつか実装してみます。 VIVEのようなルームスケールの位置トラッキングが可能なVRシステムでも、 広い空間を表現するためにはプレイヤーが歩き回る以外の移動方法も提供しなくてはなりません。

現在各VRアプリで色々な移動方法が模索されていますが、その中で最も大事なことはVR酔いを避けるということです。 VRではプレイヤー自身の動きに逆らってプログラムから強制的に視界を動かすとすぐに酔ってしまいます。 この点に注意しながら、いくつかの移動方法を試してみたいと思います。

3-1. 力学テレポーターの実装

まずは、物理空間に更なる臨場感を出すために高さの概念を取り入れたいと思います。 ルーム内を歩き回るプレイヤーの位置を検知して、足元に乗り越えられる段差があった場合、HMDの視界の高さを変更します。 さらに高いところから足を踏み外した場合は、自然落下する処理も入れてみたいと思います。

** 今回のサンプルシーンの場所 **
https://github.com/yumemi-inc/vr-studies/tree/master/vol1/VR-studies/Assets/VR-studies/3_VR-teleporter/3-1_PhysicsTeleporter


新規シーンの構築

新規シーンを作成したら、まずは前章の通りSteamVR Pluginをインポートして、[CameraRig]プレハブを配置後、MainCameraを削除します。


ステージの作成

まずは床を作ります。シーンに適当な大きさで3D Object/Planeを追加してください。

次にCubeで階段を作ります。この際人が上に乗れるぐらいのサイズ感で作成します。 さらにここではY位置を0.3=30cmずつ高くして、段々と登っていけるようにしてみました。


PhysicsTeleporter.csの作成

次にプレイヤーの位置をトラッキングするスクリプトを作成します。 Projectパネル内にPhysicsTeleporterという名前でC#Scriptを作成し、Hierarchyパネル上の[CameraRig]オブジェクトにアタッチします。

まずはSteamVR PluginよりHMDデバイスの参照を取得して保持しておきます。

SteamVR_Controller.Device head = null;

public float climbableHeight = 0.5f;

bool  isFalling = false;
float fallEndY = 0;
float fallVeloY = 0;

void Start () {
  head = SteamVR_Controller.Input( (int) SteamVR_TrackedObject.EIndex.Hmd );
}

次にFixedUpdate()の中で、HMD=プレイヤーの頭の位置を常時トラッキングします。

足元のオブジェクトの検出は、現在のHMDのXZ平面上の位置から真下にレイキャストして、最初にヒットしたオブジェクトとします。 このとき、その検出位置がプレイヤーのいるY位置よりも高く、乗り越えることが可能な高さの場合には、スクリプトからY位置を変更します。

ここでこのスクリプトは[CameraRig]オブジェクトにアタッチされており、通常this.transformは常時(0, 0, 0)となりますが、 この座標を変更するとその分HMDやコントローラーの座標全体が相対的に移動されることをうまく利用すると良いでしょう。

また、プレイヤーがいる現在の位置よりも低いY位置が検出された場合は、落下のアニメーションを実行するようにします。

void FixedUpdate () {

  // 現在の足元+頭のY位置を算出
  Vector3 eyePos = transform.position + head.transform.pos;

  // 真下へレイキャストして障害物の高さを調べる
  RaycastHit hitInfo; 
  bool hasHit = Physics.Raycast ( eyePos, Vector3.down, out hitInfo, 100 );
  if ( hasHit ) {

    var offsetH = hitInfo.point.y - transform.position.y;
    if( isFalling == false ){

      // 相対的に登れる高さの場合は、足元のY位置を移動
      if ( offsetH > 0 && offsetH <= climbableHeight ) {

        transform.position = new Vector3 ( transform.position.x, hitInfo.point.y, transform.position.z );

      // 落下時は重力アニメを開始
      } else if( offsetH < 0 ) {

        fallEndY = hitInfo.point.y;
        fallVeloY = 0;
        isFalling = true;
      }
    }
  }
}

最後にUpdate()の中で、落下のアニメーションを処理します。

void Update () {

  // 落下中の場合は重力アニメを付加
  if( isFalling ){
    fallVeloY += (Physics.gravity.y * Time.deltaTime);
    var newY = this.transform.position.y + fallVeloY * Time.deltaTime;
    if( newY <= fallEndY ){
      newY = fallEndY;
      isFalling = false;
    }
    this.transform.position = new Vector3( transform.position.x, newY, transform.position.z );
  }
}

シーンの実行

シーンを実行し、実際に階段を登ったり降りたりしてみてください。 乗り越えられる高さの場合は視界の高さが変更され、踏み外すと自然落下することが確認できたでしょうか。

以下のサンプルシーンでは、さらにユーザーの足元に影を表示して現在のY位置を分かりやすくする処理などを入れてあります。

** 今回のサンプルシーンの場所 **
https://github.com/yumemi-inc/vr-studies/tree/master/vol1/VR-studies/Assets/VR-studies/3_VR-teleporter/3-1_PhysicsTeleporter