クラスター開発者ブログ

Tunnelingによる移動ならVR酔いしないらしいから実装してみた

VR酔いしやすいエンジニアの溝口です。

VR酔いを軽減するために様々な移動方法が研究されています。

VR酔い防止に向けて。これまで登場したVRでの酔わない移動方法のアイデアまとめ。 | Mogura VR - 国内外のVR最新情報

今回はこの記事で紹介されているTunnelingを実装してみました。

サンプルプロジェクトはClusterVR/tunneling_locomotion_sampleです。Unityのバージョンは5.3.3p1です。

WASDで移動、QでTunnelingと通常の移動を切り替えます。

以下、実装の要点を紹介します。

3つのカメラとRender Texture

下記の3つのカメラを用意しました。

  • 現在地のカメラ: MainCamera
  • 移動先のカメラ: TunnelingCamera
  • 移動先を描画するカメラ: CanvasCamera

TunnelingCameraのTarget Textureに設定したRender TextureをRawImageに描画しています。

そのRawImageをCanvasCameraの子GameObjectとして配置して、移動先が最前面に描画されるようにします。

コード抜粋

Model-View-(Reactive)Presenter Patternを意識して実装しています。

PlayerというモデルにPositionやRotationのReavtivePropertyを持たせ、各プレゼンターからそれらをSubscribeしてプレゼンター自身のTransformやalphaを更新しています。

ユーザーのWASDキーによる移動やHMDに同期したMainCameraのRotationをPlayerモデルに渡すと、ReactivePropertyを通じてプレゼンターが更新されます。

Player.cs(抜粋)
1
2
3
public ReactiveProperty<MovingState> State { get; private set; }
public Vector3ReactiveProperty Position { get; private set; }
public QuaternionReactiveProperty Rotation { get; private set; }
MainCameraPresenter.cs(抜粋)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Start()
{
    var player = Player.Instance;

    player.State
        .Where(_ => player.IsTunnelingEnabled)
        .Where(s => s == Player.MovingState.Idle)
        .Subscribe(_ => transform.position = player.Position.Value)
        .AddTo(this);

    // ...

    // カメラのRotationをPlayerに渡す
    var mainCameraTransform = Camera.main.transform;
    this.UpdateAsObservable()
        .Subscribe(_ => player.RotateTo(mainCameraTransform.rotation));
}
TunnelingCameraPresenter.cs(抜粋)
1
2
3
4
5
6
7
8
9
10
11
12
void Start()
{
  var player = Player.Instance;

  player.Position
      .Subscribe(p => transform.position = p)
      .AddTo(this);

  player.Rotation
      .Subscribe(r => transform.rotation = r)
      .AddTo(this);
}
TunnelingDisplayPresenter.cs(抜粋)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Start()
{
    var player = Player.Instance;

    // ...

    var stateChangedStream = player.State
        .Where(_ => player.IsTunnelingEnabled);

    stateChangedStream
        .Where(s => s == Player.MovingState.Idle)
        .Subscribe(_ => fadeOut())
        .AddTo(this);

    stateChangedStream
        .Where(s => s == Player.MovingState.Move)
        .Subscribe(_ => fadeIn())
        .AddTo(this);
}

TunnelingによるVR酔いについて

Tunnelingによる移動ではVR酔いを全く感じませんでしたが、通常の移動より没入感はかなり損なわれていると感じました。

没入感とVR酔い軽減のトレードオフを考慮して、Tunnelingの採用を決める必要がありそうです。

参考

このエントリーをはてなブックマークに追加