SimpleNetwork
SimpleNetworkは、VRChatの複雑なネットワークをシンプルにするNetworkingラッパーなUdonSharpモジュールです。
Install / Use
/learn @tutinoco/SimpleNetworkREADME
SimpleNetwork
SimpleNetworkは、VRChatのNetworkingがしんどい方のためのNetworkingラッパーなUdonSharpモジュールです。
SendCustomNetworkEventメソッドで、引数を扱えない問題を解消する目的で作成されました。
SimpleNetworkは、SimpleNetworkUdonBehaviorの進化版です。
特徴
SendCustomNetworkEventで不可能な引数の送信ができ、メッセージングプログラミングを可能にします。- 煩わしい所有権から解放され、権限の無いオブジェクトからも低レイテンシーで高速な通信が行えます。
- オブジェクトにグループ名を設定して、複数のオブジェクトにイベントをイテレーション送信できます。
- 大量にイベントを発行しても、自動的に全てのイベントを1回の送信にまとめるため、安定して動作します。
AllやOwnerのほかに、Masterやプレイヤーを直接設定して、イベント受信者を限定することができます。Masterにイベント送信を依頼するなど、他のプレイヤーにイベントの送信を依頼することができます。- 最後に実行したイベントやイベント履歴をサーバに保存し、後から参加したプレイヤーに送信することができます。
SendCustomNetworkEventDelayedFramesに相当する、ネットワークイベントの遅延送信が可能です。- シーンに存在しない動的に生成したオブジェクトに対しても通信が可能です。
使い方
SimpleNetwork.prefabをシーンに配置し、適当なクラスを作成してSimpleNetworkBehaviourを継承すると、SendEvent()メソッドで、全てのプレイヤー(自分自身を含む)のオブジェクトに値を送信することができます。
イベントを受信するには、サブクラスでReceiveEventメソッドをオーバーライドします。第一引数にはイベント名が、第二引数には値が届きます。
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using tutinoco; // 追加して
public class Test : SimpleNetworkBehaviour // 継承して
{
void Start()
{
SimpleNetworkInit(); // 初期化して
SendEvent("Talk", "こんにちは!"); // イベントを送ると
}
public override void ReceiveEvent(string name, string value)
{
if( name == "Talk" ) {
Debug.Log(value); // こんにちは!が全ユーザに届きます。
}
}
}
上記のコードでは、オブジェクトが自らのイベントを送信していますが、特に嬉しいのは、値を送信できるようになったことで、命令されたら動くアクションだけをまとめたクラスを簡単に作成できるようになることです。 以下は、命令を待って動くモンスタークラスの例です。
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using tutinoco;
public class Monster : SimpleNetworkBehaviour
{
[SerializeField] private Rigidbody rigidbody;
[SerializeField] private Text fukidashi;
void Start()
{
SimpleNetworkInit();
}
public override void ReceiveEvent(string name) // 第二引数valueを省略しても
{
// 【上にジャンプ】
if( name == "jump" ) {
float power = GetFloat(); // floatで受け取れます。
rigidbody.AddForce(transform.up*power, ForceMode.Impulse);
}
// 【座標にワープ】
if( name == "warp" ) {
Vector3 pos = GetVector3(); // Vector3で受け取れます。
gameObject.transform.position = pos;
}
// 【ふきだしに文字表示】
if( name == "talk" ) {
fukidashi.text = GetString(); // stringで受け取れます。
}
}
}
このモンスタークラスを使って作られたモンスターを制御するには、適当な箇所に以下のコードを記述します。
// ジャンプ力5.0で飛ぶ!
monster.SendEvent("jump", 5.0f);
// x:1 y:2 z:3 の座標にワープ!
monster.SendEvent("warp", new Vector3(1.0f, 2.0f, 3.0f));
// ふきだしに文字を表示!
monster.SendEvent("talk", "僕を捕まえられるかな?");
様々な型の送受信
bool char byte sbyte short ushort int uint long ulong float double Vector2 Vector3 Vector4 Quaternion string VRCUrl Color Color32に加え
Object[] SimpleNetworkBehaviourの送受信に対応しています。
現在、Object[]以外の配列の送受信は非対応です。
public override void ReceiveEvent(string name)
{
// SendEvent("hoge", true);
bool boolValue = GetBool();
// SendEvent("hoge", 5);
int intValue = GetInt();
// SendEvent("hoge", 3.14f);
float floatValue = GetFloat();
// SendEvent("hoge", "こんにちは");
string stringValue = GetString();
// SendEvent("hoge", new Vector3(1.0f, 2.0f, 3.0f));
Vector3 vector3Value = GetVector3();
}
また、値の無いイベントの送信も可能です。
monster.SendEvent("Init");
monster.SendEvent("Init", none);
// monster.SendEvent("Init", null); // 型指定の無いnullは非対応です ><
イベントの送信
イベントのローカル実行
// 自分のみイベントを実行
ExecEvent("Jump", 5.0f);
イベントの遅延送信
// 約2秒の120フレーム後にイベント送信
ExecEvent("Jump", 5.0f, 120); // 自分のみ
SendEvent("Jump", 5.0f, 120); // 全員
送信先の指定
// 全員にイベントを送信(デフォルト)
SendEvent(SendTo.All, "Jump", 5.0f);
// オブジェクト所有者にイベントを送信
SendEvent(SendTo.Owner, "Jump", 5.0f);
// インスタンスマスターにイベントを送信
SendEvent(SendTo.Master, "Jump", 5.0f);
// 自分自身にイベントを送信
SendEvent(SendTo.Self, "Jump", 5.0f); // ExecEventに同じ
SendEvent(SendTo.Me, "Jump", 5.0f); // RequestEvent()ではSelfとMeは挙動が異なる
// オブジェクト所有者以外にイベントを送信
SendEvent(SendTo.NotOwner, "Jump", 5.0f);
// インスタンスマスター以外にイベントを送信
SendEvent(SendTo.NotMaster, "Jump", 5.0f);
// 自分以外にイベントを送信
SendEvent(SendTo.NotSelf, "Jump", 5.0f);
// 指定のプレイヤーにイベントを送信
SendEvent(player, "Jump", 5.0f);
もちろん以下のように送信先を指定した上でイベントの遅延送信を行うこともできます。
// インスタンスマスターに2秒遅延してイベントを送信
SendEvent(SendTo.Master, "Jump", 5.0f, 120);
イベント送信の依頼
RequestEvent()メソッドを使うことで、イベントの送信を他のプレイヤーに依頼することができます。
// インスタンスマスターから全員にイベントを送信するよう依頼
RequestEvent(RequestTo.Master, "Jump", 5.0f);
// インスタンスマスターからオブジェクトオーナーにイベントを送信するよう依頼
RequestEvent(RequestTo.Master, SendTo.Owner, "Jump", 5.0f);
// プレイヤー1からプレイヤー2にイベントを送信するよう依頼
VRCPlayerApi player1 = VRCPlayerApi.GetPlayerById(1);
VRCPlayerApi player2 = VRCPlayerApi.GetPlayerById(2);
RequestEvent(player1, player2, "Jump", 5.0f);
// オブジェクトオーナー側で60フレーム待機してから自分にイベントを送信するよう依頼
RequestEvent(RequestTo.Owner, SendTo.Me, "Jump", 5.0f, 60);
複数の値を送信する
複数の値を送るには、Pack()メソッドを利用して値をまとめます。
Vector3 position = Vector3(1.0f, 2.0f, 3.0f);
Quaternion rotation = Quaternion.identity;
monster.SendEvent("warp2", Pack(position, rotation));
Mainクラスの役割を果たしているクラスでもSimpleNetworkBehaviourを継承することをお勧めしますが、
万が一呼び出し元がSimpleNetworkBehaviourのサブクラスでない等、Packメソッドを利用できない場合には、以下のようにObject[]を利用します。
object[] values = new object[] {position, rotation}; // object[]に格納して
monster.SendEvent("warp2", values); // 送信する。
複数の値を受信するには、以下のように値の引数番号を指定します。
public override void ReceiveEvent(string name)
{
Vector3 position = GetVector3(0); // 0番目の値をVector3で取得
Quaternion rotation = GetQuaternion(1); // 1番目の値をQuaternionで取得
// 引数番号を省略することもできます。
Vector3 position2 = GetVector3(); // 最初に見つかったVector3を取得
Quaternion rotation2 = GetQuaternion(); // 最初に見つかったQuaternionを取得
}
なお、対応していない型の値をPack()に含めるとはできません。
様々な使い方
パラメータの順序
ExecEvent(名前, 値, グループ指定, 遅延);
SendEvent(送信先, 名前, 値, グループ指定, 遅延, 参加同期);
RequestEvent(依頼先, 送信先, 名前, 値, グループ指定, 遅延, 参加同期);
CreateEvent(依頼先, 送信先, 名前, 値, グループ指定, 遅延, 参加同期);
各イベント送信メソッドのパラメータの設定方法は上記の通りで、名前以外は省略することができます。
設定可能な型は以下の通りです。
- 依頼先:
RequestTo型、または依頼先プレイヤーをVRCPlayerApi型で指定します。 - 送信先:
SendTo型、または送信先プレイヤーをVRCPlayerApi型で指定します。 - 名前:
string型で実行するイベント名を指定します。 - 値:送信する値を設定します。対応している型は、色々な型の送受信を参照してください。
- グループ指定:
string型でグループ名を指定してイベントを受信するオブジェクトを選択します。 - 遅延:
int型でフレーム数を設定し、イベントの送信を遅らせます。 [](* 参加同期:JoinSync型で、ワールドに参加したユーザに同期するイベントを設定します。)
2つの受信方法
SimpleNetworkではイベントを受信する方法に「ダイレクトレシーブ」と「インディレクトレシーブ」が存在します。
ダイレクトレシーブを使うとGetFloat()などの値取得用のメソッドを利用せず、直接値を受け取ることができます。
以下のように様々なReceiveEvent()メソッドをオーバーライドすることで、型に応じて受信用メソッドを分けることができます。
// float型のデータを受信
public override void ReceiveEvent(string name, float value) { ... }
// Vector3型のデータを受信
public override void ReceiveEvent(string name, Vector3 value) { ... }
// Packでまとめられた複数の値を受信
public override void ReceiveEvent(string name, Object[] values) { ... }
おすすめの受信方法は、すべてのイベントと値を柔軟に受信できる、インディレクトレシーブです。
// 全てのイベントを受信
public override void ReceiveEvent(string name) { ... } // 第二引数を省略する
値の受信方法はGetFloat()などの受信メソッドを利用することですが、該当する型のデータが届かなかった場合にエラーが発生するため注意が必要です。
また、GetValues()メソッドを利用することで受信したすべての値を受け取ることもできます。
メタデータの受信
送信された値以外にも、付随するデータを取得することができます。
public override void ReceiveEvent(string name)
{
// 送信元オブジェクトを取得します
SimpleNetworkBehaviour behavior = GetSource();
// 送信元プレイヤーIDを取得します
int player = GetSender();
// このイベントを受信したプレイヤーIDの一覧を取得します
int[] players = GetRecipients();
// このイベントのグループターゲット文字列を取得します
string target = GetTarget();
// グループでイテレートした際のインデックスを取得します
int index = GetIndex();
// このイベントがどのくらい遅延して送られたかを取得します
int delay = GetDelay();
// 受信したすべての値を受け取ります。
Object[] values = GetValues();
// 指定した型が受信した値の何番目にあるか取得。無ければ-1が返ります。
int argIndex = IndexOf(typeof(int));
}
制御可能なオブジェクトの複製
通常、Instantiateを使って複製したオブジェクトはUdonでは制御できませんが
SimpleNetworkでは、制御可能なオブジェクトを複製する機能が備わっています。
オブジェクトを複製するにはDuplicate()メソッドを実行します。
// monsterをx:0 y:0 z:0に複製します
Duplicate(monster, new Vector3(0,0,0) /*,Quaternion.identity*/); // Quaternionは省略可能
複製されたオブジェクトを取得するには、OnDuplicateComplete()メソッドをオーバーライドします。
public override void OnDuplicateComplete( SimpleNetworkBehaviour behaviour )
{
Monster newMonster = (Monster)behaviour;
newMonster.SendEvent("Jump", 5.0f); // 複製したオブジェクトにイベントを送信
}
グループ名を設定する
SimpleNetworkInit()メソッドの第一引数にグループ名を設定すると、イベントのグループ指定送信を行った際にマッチしたグループにイベントを送信することができます。
例えば、下記のようにMonsterクラスのStart()メソッドでSimpleNetworkBehaviourを初期化する際に"Monster"というグループ名を設定します。
Monster.cs
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using tutinoco;
public class Monster : SimpleNetworkBehaviour
{
private VRCPlayerApi target;
void Start()
{
SimpleNetworkInit("Monster"); // グループ名を設定して初期化
}
public override void ReceiveEvent(string name)
{
// 【モンスターの初期化】
if( name == "Init" ) {
float distance = GetFloat();
float x = Random.Range(-0.5*distance, 0.5*distance);
float z = Random.Range(-0.5*distance, -0.5*distance);
Vector3 pos = new Vector3(x, 0f, z);
gameObject.transform.position = pos;
}
// 【プレイヤーを見る】
if( name == "See" ) {
int playerId = GetInt();
VRCPlayerApi target = VRCPlayerApi.GetPlayerById(playerId);
gameObject.transform.LookAt(target.transform);
}
}
}
全てのMonsterに同じイベントを送信...つまり、全てのMonsterに一斉に見つめられるようにするにはMainクラスを以下のようにします。
Main.cs
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using tutinoco;
public class Main : SimpleNetworkBehaviour // Mainクラスでも継承する
{
// [SerializeField] private Monsters[] monsters; // ←これはもういらない
private VRCPlayerApi targ
Related Skills
node-connect
350.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
110.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
350.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
350.8kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
