■前書き
https://simplestar-tech.hatenablog.com/entry/2019/05/02/073820 の続きです
前回を要約すると
具体的には、チュートリアルルームを作りこんで、出口があり
出口にキーカードをかざすようにして、プレイヤー名、サインインするフローを用意し
プログラムを繋ぎこみます。
■横道
Unity プロジェクト単体によるリアルタイム通信と数十MB単位の巨大なデータの即時受け渡しができるようになったわけで
これってかなり自由にオンラインゲーム作れるってことじゃないですか
土台として固めて、なんとか形にしたい
■本題
まずは出口付近でアクションするとカードキーをかざす動作をするというものを作りますか
一度もそんなアニメーションを再生したことないな、どうすれば?
あと、ボタンの関連づけをより自然に直します。
いい感じの扉のあるチュートリアルルームのアセット買ったのでライトベイクはじめたら、永遠に終わりそうにない…
あと5分して進捗なければ消します。
進捗あったので残しました。
ボタン配置はどこで変えられる?
これで変えられました
var vInput = vrmRoot.AddComponent<vThirdPersonInput>();
vInput.jumpInput = new GenericInput("Space", "A", "A");
vInput.rollInput = new GenericInput("Q", "Y", "Y");
vInput.crouchInput = new GenericInput("C", "X", "X");
var vAction = vrmRoot.AddComponent<vGenericAction>();
vAction.actionInput = new GenericInput("E", "B", "B");
ライトベイクはよくわからないが、扉の前でカードキーイベントを発生させたい。
次に、カードキーアクションを発生させる environment があるか調べてみます。
vTriggerGenericAction クラスをアタッチした Collider が指定したアニメを再生させる仕組みの様子
動きを見たら実装を追ってみます。
Animation名を指定する欄で Press_Lever を記入したら、ドアのレバーアクションが行われることを確認できました。
■ドアアクションで、ルームログインのパネルを表示
具体的に
ユーザー名、パスワード→初めての方はWebを開く
ログイン完了と同時に S3 へ現在の vrm データをアップロード
プレイヤー名を記入する項目が出てきて、ディフォルト値があれば利用
オンラインルームに入る、ボタンを押すと
アップロード完了時のイベントで vrm のダウンロード用のオブジェクトキーを作成
オブジェクトキーが作成されるのを待ち、 Magic Onion のリアルタイム通信の規定ルームに入る
をやってみます。
Sign Up ボタンを押すと所定の url に飛ぶような Unity 機能ありますかね
これかな
Unity - Scripting API: Application.OpenURL
using UnityEngine;
public class OpenURL : MonoBehaviour
{
[SerializeField] string url;
public void OnClick()
{
Application.OpenURL(url);
}
}
期待通り機能しました。
アクションが発生したときのイベントもあるので、これを利用します。
S3 アップロードのサンプル一式をコピーします。
コピーしたのは
Assets/AWS_packages をまるごとコピー
AWSS3 サンプルフォルダを切って、サンプルのスクリプトファイルをコピーしました。
UI が出てもマウスカーソルが表示されません。
誰がどこで制御しているのでしょうか
■UI表示時にのみ、マウスカーソルを表示
参考:
kan-kikuchi.hatenablog.com
たぶんどこかでCursor.lockState を制御しているコードがあるんじゃないかな
発見
vThirdPersonInput.cs
public virtual void LockCursor(bool value)
{
if (!value)
Cursor.lockState = CursorLockMode.Locked;
else
Cursor.lockState = CursorLockMode.None;
}
外部から制御するため、この関数を呼ばせてもらいますか
正常に機能しました。
■UI操作中はキャラクターアクションしないでほしい
インプットを切るとかあるんですかね
Invector の Trigger アクションの With GameObject の使い方を知りたい
実装を追うと UnityEvent の使い方になった
www.urablog.xyz
あれ、UnityEvent call 対象のコールバック関数に問題あるのかなー?呼ばれるけど引数が null になってしまう
Invoke するときは GameObject 渡しているのに
どういうこと?
ここを読みます。
UnityEvent - Unity マニュアル
足りない!次
unitygeek.hatenablog.com
設定可能な関数一覧リストは、'Dynamic'と'Static'の2つに分けられている
Dynamic/Staticの両方に表示されている。引数を動的に指定したい場合は、'Dynamic'の方を使う。
この説明で理解しました。
私はいつも通り静的な関数を設定していました。
ここを動的な関数に選択しなおします。
その後に lockInput フラグを変更すれば、ロックはできるはず
できました。
イベントの動作だけブロックできなかった。
GenericAction についてもこんな対応を入れて解決する?
this.lockInputTarget = vrmRoot.GetComponent<vThirdPersonInput>();
this.lockInputTarget.lockInput = true;
this.lockActionTarget = vrmRoot.GetComponent<vGenericAction>();
this.lockActionTarget.actionInput.useInput = false;
解決しました。
■プレイヤー名記入とチャットルームへのジョイン
今の進捗を確認すると、VRM 選択→チュートリアルルームで自由行動
扉の前で入力モードが切り替わり、Amazon Cognito の認証フロー完了 + S3 のクライアント有効化まで
パネルは引き続き、プレイヤー名とチャットルームへのジョインについて切り替わるべきで、入力ロックもまだ続けておく必要があった
そうした変更を反映させます。
S3 アップロードが完了できていること
ジョインすることができ、ジョイン完了の通知が届くことを確認しました。
■S3アップロード完了の通知とオブジェクトキーの送信
C# のコールバックの書式は…調べます。
Action なんちゃらだったと思ったけど
UnityAction callback
っていうのが使えた
オブジェクトキーの受信まで確認できました。
■別プレイヤーがログオンした際のオブジェクトキーによるダウンロード
別のプレイヤーが入ってきたときに、そのプレイヤーをインスタンス化する処理を書きます。
他のプレイヤーが参加してきたときにオブジェクトキーを取得できることを確認したので、これをダウンロードして byte 配列にします。
そしてこれを VRM インスタンスとして配置し、username ごとの配列
さて、ダウンロードしたデータから GameObject が作成されることをデバッグ実行して確認したが、シーン内にいない
どういうこと?
追記:vThirdPersonController がシングルトンとして、二つ目を消すコードになってたからでした。
あと、ビルドしたクライアントを終了するとフリーズする
プレイヤータグを付けないようにしてみるなど
現れた
相変わらず終了時にフリーズする
仕方ない、アプリ終了時に何かするコードを仕込みます。
それでもダメ Esc で必ずログアウトとCleanUp するようにしてみたがどうか
フリーズは避けられたけど、サーバー側で例外が発生していた
Leave を無理に呼ばないようにするとか
いえ、Leave イベントこなくなり悪化しました。
Quit するのをずっと先にしてみるとか
結局解決せず、最低限、フリーズしないようにするワークアラウンドのコードがこちら
void Start()
{
this.InitializeClient();
Application.wantsToQuit += WantsToQuit;
}
bool WantsToQuit()
{
CleanUpMagicOnion();
return false;
}
private async void CleanUpMagicOnion()
{
if (this.isJoin)
{
await this.streamingClient.LeaveAsync();
this.isJoin = false;
}
await this.streamingClient.DisposeAsync();
await this.channel.ShutdownAsync();
Application.wantsToQuit -= WantsToQuit;
Application.Quit();
}
エラー内容はサーバーからのステータス送信に失敗というものなので、クライアントは正しく切断処理をしていそう
サーバー側に流れるエラーは、ここまでの作業で無視することにしました。
エラー内容
W0503 19:36:35.140967 Grpc.Core.Server Exception while handling RPC. System.InvalidOperationException: Operation is not valid due to the current state of the object.
小ゴールとして、正しくダウンロードしてキャラクタがゲーム内に表示されているのでよしとします。
■デバッグ機能:ダウンロードしたキャラクターを何かしらのインプットで動かす
通信する内容を精査していきます。
現在、他プレイヤーのデータをダウンロードして VRM キャラがゲーム内に出現している状態です。
これを操作するように出来る機能をローカルで確認していきます。
まずは、ファイルからデータを取り出してゲーム内に出現させるフローを確立します
そのあとにいじっていきましょう。
Animator は割り当たっているから、ステートマシンの再生でアクションできるはずなので、外から強制的に動かす信号を送れるようにしてみます。
具体的には
Animator のステートとしてディフォルトでは LocoMotion が選択されている
Animator パラメータの Inut Magnitude を 0~1.5 で操作し
各種 InputHorizontal , InputVertical などを増減させると歩きモーションを変えられる
ステートの変化はどうやっているか調べてみると
animator.CrossFadeInFixedTime("StateName", 0.1f);
試しにローリングするか調べてみます。
位置に変化がない状態でその場でローリングを行いました。
なるほど、アクションが実行されたら、そのタイミングでアクションをブロードキャストすると同期は可能ですね。
Animator パラメータは入力に変化があったときにスナップショットを送り付ける
定期的に絶対座標と回転をブロードキャストして補間するという仕組みがぱっと浮かびました。
ひとまず小ゴールクリアですね。考えていきます。
■キャラクターのステートの変化をキャッチしてブロードキャストする
アクションの実行など、アニメーションパラメータやステートに変化が生まれた時にこれを察知するスマートな方法は議論されていないのでしょうか。
調べます。
コード見る感じ AnimatorControllerParameter[] parameters { get; } でステートを逐一チェックして
ローカルで大きな変化があったパラメータをリストアップして、変化したパラメータをブロードキャストするとか?
全部ハッシュ値でアクセスしつつ、これ毎フレームチェックするべきなのかな
計算コスと大きいかな
でも通信量を極力抑えることの方が大事だと思われる
アニメーションパラメータは値が前フレームの状態より変化したらとしますか、これなら実装が見えるので作業するだけかな
アクションはどうする、これもステートとしてとれる?
レイヤーごとにこれで取ってこれそう
AnimatorStateInfo GetCurrentAnimatorStateInfo(int layerIndex);
関連
int layerCount { get; }
StringToHash でステート名を hash にして、hasy 値で StateInfo は構築されている様子、効率的に処理できそう
bodyPositionとかRotationとか
deltaRotation, deltaPosition ってのはアニメだけの話?
ちょっとモニタリングしてみます。
日が暮れたので、次の記事につなげます。
やっと試行錯誤できてますね。GWの目的達成です。