はじめに
クライアント間で値を共有し、変化を受け取り、全員が一度だけ処理するメッセージを送ります。
インストール
まず、VCC(VRChat Creator Companion)でネットワーキングパッケージを Unity プロジェクトに追加します。プロジェクトの vccproject.json を開いてパッケージを追加するか、VCC インターフェースを使って直接インストールしてください。
インストール後、パッケージはプロジェクトで利用可能になり、以下のネットワーキング API を使い始めることができます。
値を共有する
public class Scoreboard : UdoniteBehaviour
{
private Synced<int> score = new Synced<int>();
public override void Interact()
{
score.Value = score.Value + 1;
}
}
Synced<T> はビヘイビアのフィールドです。.Value に書くと保存され、複製が依頼されます。シリアライズの呼び出しを覚えておく必要も、同期モードの属性と対にする必要もありません。
書けるのはオブジェクトのオーナーだけです。 所有していないクライアントでの代入はそのクライアントに留まります。誰でも変更してよいなら、先にオーナーシップを取ってください。
public override void Interact()
{
Ownership.Claim(gameObject);
score.Value = score.Value + 1;
}
変化を受け取る
public override void Start()
{
score.Subscribe(OnScoreChanged);
}
private void OnScoreChanged(int value)
{
label.text = value.ToString();
}
ハンドラーは書いたクライアントも含めて全員で動くので、表示の更新を 2 か所に書かずに済みます。呼ばれるのは更新がすべて届いたあとです。同期値が 2 つ以上あるときに効いてきます。ハンドラーの中で別の同期値を読むと、同じ更新で届いた値が読めます。
ハンドラーは private で構いません。自分のビヘイビアの中から呼ばれるので、外から名前で呼べる必要はありません。
メッセージを送る
メッセージは、送りたいフィールドを持つクラスです。
public class Scored : NetworkEvent
{
public int playerId;
public int points;
public override void Serialize(ByteWriter writer)
{
writer.WriteInt32(playerId);
writer.WriteInt32(points);
}
public override void Deserialize(ByteReader reader)
{
playerId = reader.ReadInt32();
points = reader.ReadInt32();
}
}
送って、受け取ります。
public override void Start()
{
Room.Subscribe<Scored>(OnScored);
}
public override void Interact()
{
Room.Send(new Scored { playerId = 1, points = 10 });
}
private void OnScored(Scored message)
{
Debug.Log(message.points + " to player " + message.playerId);
}
Room.Send はルーム全体に送ります。 static なので、ビヘイビアからでもただのクラスからでも呼べます。特定のオブジェクトについてのメッセージはそのオブジェクトの上を流れ、オブジェクトそのものであるビヘイビアに this.Send と書きます。そちらはそのオブジェクトの所有が要ります。どちらもメッセージにあります。
誰でも送れます。 同期値と違ってオーナーである必要がありません。だからメッセージは「誰かが何かをした」に向いていて、変数は「今どうなっているか」に向いています。
全クライアントが、送られた順に一度ずつ受け取ります。送信者自身も受け取ります。あとから入った人は、見ていない分を受け取りません。イベントに求めるのはそれで、値に求めるものとは逆です。
誰が送ったか
必要なときは引数をもう 1 つ取ります。
private void OnScored(VRCPlayerApi sender, Scored message)
{
Debug.Log(sender.displayName + " scored");
}
同じメッセージ型に両方の形で購読でき、どちらも呼ばれます。多くのハンドラーは送信者を必要としないので、すべてのシグネチャには入れていません。
次に読むもの
配列や、コンパイラからは見えない 1 つのケースを含む Synced<T> の全体は同期値にあります。宛先、オブジェクトごとのチャンネル、通信量はメッセージにあります。