概要
VRChatの最大の魅力は、複数のプレイヤーが同じ空間で同じ体験を共有できることです。あるプレイヤーがボタンを押したら、他の全員の視界でもドアが開く。これが実現できるのは、ネットワーク同期という仕組みがあるからです。
UdonSharpにおけるネットワーク同期は、一見複雑に見えますが、いくつかの基本的な概念を理解すれば、その仕組みは非常に明快です。本記事では、マルチプレイヤー対応ギミックを作成する上で不可欠 な以下の3つの核心的要素について解説します。
- 所有権 (Ownership): 誰がそのオブジェクトを操作する権利を持っているか。
- 同期変数 (
[UdonSynced]): プレイヤー間で共有される変数。 - シリアライゼーション: データが実際にネットワークを介して同期されるプロセス。
1. 所有権 (Ownership) - 「このオブジェクトは誰のもの?」
VRChatのワールドに存在する全てのGameObjectには、必ず一人の所有者 (Owner) がいます。所有者とは、そのオブジェクトの状態を自由に変更し、その変更を他のプレイヤーに通知する権限を持つプレイヤーのことです。
- デフォルトの所有者: ワールドに最初から配置されているオブジェクトの所有者は、そのワールドのインスタンスを立てた人(Master)です。
- 所有権のルール: 同期変数の値を変更できるのは、そのオブジェクトの所有者だけです。所有者でないプレイヤーが値を変更しようとしても、その変更はローカル(自分だけ)のものであり、他のプレイヤーには伝わりません。
この「所有者だけが変更できる」というルールが、複数のプレイヤーが同時に同じオブジェクトを操作しようとしたときに、状態が混乱するのを防ぐための基本的な仕組みです。
所有権の取得方法
プレイヤーがオブジェクトを操作するギミックを作る場合 、まずそのプレイヤーがオブジェクトの所有者になる必要があります。所有権を取得するには、Networking.SetOwner()メソッドを使用します。
Networking.SetOwner(VRCPlayerApi player, GameObject obj);
player: 新しい所有者となるプレイヤー。通常は自分自身、つまりNetworking.LocalPlayerを指定します。obj: 所有権を取得したいGameObject。通常はスクリプトがアタッチされているオブジェクト自身、つまりgameObjectを指定します。
このメソッドは、プレイヤーがオブジェクトに対して何らかのアクションを起こしたとき(例: Interact)に呼び出すのが一般的です。
public override void Interact()
{
// このオブジェクトの所有権を自分(ローカルプレイヤー)に設定する
Networking.SetOwner(Networking.LocalPlayer, gameObject);
// ... この後に同期変数を変更する処理を記述 ...
}
2. 同期変数 ([UdonSynced]) - 全員で共有するデータ
プレイヤー間で共有したい変数は、宣言の直前に[UdonSynced]という属性を付けます。これにより、その変数は「ネットワーク同期の対象である」とUdonに認識されます。
using UdonSharp;
using UnityEngine;
public class SyncedVariableExample : UdonSharpBehaviour
{
// このbool値は、インスタンス内の全プレイヤーで同期される
[UdonSynced]
private bool isLightOn = false;
}
[UdonSynced]を付けられる変数の型には制限がありますが、int, float, bool, string, Vector3, Quaternion, Colorなど、基本的なデータ型の多くがサポートされています。
3. シリアライゼーション - データ同期のプロセス
[UdonSynced]属性を付けた変数の値は、以下のプロセスを経て全プレイヤーに同期されます。この一連の流れをシリアライゼーション (Serialization) と呼びます。
- 所有権の取得: プレイヤーAがオブジェクトを操作し、
Networking.SetOwner()で所有者になります。 - 同期変数の変更: プレイヤーA(所有者)が、
[UdonSynced]変数の値を変更します。(例:isLightOn = true;) - シリアライゼーションの要求: プレイヤーAが
RequestSerialization()を呼び出します。これは「変数の値が変更されたので、このデータを他の全員に送ってください」というリクエストです。 - データの送信: VRChatのサーバーが、プレイヤーAから送られてきた新しい変数のデータ(シリアライズされたデータ)を、インスタンス内の他の全プレイヤー(プレイヤーB、C、...)に送信します。
- デシリアライゼーション: データを受け取った他のプレイヤー(B、C、...)のUdon VMが、
OnDeserialization()イベントを自動的に呼び出します。このイベントが発生した時点で、彼らの持つ[UdonSynced]変数の値は、プレイヤーAが設定した新しい値に更新されています。 - 状態の反映:
OnDeserialization()メソッド内で、更新された変数の値に基づいてワールドの状態(例: ライトを点灯させる)を更新する処理を記述します。
実装例:同期するスイッチ
このプロセスを、クリックするとライトがON/OFFする同期スイッチで見てみましょう。
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
public class SyncedSwitch : UdonSharpBehaviour
{
public GameObject targetLight;
[UdonSynced]
private bool isLightOn = false;
void Start()
{
// 初期状態を反映
UpdateLightState();
}
public override void Interact()
{
// 1. 所有権を取得
Networking.SetOwner(Networking.LocalPlayer, gameObject);
// 2. 同期変数を変更
isLightOn = !isLightOn;
// 3. 変更を全プレイヤーに通知するよう要求
RequestSerialization();
// 自分自身の見た目も即座に更新
UpdateLightState();
}
// 5. 他のプレイヤーでデータが更新されたときに呼び出される
public override void OnDeserialization()
{
// 6. 更新された状態を反映
UpdateLightState();
}
private void UpdateLightState()
{
if (targetLight != null)
{
targetLight.SetActive(isLightOn);
}
}
}
このコードでは、Interactしたプレイヤーが値を変更し、RequestSerialization()で通知します。他のプレイヤーはOnDeserializationで通知を受け取り、UpdateLightState()を呼び出すことで、全員のライトの状態が一致します。
まとめ
- ネットワーク同期の基本は所有権 (Ownership) です。同期変数を変更できるのは所有者だけです。
- プレイヤーがオブジェクトを操作する際は、まず
Networking.SetOwner()で所有権を取得します。 - プレイヤー間で共有したい変数の前には
[UdonSynced]属性を付けます。 - 所有者が同期変数の値を変更したら、
RequestSerialization()を呼び出して変更を通知します。 - 他のプレイヤーは
OnDeserialization()イベントで変更を受け取り、ワールドの状態を更新します。
この「所有権取得 → 値の変更 → 通知 → 受信して更新」という流れが、UdonSharpにおけるネットワーク同期の最も基本的なパターンです。このパターンを理解することが、マルチプレイヤー対応ギミックを制作するための第一歩となります。
次の記事では、スクリプト同士で連携するための「メソッドとカスタムイベント」について解説します。