simplestarの技術ブログ

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

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

■前書き
前回の記事はこちら
simplestar-tech.hatenablog.com

前回の内容をまとめると
1.扉のあるチュートリアル閉鎖空間を用意し、VRMファイル選択で動的に VRM キャラクターを読み込める
2.扉の前でアクションを行うとAWS Cognito のサインアップ・サインインが行える
3.サインイン後は S3 に VRM をアップロードし、プレイヤー名を決めて Magic Onion のサーバーに入室する
4.二人目のプレイヤーが入室した通知で S3 オブジェクトキーを取得し、これを S3 からダウンロードしてシーン内に二人目のプレイヤーの姿が表示される
5.プレイヤーに外部から Animator パラメータを与えることで自由に操作できることを確認
6.Animator から同期するべき情報を取得できそうな関数を前調査、報告

といったものでした。

今回は、前調査でわかっている Unity の Animator のパラメータの変化を観察して送受信するインタフェースを設計します。
観察→理解→想像→試行→観察 のループを高速で回して、機械が完成する感じのことします。

■横道
本筋とは関係ないけど、気になるのは S3 アップロードは即時完了しないので、可能なら Magic Onion サーバーに入ることを決めたら
そのときはログイン処理中とかのメッセージを出し、S3 アップロード完了と同時にサーバールームに入室を決めたい

あとは、S3 アップロード権限が無いとかで失敗したり、サーバーに入れなかった時などはその旨を表示して、ローカルのゲームに戻ってこれるつくりも入れてあげたい

一般公開したときは、ルーム最大数を越えたら、その旨で弾かれるつくりとかもいいね
あと、チャット機能と表情やエモーションアクションを送れるメニューと機能も欲しいところ

■本題
Animator のパラメータの変化を検知して、これを報告するプログラムを書いてみます。
期待通り動くかな

layer は 5つ、そのうち、アクションをしている最中は State の nameHash 値が -1903714832 のときローリング
直立ロコモーション時は 1905792070 の値になることを確認

hash 値を与えて CrossFade できるか確認します。
できました。

気づいたことに、Invector のコンポーネントの割当たっている VRM キャラに CrossFade させると移動し、そうでない VRM キャラはその場でアニメーションしました。
Root Motion の違い?
yes

ただ、単に apply root motion にチェックを入れると、アイドル時に振動してしまうので、何らかのスクリプト編集が必要そう

そうだ、 Animator.applyRootMotion も同期するステートにしてしまおう
動的に切り替えて、期待通りのモーションとなることは確認できた。

パラメータ、レイヤーごとのステート、あとは位置と回転と速度などですね
これも Animator から取れるのでしょうか

取れますが、OnAnimatorIK とか OnState とかの関数内で呼べと怒られました。

Debug.Log($"bodyPosition = {this.debugAnimator.bodyPosition}");
Debug.Log($"rootRotation = {this.debugAnimator.bodyRotation}");
Debug.Log($"velocity = {this.debugAnimator.velocity}");
Debug.Log($"angularVelocity = {this.debugAnimator.angularVelocity}");

速度や角速度が取れるのはいいですね、これなら毎フレーム送らなくても滑らかな補間が行える?

この関数はいったい
yutakaseda3216.hatenablog.com

無知だったので、落とし穴に気付かせてもらい、助かりました。

OnAnimatorIK 関数はどうでしょう。
Animator に IK レイヤーを一つ加えると、呼び出してもらえるようになり警告が消えました。

小ゴールとして、この関数でモニタリングしつつ、頻度を調整できるようにしつつ、前回ブロードキャストした状態からの差異をまとめて列挙できる仕組みを書いてみます。

定期的ってのは FPS とかで調べると解決
OnAnimatorIK は 50 FPS であることもわかった。

アニメーションが設定されていると Body の位置が移動する
Distance 計算して余白を求めればゆるやかに更新

ところで BodyPosition の更新は意味あるのか
意味なかった、なんと、ではこのオブジェクトの transform を利用することにします。

これなら、適用後
移動することを確認

アニメーターパラメータも差分をチェックする機構を作って動作を見てみます。
問題なさそうですね

アニメーションステートを見ます。
こちらも差分の検出は問題なし

animator.applyRootMotion は変化を受け取れなかったので、無視します。

これで小ゴール達成です。

■スナップショット差分が発生したデータだけを送信するインタフェース

値に変更があったものを瞬時に送信できるような仕組みとするので
差異があったパラメータを配列にして送信できたらいいなと

動的に増減するので List 型にしてみる

まずはリストが変かあったときに要素を持ち、これを送信する
そんなリストが作られる様子を確認してから

それを送信できるインタフェースを Magic Onion 側に用意してコードジェネレートしてビルド通してみましょう

リストになれない
値が float, int, bool, Vector3, Quaternion に分かれます。

name hash を送信すれば float, int, bool はそれぞれリスト化できそう
インタフェースは三つになる形か

レイヤーごとのアニメーションステートは一つインタフェースを呼ぶ形

Vector3 とかはそれぞれインタフェースを用意してしまう形で x 4 ってことで

となるとインタフェースは全部で 8 つ増えることに
List に関してだけ namehash と value のセットを送信する形になりますね。

作って、送受信できているか、試してみます。

サーバー側でも UnityEngine.dllを選んでAddをクリックして参照追加すれば Vector3 を引数に指定して処理できるようになります。
C:\Program Files\Unity\Hub\Editor\2019.1.0f2\Editor\Data\Managed\UnityEngine.dll
これです。

あれ、サーバーとして起動できない。
プロジェクトに含めないといけない様子だが
ビルドは通るけど、ランタイムエラーという

Magic Onion で Unity Vecotr 3 とか使えた気がしたけど、気のせい?
全部 float にして送受信するように書き直して Magic Onion のコード生成を実行

ミスマッチで作れないとエラー

追加した行をすべて元に戻して、同じエラー

内容が読めないけど、これは?
もう見落としている箇所が見つけられないのだけど…

入念にチェックをしてみます。

エラーが発生するのはコードを書いていた部分ではなく Unity アセンブリからの MessagePack コード自動生成の部分でした。
なぞ、解決方法がわからん。

簡易なプロジェクトで Magic Onion だけのプロジェクトを作ってコードジェネレートだけするというのがいいのかな…
それで解決を試みていきましょう。

通信はできるようになった、けど
同じアカウントで複数ログインを試行すると、プレイヤーIDが重なってしまう。

ここはプレイヤーごとに異なる、ログインハッシュ値を一時的に作成してユーザーの重なりを除去するのが良いか
NameToHash を使わせてもらうのがよいか

リアルタイムに情報を送受信しあっているので、小ゴールはクリアしていた

■日が暮れた…
続きは、次の記事に書きます。