VRChat UdonNetworking:同期
ワールド開発とデバッグの80%は、このネットワーキングにより予期せぬ問題が発生します。
ネットワーキングは基本的に、「すべてのクライアントはローカルであり、特定のユーザー(特にワールドマスター)が継続的にワールドの状態を同期させる必要がある」ということです。
つまり、各ユーザーがそれぞれのシーンを持ち、ゲームオブジェクトや変数に関する情報の更新を継続的に受け取り、まるで全員が一緒に動いているかのように見せるトリックだと考えてください。
これは非常に重要であり、ネットワーキングを考慮せずにコードを書くと意図しない結果が多く発生します。
正しく同期されていないためにローカルでのみ動作したり、途中で同期を受け取れなかった変数
のためにランタイム例外でスクリプト自体が停止する場合があります(UdonSharpでは例外処理がされていないため問題が発生します)。
したがって、プロジェクトの計画段階から常にネットワーキングを考慮することで、予期せぬ問題を減らすことができます。
同期方式には三つあり、None
は文字通り同期なしで全てローカルであるものです。
Continuous
は短い間隔で自動的にアップデートを送信します。
シンプルなスクリプトであれば使用するのに適していますが、期待通りに動作しない場合がしばしばあります(特にpingの違いのため)。
Manual
は文字通り同期を手動で実行する方式であり、デバッグをしていると最終的には全て管理できるこの方式に移行する方が扱いやすくなります。
Udonで使用されている同期要素には様々なものがありますが、よく使われるのは[UdonSynced]
アトリビュート、RequestSerialization
、OnDeserialization
、SendCustomNetworkEvent
、OnPlayerJoined
などです。
[UdonSynced]
int numberPlayer = -1;
[UdonSynced]
アトリビュートは、変数がローカル変数(ネットワーク的)ではなく、すべてのクライアントに同期される変数であることをコンパイラーに知らせるものです。
同期方式がContinuous
の場合、一定時間ごとに自動的に同期され、
Manual
の場合、マスターがRequestSerialization()
を呼び出すと[UdonSynced]
変数の情報がネットワークに伝播されます。
すべての型に[UdonSynced]
を付けられるわけではなく、こちらにあるもののみ可能です。
numberPlayer = 5;
RequestSerialization();
RequestSerialization()
は[UdonSynced]
変数を同期させたいときに呼び出し、すべての[UdonSynced]
変数が同期されます。
あまり頻繁に使用するとネットワークのオーバーヘッドが大きくなるため、状況に応じて適切に使用する必要があります。
public override void OnDeserialization()
{
CheckSyncedVariables();
}
OnDeserialization()
はRequestSerialization()
のコールバック関数で、[UdonSynced]
変数の値が同期を通じて変更されたときに自動的に呼び出されます。
UdonSharpBehaviour
クラス内のOnDeserialization()
を必要に応じてオーバーライドして使用します。
if (!Networking.IsMaster)
{
Debug.Log("player is not a master, return");
return;
}
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, "ActivePositionsLocal");
SendCustomNetworkEvent
は他のクライアントに特定の関数を呼び出させることができます。
引数が二つあり、NetworkEventTarget
は
enum VRC.Udon.Common.Interfaces.NetworkEventTarget
Name Summary All All players in the instance Owner Owner of the game object
このように二つあり、後ろに続くstring
には呼び出したい関数名を入れます。
マスターのみで使用することでオーバーヘッドを減らし、デバッグも容易になるため、使用前にクライアントがマスターかどうかを確認することが推奨されます。
また、渡す引数が事実上関数名一つだけであるため、呼び出そうとする関数が追加で引数を受け取る場合、補間や複数の関数を作成するなどの複雑な作業が必要となります。
public override void OnPlayerJoined(VRCPlayerApi player)
{
RequestSerialization();
Debug.Log(player.displayName + " has joined the world!");
if (player.isLocal)
{
SendCustomEventDelayedSeconds("CheckSyncedVariables", 8.0f);
SendCustomEventDelayedSeconds("LoadGame", 9.0f);
}
}
OnPlayerJoined
は後から参加した他のプレイヤーを処理する際に有用で、これもオーバーライドして使用できます。
他のプレイヤーがワールドに参加するたびに、すべてのクライアントのインスタンスで自動的に呼び出されます。
VRCPlayerApi player
は参加したプレイヤーを指します。
参加したプレイヤー本人にのみ特別に実装したいロジックがある場合は、player.isLocal
フラグを確認して実装します。