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

ネットワークイベント

ネットワークイベントを使用すると、スクリプト間で単純な一方向のネットワーク通信を行うことができます。スクリプトがネットワークイベントを実行すると、インスタンス内に現在存在する対象プレイヤーに対して、そのイベントが1回実行されます。

ネットワークイベントは後から参加したプレイヤーに対しては繰り返されません。そのため、装飾的なエフェクトのように、短期間のみ有効な一時的なアクションに適しています。後から参加したプレイヤーにとっても重要なロジックや状態の変化には、ネットワークイベントを使用しないでください。その場合は、network variables を使用してください。

イベントの定義

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

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

メソッドやグラフイベントノードをリモートから実行できるようにするには、以下のルールに従う必要があります。Graph と UdonSharp の両方において、受け取り側 の UdonBehaviour は sync mode None を使用しては いけません

Udon Graph カスタムイベントノードをネットワーク経由で呼び出せるようにするには、以下の要件を満たす必要があります。

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

イベントの呼び出し

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

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

以下の例に従って、インスタンス内のプレイヤーに対してカスタムネットワークイベントをトリガーしてください。

Udon graph for an object that logs a message when it is interacted with.

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

Udonコードは、リモートのプレイヤー側でこれらのイベントが呼び出されるのを待機せず、単に送信してすぐに次の処理へ進むことに注意してください。ただし、送信者自身がイベントを受信する場合、通常の関数呼び出しと同様に、ローカルでトリガーされてから次の処理へ進みます。

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

ただし、イベントが [NetworkCallable(maxEventsPerSecond: X)] 属性によって レート制限 されている場合(VRChat内部のレート制限やスループット制限は含まれません)、他のキューの消化が妨げられることはありません。例えば、イベントAの制限が1秒間に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がローカルプレイヤーのIDと一致した場合にのみイベントを実行するようにします。その場合は NetworkEventTarget.All を使用するか、ローカルでイベントをトリガーするための特別な処理を追加してください。

パラメータを伴うイベントの送信

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

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

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

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

例えば、文字列と整数を受け取るシンプルなグラフは以下の通りです:

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

SendCustomNetworkEvent に渡すパラメータの型が、イベント側で宣言した型と一致していることを確認してください。一致していない場合、送信は失敗します!

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

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

一般的に、パラメータサイズは最小限に抑え、巨大なオブジェクトや複雑な構造を送信することは避けてください。以下のハードリミット(厳格な制限)が存在します。

さらに、イベントが遅延する原因となる2段階の制限があります。

note

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

ただし、これらの内部イベントは レート制限キュー および Get(All)QueuedEvents 関数から確認できます。例えば、レート制限を「1秒間に2イベント」に設定している状態で、2048バイトのデータを持つ SendCustomNetworkEvent を呼び出した場合、実質的な許可レートは1秒間に1回となります。これは、2048バイトの場合、1つのカスタムネットワークイベントが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) )

レート制限

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

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

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

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

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

warning

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

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

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

最後に、送信イベントには全体でグローバルな上限も存在し、現在は1秒あたり約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(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 に設定されている場合、そのノードは「レガシー」とみなされます。

warning

パブリックメソッドやグラフイベントがネットワーク経由で呼び出されないようにするには、名前の先頭にアンダースコア _ を付けるようにしてください。これにより、ワールドやプレハブのセキュリティを強化できます。

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

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

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

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

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

最終更新: