VRChat 非公式日本語ドキュメント

ネットワークイベント

ネットワークイベントを使用すると、スクリプト間でシンプルな一方向のネットワーク通信が可能になります。スクリプトがネットワークイベントを実行すると、そのイベントは現在のインスタンス内にいるターゲットプレイヤーに対して一度だけ実行されます。

ネットワークイベントは後から参加したプレイヤーに対しては繰り返されないため、視覚効果のような短期間のみ有効な一時的なアクションに最適です。後から参加するプレイヤーにとって重要なロジックや状態の変化にはネットワークイベントを使用しないでください。代わりにネットワーク変数を使用してください。

イベントの定義

イベントを宣言するには、名前を付ける必要があります。この名前はUdonBehaviourごとに一意である必要がありますが、異なるBehaviour間であれば再利用可能です。名前はUdon Graphのイベントノードのテキストフィールド、またはUdonSharpのメソッド定義によって決定され、大文字と小文字が区別されます。UdonSharpでは、IDEによるチェックを可能にするために nameof を使用してください。

SendCustomNetworkEvent を使用してイベントを呼び出す際、イベント名によって実行されるメソッドが決定されます。

メソッドやグラフのイベントノードをリモートから実行可能にするには、以下のルールに従う必要があります。Udon GraphとUdonSharpのいずれにおいても、受信側のUdonBehaviourは sync modeNone を指定してはなりません。

Udon Graphのカスタムイベントノードをネットワーク呼び出し可能にするには、以下の要件を満たす必要があります:

  • カスタムイベントの名前は、アンダースコア _ で始まってはなりません。
  • イベントノードは有効なFlow接続を持っている必要があります。

イベントの呼び出し

ネットワークイベントを発生させるには、UdonBehaviourUdonSharpBehaviourで利用可能なSendCustomNetworkEventメソッドを使用するか、NetworkCalling.SendCustomNetworkEvent経由で明示的に呼び出します。これらは同一の機能であり、互換性のためにのみ提供されています。

呼び出しのターゲットとなる UdonBehaviour は、 SendCustomNetworkEvent を実行しているものとは別のものでも構いません。無効化されているBehaviorをターゲットにすることも可能です。

以下の例に従って、インスタンス内のプレイヤーに対してカスタムネットワークイベントをトリガーします:

インタラクト時にメッセージをログ出力するオブジェクトのUdonグラフ。

  1. UdonBehaviourの同期モードが「None」ではなく、「Continuous」または「Manual」に設定されていることを確認してください。
  2. 「Event Custom」ノードを作成します。
  3. ノードの入力ボックスを使用して、このノードに一意の名前を付けます。
  4. 「Send Custom Network Event」ノードを追加します。
  5. eventName ドロップダウンで、イベントに割り当てた名前を選択します。また、文字列のフローを接続して動的にイベント名を選択したり、別のBehaviorから名前を入力したりすることも可能です。
  6. ルーム内の各プレイヤーでこのイベントをトリガーするには、デフォルトの All のままにするか、 別のオプション に変更してください。
  7. instance 入力は空のままにすることで現在の UdonBehaviour をターゲットにできます。または、別の UdonBehaviour への参照を接続することで、代わりにその UdonBehaviour 上のカスタムイベントを発火させることも可能です。

注意点として、あなたの Udon コードはこれらのイベントがリモートプレイヤー上で呼び出されるのを待機しません。単にイベントを送信し、即座に次の処理へ進みます。ただし、イベントの受信者が送信者自身である場合は、通常の関数呼び出しと同様に、処理を続行する前にローカルでイベントがトリガーされます。

イベントの送信順序と受信順序は、自身で定義した レート制限 に到達しない限り保証されます。イベント A を送信した後にイベント B を送信した場合、受信側も AB の順序で受信します。この保証はシーン内のすべての Behavior 間で有効ですが、複数のプレイヤーが同時にイベントを送信する場合には適用されません。

ただし、イベントが [NetworkCallable(maxEventsPerSecond: X)] 属性によってレート制限(VRChat内部のレート制限やスループット制限は含みません!)されている場合でも、他のキューの消化が妨げられることはありません。例えば、イベントAの制限が毎秒3回で、一度に5つのイベント A1, A2, A3, A4, B1 を送信した場合、A4がレート制限に達するため、イベントB1が「キューをスキップ」して、A1, A2, A3, B1, A4 の順序で到達します。

イベントターゲット

ネットワークイベントは、常にインスタンス内の1人または複数のプレイヤーをターゲットにします。ターゲットとして以下を選択できます:

ターゲット説明
NetworkEventTarget.Allインスタンス内のすべてのプレイヤーがイベントを受信します。
NetworkEventTarget.Othersローカルプレイヤーを除く、インスタンス内のすべてのプレイヤーがイベントを受信します。
NetworkEventTarget.Ownerオブジェクトのオーナーがイベントを受信します。
NetworkEventTarget.Self「ループバック」ターゲットです。送信プレイヤーのみがイベントを受信します。ネットワーク上を実際に送信されることはないため、すべてのレート制限をバイパスします。

ローカルプレイヤーが自分自身にネットワークイベントを送信する場合、そのイベントは即座に実行されます。例えば、 NetworkEventTarget.All はすべてのプレイヤーにネットワークイベントを送信しますが、ローカルプレイヤーは待機することなく即座にそのイベントを実行します。

特定のプレイヤーにイベントを送信するには、対象プレイヤーの playerId をパラメータに含め、受信したIDがローカルプレイヤーと一致する場合にのみイベントを実行するようにします。その際は NetworkEventTarget.All を使用するか、ローカルでイベントをトリガーするための特別な処理を追加してください。

パラメータ付きイベントの送信

ネットワークイベントは最大 8 つのパラメータを保持できます。各パラメータは同期可能な変数型intstringfloatbool など)である必要があり、UdonSharpでは、受信側のメソッドに [NetworkCallable] 属性を付与する必要があります。

Udon Graphでカスタムイベントを作成する際、いくつのパラメータを持たせるかを選択できます。デフォルトのバリアントはパラメータなしです。ノード上のドロップダウンを使用して、パラメータの数は後からいつでも変更可能です。

パラメータを持つノードでは、左側のドロップダウンで型を選択できます。出力データポートの型は自動的に変更されます。

「Send Custom Network Event」ノードからパラメータを送信するには、ドロップダウンから適切な数のパラメータを持つオーバーロードを選択してください。

例として、文字列(string)と整数(int)を受け取るシンプルなグラフを以下に示します:

インタラクト時に異なる型の2つのパラメータを持つイベントを送信するUdonグラフ

SendCustomNetworkEventに渡すパラメータの型は、イベント側で宣言した型と一致している必要があります。一致していない場合、送信は失敗します!

SendCustomNetworkEvent の入力として null を渡した場合、受信側で呼び出されるメソッドは、メソッドシグネチャで宣言されたパラメータの型 T に応じた default(T) を受け取ります。つまり、NULL許容型は null として受け取られ、非NULL許容型はデフォルト値として受け取られます(例:int パラメータとして null を送信すると 0 を受け取ります。これは default(int) です)。

パラメータサイズの制限とイベントの分割

一般的に、問題を防ぐためにパラメータサイズは最小限に抑え、大きなオブジェクトや複雑な構造の送信は避けてください。以下のハードリミット(絶対的な制限)が存在します:

  • 1つのネットワークイベントに含まれる全パラメータの合計サイズは 16 KB を超えることはできません。
  • 単一のパラメータのサイズ自体に制限はありませんが、イベント全体の合計サイズによる制限を受けます。
  • 送信データ量の合計は、最大で約 18 KB/s に厳格に制限されています。これにはすべてのネットワークオーバーヘッドが含まれるため、実際には 8〜10 KB/s を超えることはほとんどありません。

さらに、イベントの遅延を引き起こす可能性がある2段階の制限があります:

  • スループットベースの制限 – これは、常に処理されているネットワークデータの合計量に適用され、通常のUdon同期と同じルールに従います。
  • ユーザー設定によるレート制限 – これは、ネットワークイベントを1秒間に送信できる頻度を制御するもので、[NetworkCallable(maxEventsPerSecond: X)] 属性を使用して調整できます。
注意

1024バイト(1キロバイト)を超えるパラメータデータを含むイベントを送信した場合、それは内部的に複数のイベントに分割されます。このプロセスはUdonからはほぼ透過的です。受信側はイベントを再構成し、対象の UdonBehaviour に対して一度だけ呼び出しを行います。

ただし、これらの内部イベントは レート制限キュー および Get(All)QueuedEvents 関数からは確認可能です。例えば、レート制限を「1秒間に2イベント」に設定している状態で、2048バイトのデータを含む SendCustomNetworkEvent を呼び出した場合、実効的な許可レートは1秒間に1イベントの呼び出しとなります。これは、2048バイトのデータでは単一のカスタムネットワークイベントが2つの内部イベントに変換されるためです。

「パラメータサイズ」とは、パラメータデータがエンコードされた後のバイト数を指します。これには内部ヘッダーや、制御できないその他のオーバーヘッドは含まれません。例をいくつか挙げます:

// fits into 1 internal event:
int x = 0; // = 4 bytes, sizeof(int)
Vector3 v = Vector3.zero; // = 12 bytes, sizeof(float) * 3
new string('x', 400); // = 400 bytes, UTF-8 encoded
"うどんは美味しい"; // = 24 bytes, UTF-8 encoded with non-ASCII characters
new char[128]; // = 256 bytes, UTF-16 (following C# spec)
new byte[1024]; // = 1024 bytes

// requires more than 1 internal event:
new byte[1025]; // = 1025 bytes, 2 events sent
new byte[16 * 1024] // = 16384 bytes, 16 events sent, maximum allowed size
new int[512]; // = 2048 bytes, sizeof(int) * 512, 2 events sent

// string[] and VRCUrl[] are special cases:
new string[2] { "test", "foobar" }; // = 18 bytes, 4 + 6 from UTF-8 encoded strings, 8 additional for a length value per array entry ( 2 * sizeof(int) )

レート制限

ネットワークイベントには、過剰な使用を防ぐためにレート制限が設けられています:

  • デフォルトのレート:毎秒5イベント
  • 最大設定可能レート:毎秒100イベント

レート制限を変更するには、[NetworkCallable(maxEventsPerSecond: X)]属性を使用してください。Xは1から100までの整数(両端を含む)を指定できます。

Udon Graphでは、ノードの対応する入力フィールドに目的の値を設定するだけです。パラメータのないイベントの場合、この値を0に設定することでレガシーイベントとして扱えることに注意してください。

このパラメータは、イベントの送信速度を指定します。「1秒あたりのイベント数」で指定され、例えば値が5の場合は「最大で毎秒5イベント」を意味します。 SendCustomNetworkEventを1回呼び出すと、レート制限の観点から複数のイベントが発行される場合があります。詳しくはイベントの分割を参照してください!

すべてのレート制限はベストエフォート方式で適用されます。ローカルのパフォーマンス、ネットワーク利用状況、サーバー負荷に基づいて、設定値や指定値と完全に一致しない場合があります。

警告

この制限は安全対策として利用できるものです!悪意のあるユーザーがイベントを悪用してワールドに問題を引き起こすことを防ぐため、この値を可能な限り低く設定することを強く推奨します。

このレート制限は、送信側クライアントとサーバー側の両方で適用されることに注意してください。通常の使用において、サーバー側の制限は悪意のあるユーザーからの保護のみを目的としており、通常は意識する必要はありません。ローカルクライアントの挙動はキューイング(待ち行列)であり、短時間に大量のイベントが送信された場合、レート制限によって送信可能になるまでキューに入れられます。キューに入れられるメッセージ数に制限はないため、無制限に高速でメッセージを送信し続けると、ワールド内のすべてのUdonネットワークに支障をきたす可能性があるため注意してください。

イベントはローカルプレイヤーに対して即座に実行されるため、その場合にはレート制限は適用されません。つまり、ローカルで既に実行されたイベントであっても、リモートプレイヤー用にはキューに入れられる可能性があります。

最後に、送信されるイベント全体に対してもグローバルな制限があり、現在は毎秒約100イベントとなっています。この制限は動的であり、ユーザー側で設定することはできません。制限は予告なく変更される可能性がありますが、VRChat側で大幅な引き下げを行う場合には事前に告知されます。この制限は設定可能な制限と同様に適用され、制限を超えたイベントはキューイングされます。

混雑状況の監視

NetworkCalling クラスの以下の関数は、レート制限の処理に役立ちます:

関数説明
int NetworkCalling.GetQueuedEvents(udonBehaviour, eventName)現在送信待ちとしてキューに入っているイベントの数を返します。通常動作時には、この数値は0から設定したレート制限の範囲内に収まります。この数値がレート制限を超えている場合、イベントを送信する速度が速すぎることになり、レート制限によって送信可能になるまでイベントはキューに入れられます。
int NetworkCalling.GetAllQueuedEvents()現在キューに入れられているすべてのイベントの合計数を返します。0を超える数値であっても、必ずしもネットワーク状況が悪いことを示すわけではありません。低負荷であってもイベントが短時間キューに入れられることはあるためです。

