ハツェの真時代傾向璋

興味を持ったことを書いていく鱗片的な場所から先の未来の場

動く床を作って見た

こんばんは。ハツェです。
当記事は、VRChat Advent Calendar 2020の記事として参加しています。
今回は、一時期話題になった某なんとかソード床にならない手法を探ってみようという内容を書いていこうと思います。
今回の動作環境は、Unity : 2018.4.20f1、VRCSDK : 2020.12.09.04.44、U# : v0.18.8です。

目次


前置き

一般的な移動床の作成手法

通常、VRChatで何も考えずに床だけを動かすと、以下のように床の上で立ち止まることが出来ずにツルっと滑り落ちる床が完成します。
f:id:hatuxes:20201219154546g:plain
これを回避し、移動しなくてもプレイヤーが床と一緒に移動する方法はないかというのを模索します。

VRChatの制限が厳しい

通常、Unityで移動床を作る方法を検索すると、以下の方法が出てくることが多いです。

  • 乗った時にプレイヤーを床の子にする
  • プレイヤーのtransformを床と合わせて移動させる

ですが、Udonではプレイヤーのtransform自体を取得することも代入することも出来ないため、上記の方法で再現することは厳しいです。
そのため、今Udonでサポートされている関数を用いてどうにか対策していく必要があります。

本題

では、ここから実際に試してみたこととその結果について書いていこうと思います。
今回、床はスクリプトで記述している物理演算に沿って移動しています。

private void FixedUpdate()
{
    // 床の移動
    if (_isReverse)
    {
        Vector3 toVector = Vector3.MoveTowards(transform.position, _startPosition.position, _speed * Time.deltaTime);
            
        _rigidBody.MovePosition(toVector);

        if (Vector3.Distance(transform.position, _startPosition.position) < 0.1f)
        {
            _isReverse = false;
        }
    }
    else
    {
        Vector3 toVector = Vector3.MoveTowards(transform.position, _endPosition.position, _speed * Time.deltaTime);
            
        _rigidBody.MovePosition(toVector);
            
        if (Vector3.Distance(transform.position, _endPosition.position) < 0.1f)
        {
            _isReverse = true;
        }
    }
}


コライダーで囲んでみる

発想

最初の発想。動く床の子にコライダーの壁を用意すればなんとかなるでしょと言う発想です。こんな感じ。
f:id:hatuxes:20201219024406p:plain

結果

f:id:hatuxes:20201219024426g:plain f:id:hatuxes:20201219165442g:plain ダメでした。乗った後操作しないでいたら、ブルンって感じでコライダーから押し出されたような挙動で落とされました。
単純にコライダーで押してもらうっていう方法は厳しそうですね。

メリット

  • 特に複雑なことを用意する必要がない

デメリット

  • そもそもちゃんと機能してない
  • 複雑な形の床には使えない


椅子を使って移動させる

発想

VRChatでアバターを無理やり動かすときに使う椅子。椅子に座らせて無理やり動きを同期すればいいんじゃないかという発想です。

結果

f:id:hatuxes:20201219170009g:plain f:id:hatuxes:20201219034721g:plain まぁ予想通りの結果ですよね。座ると位置が固定されるので床と合わせて動いているという感じです。
知見が溜まったので少し訂正。
VRC_Stationのseatedのチェックを外すと立ったまま座標が固定できるみたいなので、その状態で入力に合わせて椅子を移動させてあげればもう少しまともなものが出来ると思います。

メリット

  • 床の子に椅子を置くことでほとんど実装できる

デメリット

  • 床の上で移動することは出来ない
  • アニメーションを変えてあげないと、見た目が不格好


テレポートで逐次移動させる

発想

ここからUdonらしい発想。移動床の中心座標に毎フレームテレポートさせればそれっぽく見えるのではないか?という発想です。

結果

f:id:hatuxes:20201219170507g:plain f:id:hatuxes:20201219035923g:plain 一応見た目通りにはなります。直立不動で動いているのはなかなかシュールです。
サンプルとして作ったテレポート式移動床は、ジャンプでのみ脱出出来るようにしました。

メリット

  • しっかりと床に合わせて移動してくれる
  • 複数人で乗っても問題ない
  • 移動による脱出方式にしなければ、移動キーを押しても床から落ちる心配はない

デメリット

  • 実装方法を考えて作らないと脱出不可能になる
  • テレポート自体が全体の通信を喰うので、毎フレーム行うと他の処理が全部ダメになる(GiFの後半の床の同期がガバガバになっている)


速度を与える

発想

移動床で良くある実装のうちの一つ。現在の座標と一個前の座標から速度を計算して、その分だけプレイヤーに力を与えるという発想です。

結果

f:id:hatuxes:20201219164052g:plain f:id:hatuxes:20201219040949g:plain かなりいい感じになりました。

メリット

  • 移動が自由に出来る
  • 複数人で乗っても問題ない
  • 同期に負荷をかけずに出来る

デメリット

  • 常に移動状態になる
  • ジャンプが出来ない
  • 移動速度が遅くなる
  • 常にジャンプ状態のモーションになる


追記

ジャンプ出来ることが発覚しました。
VRChatでは、ジャンプをプレイヤーに速度を与えることで実装していたみたいです。
なので、現在のプレイヤーの速度状態を甘味した処理内容にしてあげればプレイヤーを動く床に追従させながらジャンプさせることが可能のようです。
詳細はGithubコードをご覧ください。

あとがき

色々試して来ましたが、個人的には最後のSetVelocityでプレイヤーを押すという方法が一番良いんじゃないかなと思います。
速度を与える方法なら、床の移動をスクリプトで制御してもアニメーションで制御しても対応できます。あと、床の形が何であっても対応できますね。
さらには、Teleportのような通信を喰うみたいなことも無さそうなので現状はこれかなという感じでした。
他にも良い方法がありましたら、コメントで教えて頂けると幸いです。

今回使用したサンプルプロジェクトはGithubで公開しております。ライセンスを確認の上、お使いください。
github.com
以上になります!
VRChat Advent Calendar 2020の明日の記事は、せちろーさんの記事になります!お楽しみに!!