はじめに
ようやくUdonSharpのSendCustomNetworkEvent
に引数の受け渡し機能が追加されましたね!!
このアップデートによってSendCustomNetworkEvent
の使い方が少し変わったので、新しくまとめてみました
もくじ
-
新しいSDKにするには
-
新しいSDKでできるようになったこと
-
SendCustomEventとSendCustomNetworkEventの違い
-
SendCustomNetworkEventの簡単な使い方
-
新しいSDKで追加された便利なもの
0. 新しいSDKにするには
新しいSendCustomNetworkEventを使うにはVRChatSDK 3.8.1以上が必要です
まだアップデートしていない人はVCCからアップデートしておきましょう!
(VRChatSDK - Worldsと- Baseの2種類がありますが、両方ともアップデートしておけば良いと思います)
1. 新しいSDKでできるようになったこと
新しいSDKでは
-
SendCustomNetworkEvent
に引数の受け渡し機能の追加 -
NetworkEventTarget.Others
、NetworkEventTarget.Self
の追加 -
NetworkEventを呼び出したプレイヤーの取得
-
VRC.Udon.Common.Interfaces.NetworkEventTarget.~~~
を短く記述
ができるようになりました!
これ以外にも追加されたものはありますが、ここでは紹介しません...(難しく私にはわかりませんでした)
詳しくは公式サイトの説明をごらんください
2. SendCustomEventとSendCustomNetworkEventの違い
今回ここで主に取り扱うSendCustomNetworkEvent
と似たものとしてSendCustomEvent
というものがあります
両者の違いはネットワーク同期するかどうかです
SendCustomNetworkEvent
はネットワーク同期しますが、SendCustomEvent
はネットワーク同期しません
SendCustomNetworkEvent
を使うと自分以外のプレイヤーにもその動作を行わせることができますが、SendCustomEvent
だと自分にしかその動作を行わせることができません
(その代わりにSendCustomEventにはSendCustomEventDelayedSeconds
というn秒後にその動作を行わせることができる、というメソッドがあったりします)
また、新しいSDKではSendCustomNetworkEventで引数が使えるようになりましたが、SendCustomEventでは引数は使えません!!!
そのSendCustomEventで引数が使えないという制限の回避策として今回のアップデートでNetworkEventTarget.Self
が追加されたようです。そのことについては5で解説します
C#での呼び出しとの比較↓
C#での呼び出しとの比較
SendCustomEvent
は同期の機能がないもの、と説明しましたが同期が必要でない部分であればわざわざこれを使わなくても良かったりします
じゃあどうやるのといえば大したことではないのですが、C#のメソッドの呼び出しと同じように記述することでUdonSharpでも同じようにメソッドを呼び出すことができます
public class WhiteCube : UdonSharpBehaviour
{
void Start()
{
SendCustomEvent("Greet");
}
public void Greet()
{
Debug.Log("こんにちは!");
}
}
こんにちは!
上記の例はSendCustomEvent
を使った例ですが、これは以下のように書くこともできます
public class WhiteCube : UdonSharpBehaviour
{
void Start()
{
Greet();
}
public void Greet()
{
Debug.Log("こんにちは!");
}
}
こんにちは!
このようにどちらで書いても出力結果は変わりません
しかし両者には明確な差があります。
SendCustomEvent
を使用した場合、そのメソッドの返り値を受け取ることができません!!
2つ目の例のやり方ではそのメソッドの返り値を受け取ることが可能です
なのでぶっちゃけあんまりSendCustomEvent
を使う場所はない気がするのですが、SendCustomEventDelayedSeconds
にはしっかりと使う場面があります
その場面とは、
n秒後に指定の動作をさせたいというときです。
今まででUnityを触ったことのある人はコルーチン使えばいいやん、と思ったかもしれませんがUdonSharpではコルーチンは使用不可能です
具体的なコードについてはハツェさんの一定間隔で処理をするやつの正攻法と裏技 - ハツェの真時代傾向璋をごらんください
SendCustomNetworkEventの簡単な使い方
新しくなった機能を利用するにはVRC.SDK3をインポートするのが(おそらく)必須です!!
引数について
SendCustomNetworkEventの引数は
第1引数 :メッセージを送る対象
第2引数 :実際に実行させるメソッドの名前(String)
第3引数~:メソッドに渡す引数(任意)
となっています。
-
第1引数
メッセージを送る対象をここで指定します
選ぶことのできるものは以下のとおりです指定できる種類 意味 NetworkEventTarget.All そのワールドにログインしている全員に送信する NetworkEventTarget.Owner そのオブジェクトのオーナにのみ送信する NetworkEventTarget.Others 自分以外の全員に送信する NetworkEventTarget.Self 自分のみに送信する アップデートでは
Others
とSelf
が追加されたようです
また、従来のやり方ではVRC.Udon.Common.Interfaces.NetworkEventTarget.~~~
と、呪文レベルで長かった文章がNetworkEventTarget.~~~
で良くなっています(短縮した書き方を使用する場合には
using VRC.Udon.Common.Interfaces;
を追加しておきましょう)
-
第2引数
実行するメソッドの名前を指定します
名前はStringで与える必要があります。直接"hogehoge"
と指定しても良いですし、nameof(hogehoge)
として与えても良いです(公式的にはnameofを使用する方法を推奨している気がしますが、UdonSharpの記事では前者の直接文字列で指定する方法をよく見る気もします。まあどっちでもいいと思います)
-
第3引数以降
メソッドに渡す引数を設定することができます
ここで設定できる引数の上限数は8個までで、同期変数として使用できる型ならおそらく全て利用可能ですよく使いそうな型としては
bool、int、long、float、Vector2、Vector3、char、String、VRCUrl
などがサポートされています現在サポートされているすべての型を見るには公式サイトをごらんください
簡単な例
まず簡単な例をここで紹介します
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.SDK3;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
using VRC.SDK3.UdonNetworkCalling;
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class WhiteCube : UdonSharpBehaviour
{
void Start()
{
SendCustomNetworkEvent(NetworkEventTarget.All, "Greet", "hogehoge");
}
[NetworkCallable]
public void Greet(string name)
{
Debug.Log(name + "さん、こんにちは!");
}
}
hogehogeさん、こんにちは!
まず、使い始めるときにはusing VRC.SDK3;
とusingの部分に追加するようにしましょう!
これを追加することによって新しく追加されたものを簡単に利用できるようになります
そして呼び出したいメソッドは、必ずpublicで宣言するようにして、[NetworkCallable]
属性を付与するようにしましょう!
(引数を付けた呼び出しをする際にはこれが必須になります。引数を必要としないものの呼び出しではこの属性付与はおそらく必要ありませんが、公式ではつけるようにおすすめされているので引数が必要でない場合でもつけるようにしておきましょう)
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]について
この部分では共有変数の同期方法について設定しています
BehaviourSyncMode.Manual
を任意のものに変更することで同期方法を変更することができます
設定可能な値は以下のとおりです
モード | 意味 |
---|---|
None | 同期を行わない |
NoVariableSync | 動作に共有変数が強制されない...らしいです(よくわかりません) |
Continuous | 一定周期ごとに自動的に変数の同期を行う |
Manual | 手動で変数の同期を行う |
Continuousは自動的に変数の同期を行うが、Manualだと手動で設定したタイミングでしか同期が行われないので、ネットワーク的にManualを使うことがおすすめ、とされています(多分)
しかし、Manualモードの弊害として、VRC ObjectSync
コンポーネントが使用できなくなります。
ちなみにNoneにすると全く同期が行われないらしく、この状態ではSendCustomNetworkEvent
は使用できなくなります。
NoVariableSyncについては同期を行わないという点ではNoneと共通していますが、唯一違う点として、SendCustomNetworkEvent
が使用可能です
同期をオフにしたいけれどSendCustomNetworkEvent
を使いたい!というときにはNoVariableSyncを使いましょう
ちなみに[UdonBehaviourSyncMode(BehaviourSyncMode.~~~)]
のようなものを書かない場合、このようにインスペクターから同期方法を設定することができます
しかし、[UdonBehaviourSyncMode(BehaviourSyncMode.~~~)]
を書くと
このように設定項目がグレースケールになり、インスペクター上から変更ができなくなります
(ちなみに下の例はManualモードで固定しています)
コード側から指定したほうが安全性も増すと思うのでできるだけ[UdonBehaviourSyncMode(BehaviourSyncMode.~~~)]
という書き方を使ったほうが良いと思います
もっと詳しくこの同期方法について知りたい方はシン・U# 入門 ② - ハツェの真時代傾向璋を見ることをおすすめします!
別のクラスのメソッドを呼び出すには
SendCustomNetworkEventでは別のクラスのメソッドを呼び出すことができます!
(これができるようになると、「特定の物にインタラクトしたときに、別の物の位置を変える」というようなことができるようになります。)
以下はWhiteCubeにインタラクトしたときに、RedCubeの位置を少し上にするというサンプルです
WhiteCubeにはWhiteCube.csが、RedCubeにはRedCube.csがそれぞれアタッチされています
(見やすくなるようにサンプルコードではusingが省略されていますが、RedCube.csではちゃんとusing VRC.SDK3;
を追加しましょう!)
/*
呼び出す側のUdonです
*/
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class WhiteCube : UdonSharpBehaviour
{
[SerializeField]
private UdonBehaviour redCubeUdon;// RedCubeのUdonBehaviourをここでセット
public override void Interact()
{
redCubeUdon.SendCustomNetworkEvent(NetworkEventTarget.All, "MoveUp", 1);
}
}
/*
呼び出される側のUdonです
*/
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class RedCube : UdonSharpBehaviour
{
[NetworkCallable]
public void MoveUp(int value)
{
transform.position += new Vector3(0, value, 0);
}
}
別のクラスのメソッドを呼び出すには、そのクラスのインスタンス(UdonBehaviour)を取得することがまず必要です
この例では[SerializeField]属性を付与しているのでredCubeUdonはインスペクター上で指定できます
下の写真で青く囲まれている部分にRedCubeを指定しましょう
これで、WhiteCubeに対してインタラクトするとRedCubeが、SendCustomNetworkEvent
で指定した数値だけ上昇するようになりました!
(SendCustomNetworkEventでインスタンス内のプレイヤー全員にこのメッセージを送信しているため他のプレイヤーからも同じように見えるようになっています)
このようにすることで他のクラスのメソッドについてもSendCustomNetworkEvent
で呼び出せます
特定のプレイヤーにのみSendCustomNetworkEventを実行させる
SendCustomNetworkEvent
には残念ながら特定のプレイヤーにのみ送信する、といったものは用意されていません
しかし、引数にプレイヤーIDを与え、それをもとに「特定のプレイヤー」かどうか判定するといった手法を取ることでそれを回避することができます
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.SDK3;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
using VRC.SDK3.UdonNetworkCalling;
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class WhiteCube : UdonSharpBehaviour
{
public override void Interact()
{
VRCPlayerApi localPlayer = Networking.LocalPlayer;
Debug.Log("あなたのプレイヤーID: " + localPlayer.playerId);
SendCustomNetworkEvent(NetworkEventTarget.All, "Greet", "hoge", 2);
}
[NetworkCallable]
public void Greet(string name, int playerId)
{
VRCPlayerApi localPlayer = Networking.LocalPlayer;
if (localPlayer.playerId == playerId)
{
Debug.Log("こんにちは " + name + "さん! (Player ID: " + playerId + ")");
}
else
{
Debug.Log("プレイヤーIDが一致しません。");
}
}
}
上の例ではSendCustomNetworkEvent
の引数として 2 を指定しています
実際に使うときはこの部分を、その実行させたいプレイヤーのIDに変更すれば良いと思います
Greetメソッドではif文を使って、与えられたプレイヤーIDと自分のプレイヤーIDが同じかどうか判定しています
このとき注意してほしいことが一つあって、呼び出される側のメソッドを宣言するときにはデフォルト引数を使用することができません!
[NetworkCallable]
public void Greet(string name, int playerId = -1)
{
//何らかの処理
}
このようにint playerId = -1
として宣言することはできません
(ちなみにこれはコンパイル時にエラーになります)
4. 新しいSDKで追加された便利なもの
新しいSDKではSendCustomNetworkEvent
を呼び出したプレイヤーのVRCPlayerApiを取得できるようになりました!
具体的にはNetworkCalling.CallingPlayer
プロパティを使用します
このプロパティの値としてはネットワーク呼び出しが行われた場合には、その呼び出しを行ったプレイヤーのApiを、ネットワーク呼び出し出ない場合にはNoneを取得できます
以下はCallingPlayerプロパティを使用してSendCustomNetworkEventを呼び出したプレイヤーのプレイヤーIDを取得する例です
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.SDK3;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
using VRC.SDK3.UdonNetworkCalling;
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class WhiteCube : UdonSharpBehaviour
{
void Start()
{
SendCustomNetworkEvent(NetworkEventTarget.All, "Greet", "hogehoge");
}
[NetworkCallable]
public void Greet(string name)
{
VRCPlayerApi caller = NetworkCalling.CallingPlayer;
Debug.Log(name + "さん、こんにちは!" + "プレイヤーIDは " + caller.playerId + " です。");
}
}
hogehogeさん、こんにちは!プレイヤーIDは1です。
もうちょっと細かい仕様説明
一部今までで説明した内容と被る部分もありますが、SendCustomNetworkEvent
を使うときにいくつか注意しないといけないことがあります
- 引数を使った呼び出しを使う際は
[NetworkCallable]
の付与が必須- 以前までの互換性を保つために引数を使わない場合には付与しなくても良くなっている
- 呼び出し先のメソッドではオーバーロードが使用不可
- アクセス修飾子
public
の付与が必須 - 呼び出し先のメソッドではデフォルト引数の設定ができない
public void Greet(string name, int playerId = -1)
- 一度に送れるデータの上限値は16KBまで
- 1KBを超えるデータは複数に分割されて送信される
- 同期モードをNoneにした場合は
NetworkEventTarget.Self
にしても動作させることはできない
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]//←同期モードがNoneになっている!
public class WhiteCube : UdonSharpBehaviour
{
public override void Interact()
{
SendCustomNetworkEvent(NetworkEventTarget.Self, "Greet");
}
[NetworkCallable]
public void Greet()
{
Debug.Log("Hello from WhiteCube!");
}
}