Networking.IsClogged プロパティを使用して、ネットワークの状態を判断することもできます。これはイベントの過剰な送信によって影響を受けます。

例:

using TMPro;  
using UdonSharp;
using UnityEngine;
using VRC.SDK3.UdonNetworkCalling;
using VRC.Udon.Common.Interfaces;

public class EventQueueExample : UdonSharpBehaviour
{
[SerializeField] private TextMeshProUGUI queueStatus;

void Update()
{
queueStatus.text = $"Queue: {NetworkCalling.GetAllQueuedEvents()}";
queueStatus.text += $"\nSpecific Event Queue: {NetworkCalling.GetQueuedEvents((IUdonEventReceiver)this, "SomeNetworkEvent")}";
queueStatus.text += $"\nClogged: {Networking.IsClogged}";
}

// some network events...
}

同じインスタンス内でのワールドバージョンの不一致

非常に特殊なエッジケースとして、悪意のある動作がなくてもサーバー側のレート制限によってイベントが破棄されるシナリオが1つあります。レート制限は各クライアントのローカルなワールドの視点に基づいて適用されるため、レート制限を厳しくした新しいバージョンのワールドをアップロードし、同じインスタンス内に異なるワールドバージョンのユーザーが混在している場合、送信側クライアントが受信側クライアントのレート制限の想定を超えてしまうことがあります。この場合に限り、サーバーはイベントをサイレントに破棄し、配信しない可能性があります。

イベントの送信者へのアクセス

NetworkCalling クラスの以下のプロパティは、イベントの処理に役立ちます:

プロパティ説明
VRCPlayerApi NetworkCalling.CallingPlayerこのネットワーク呼び出しを開始したプレイヤーの VRCPlayerApi です。ネットワーク呼び出し中ではない場合は null となります。
bool NetworkCalling.InNetworkCall 現在の行がネットワーク呼び出しの一部として実行されているかどうかを示します。現在の行がネットワーク呼び出しの一部として実行されているかどうかを示します。この状態はエントリー関数が終了するまでリセットされないことに注意してください。つまり、イベントのエントリーポイントから別のスクリプトや異なる関数を呼び出した場合でも、この状態は維持されます。

レガシーイベントとセキュリティ

[NetworkCallable] は SDK 3.8.1 で導入されました。以前の Udon では、_MethodName のようにアンダースコアで始まるものを除き、すべてのパブリックメソッドを呼び出すことができました。後方互換性のため、アンダースコアで始まらない引数なしのパブリックメソッドは引き続き呼び出せますが、推奨はされません。メソッドに [NetworkCallable] 属性を付与することで、アンダースコアで始まるメソッドであってもネットワーク経由で呼び出せるようになります。

Udon Graph において、カスタムイベントノードの MaxEventsPerSecond 入力フィールドが 0 に設定されている場合、そのノードは「レガシー」とみなされます。

警告

パブリックメソッドやグラフイベントがネットワーク経由で呼び出されないようにするには、名前の先頭にアンダースコア _ を使用する必要があります。これにより、ワールドやプレハブのセキュリティを向上させることができます。

コンポーネントインデックスによるターゲット指定

レガシーイベントに関する特に重要な補足として、GameObjectに対してイベントを送信する場合と、オブジェクト上の特定のComponentに対してイベントを送信する場合のセマンティクスの違いを考慮してください。

[NetworkCallable]でマークされた関数を呼び出すと、その呼び出しはComponentターゲット指定のセマンティクスを使用します。これは、たとえGameObject上に2つ以上のUdonBehaviourがあったとしても、指定されたUdonBehaviourのみがイベントを受け取ることを意味します。

レガシーケース([NetworkCallable]でマークされていない関数へ引数なしでイベントを送信する場合)は、GameObjectセマンティクスを使用します。つまり、これはUnityの組み込みのGameObject.SendMessageと同様に動作し、ターゲットとしたBehaviorを含むオブジェクト上のすべてのUdonBehavioursに対して関数を呼び出します。

注意点として、Componentターゲット指定はコンポーネントのインデックス(順序)に依存するため、ネットワーク同期されたUdonBehaviourを使用するGameObject上のコンポーネントに対してDestroyを使用することは推奨されません。これを行うと、未定義の動作を引き起こす可能性があります。

最終更新: