simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity:オンラインVRM TPSゲーム作りログ5

■前書き
今回でこの連番タイトルもラストになりそうな進捗でてます。

前回の記事はこちら
simplestar-tech.hatenablog.com
要約すると
1.送信するタイミングとデータ構造を知るための調査
2.送受信のRPCインタフェースを設計
3.実際に送受信できていることを確認
でした。

今回は最後の繋ぎこみ部分、これがうまくいけばキャラクターはレイテンシが小さい状態で同期してアニメーションしてくれるはず。

■キャラクターの移動と回転
速度と角速度の適用

送られてくるデータを適用するには何かしらのキーでゲームオブジェクトを見分けないといけません。
プレイヤー名は自由に決められてしまうので、GUID を生成してから、それを整数値にすることで、これをプレイヤーIDとしました。
重なったら奇跡としましょう。

GUID の生成と文字列にするフォーマットまではここを参考にし
C#でGUIDをToStringするときに使用できる書式 - PG日誌

文字列から int 値を作る書式はここを参考にしました。
C# GUIDをシードにして整数の乱数を作る - 備忘録

ついでに Unity 2019.1.1f1 に更新して作業再開

最初はキーマップに VRM インスタンスを登録して、これを参照しつつ、受け取った位置と回転を適用するサンプルの動作を見てみます。
動きは完璧でした。

補間をどうするか考える必要があります。

■キャラクターのモーションの再生
CrossFace できることは知っているので、これを実装して動きを見てみましょう。
送受信のインタフェースは完成しているので、実際に送受信するコードだけは書いてしまいましょう。
書きました。

受け取ったモーションパラメータの適用を書いていきます。(ローカルで動作確認済みなので、期待できる)
書きました。

動作確認してみます。
念のため、受信しているかのログも記載しておきます。

動作の同期は完璧でした。
あとは、ログを取り除いて、送受信する頻度を state 変化は毎フレーム適用するようにしてみます。

悲しいことにログ吹っ飛んじゃった…

なんやかんやで、リアルタイム通信同期できるようになりました。

作業ログはここまでとします。

■補間
これは記録すべきだなぁと後から思い出したのでここに書くことにします。

using UnityEngine;

public class TransformInterpolation : MonoBehaviour
{
    internal Vector3 position;
    internal Quaternion rotation;

    [SerializeField] float acceleration = 0.2f;
    [SerializeField] float convergence = 0.9f;
    [SerializeField] float warpDistance = 1.5f;
    [SerializeField] float rotateSpeed = 500f;

    float velocity = 0;

    void Update()
    {
        transform.position = Vector3.MoveTowards(transform.position, position, velocity * Time.deltaTime);
        var distance = Vector3.Distance(position, transform.position);
        if (0.001f > distance)
        {
            velocity *= convergence;
        }
        else
        {
            velocity += acceleration;
        }
        velocity = Mathf.Clamp(velocity, 0, 5);
        if (warpDistance < Vector3.Distance(transform.position, position))
        {
            transform.position = position;
        }        
        transform.rotation = Quaternion.RotateTowards(transform.rotation, rotation, rotateSpeed * Time.deltaTime);
    }
}