1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VRC Udon Networking : Synchronization

Last updated at Posted at 2024-10-27

VRChat UdonNetworking:同期

ワールド開発とデバッグの80%は、このネットワーキングにより予期せぬ問題が発生します。
ネットワーキングは基本的に、「すべてのクライアントはローカルであり、特定のユーザー(特にワールドマスター)が継続的にワールドの状態を同期させる必要がある」ということです。
つまり、各ユーザーがそれぞれのシーンを持ち、ゲームオブジェクト変数に関する情報の更新を継続的に受け取り、まるで全員が一緒に動いているかのように見せるトリックだと考えてください。
これは非常に重要であり、ネットワーキングを考慮せずにコードを書くと意図しない結果が多く発生します。
正しく同期されていないためにローカルでのみ動作したり、途中で同期を受け取れなかった変数のためにランタイム例外でスクリプト自体が停止する場合があります(UdonSharpでは例外処理がされていないため問題が発生します)。
したがって、プロジェクトの計画段階から常にネットワーキングを考慮することで、予期せぬ問題を減らすことができます。

同期方式には三つあり、Noneは文字通り同期なしで全てローカルであるものです。
Continuousは短い間隔で自動的にアップデートを送信します。
シンプルなスクリプトであれば使用するのに適していますが、期待通りに動作しない場合がしばしばあります(特にpingの違いのため)。
Manualは文字通り同期を手動で実行する方式であり、デバッグをしていると最終的には全て管理できるこの方式に移行する方が扱いやすくなります。

Udonで使用されている同期要素には様々なものがありますが、よく使われるのは[UdonSynced]アトリビュート、RequestSerializationOnDeserializationSendCustomNetworkEventOnPlayerJoinedなどです。

[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フラグを確認して実装します。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?