持てるコップを作りました。ところが友達と入ってみると、自分が机に置いたコップが、相手の画面ではまだ床に落ちたままです。
Pickupは掴めるようにするだけの部品で、位置を共有する機能は持っていません。 これを足すのが VRC Object Sync です。
この記事では、投げた物の位置が全員でそろう ところまでを作ります。あわせて、位置以外の状態は別扱いになる理由も見ていきます。
この記事でわかること
- Object Syncが運ぶもの、運ばないもの
- Pickupと組み合わせたときの所有権の動き
- 初期位置へ戻す方法
- 付けるべきもの、付けるべきでないもの
Pickupで持てる懐中電灯を作る と ネットワーク同期入門 を試した状態から始めます。
運ばれるのは位置と回転だけ
VRC Object Sync は、そのオブジェクトの 位置と回転 を、所有者から他の全員へ送り続ける部品です。

運ばれるのはこれだけです。色が変わった、ライトが点いた、といった状態は含まれません。
ここを取り違えると、「持ち運びは共有されるのに、懐中電灯の光だけ自分にしか見えない」という状態になります。光は別に同期する必要があるからです。
構成はこうなります。
| 部品 | 役割 |
|---|---|
| Collider + Rigidbody | ぶつかる、落ちる |
| VRC Pickup | 掴めるようにする |
| VRC Object Sync | 位置と回転を共有する |

もう1つ、所有権が自動で移る のがPickupの便利なところです。掴んだ瞬間にその人がOwnerになるので、位置を送る役も自動的に切り替わります。追加のコードは要りません。
実践:投げられるコップを作る
持てるコップを作って、投げた位置が全員で同じになるようにします。
1. コップを作る
「3D Object → Cube」で SharedCup を作り、(0, 1, 1)、Scale (0.15, 0.2, 0.15) に置きます。
「Add Component」で次を追加します。
- VRC Pickup(Rigidbodyも一緒に付きます)
- VRC Object Sync
VRC Pickupの設定は、Auto Hold をオン、Pickupable をオンにします。
Rigidbodyは Use Gravity をオンのまま、Is Kinematic はオフのままです。投げたら落ちる、という自然な動きになります。
追加するのはこれだけです。 コードは書きません。
2. 2人で確かめる
Build & Test で Number of Clients を 2 にして起動します。
| 順序 | 操作 | 期待する結果 |
|---|---|---|
| 1 | AとBが入る | 両方の画面で、同じ場所にコップがある |
| 2 | Aが掴む | Aの手にコップが移る。Bの画面でも、Aの手のあたりに動く |
| 3 | Aが投げる | 両方の画面で、同じように飛んで落ちる |
| 4 | Bが拾う | 所有権がBに移る。Aの画面でもBの手に見える |
| 5 | Bが持ったままAが退出 | コップはBの手に残る |
コードを1行も書いていないのに、ここまで動きます。 Pickupが所有権を運び、Object Syncが位置を運んでいるからです。
3. 初期位置に戻せるようにする
投げたコップが床の下や壁の向こうへ行くと、拾えなくなります。戻すボタンを作っておくと安心です。

「3D Object → Cube」で ResetButton を作り、(1.5, 1, 1) に置きます。
Assets/Scripts で CupResetter を作ります。
using UdonSharp;
using UnityEngine;
using VRC.SDK3.Components;
using VRC.SDKBase;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class CupResetter : UdonSharpBehaviour
{
[SerializeField] private VRCObjectSync targetSync;
public override void Interact()
{
if (targetSync == null) return;
// 戻す前に、自分が所有者になる
if (!Networking.IsOwner(targetSync.gameObject))
{
Networking.SetOwner(Networking.LocalPlayer, targetSync.gameObject);
}
targetSync.Respawn(); // 最初の位置へ戻す
Debug.Log("[CupResetter] 戻しました");
}
}
Respawn() は、Object Syncが覚えている最初の位置 へ戻すメソッドです。位置を自分で記録しておく必要はありません。
戻す前に所有権を取っている のがポイントです。位置を動かせるのは所有者だけなので、他の人が持っているコップを戻したいときは、まず所有権を取ります。
ResetButton に Udon Behaviour を追加し、Target Syncに SharedCup をドラッグします。
位置以外の状態は、自分で同期する
ここで、Pickupで作った懐中電灯 を思い出してください。持ったままUseで点灯する仕掛けでした。
これに Object Sync を足すと、持ち運びは共有されますが、光は自分にしか見えません。
理由はもう分かると思います。Object Syncが運ぶのは位置と回転だけで、Light.enabled は運ばれないからです。
光も共有するには、同期変数を1つ足します。
[UdonSynced] private bool beamOn;
public override void OnPickupUseDown()
{
// 持っている人が所有者になっているので、そのまま書ける
beamOn = !beamOn;
ApplyBeam();
RequestSerialization();
}
public override void OnDeserialization()
{
ApplyBeam();
}
private void ApplyBeam()
{
if (beam != null) beam.enabled = beamOn;
}
同期入門とまったく同じ形 です。状態を1つ持って、受け取ったら反映する。対象が扉からライトに変わっただけです。
なお、この場合の同期モードは Manual にします。位置は Object Sync が別に運んでくれるので、こちらは「変わったときだけ」で足ります。
うまくいかないときは
- 位置が共有されない → VRC Object Syncが付いているか確認します。Pickupだけでは共有されません
- 持ったのに相手の画面で動かない → 所有権が移っていない可能性があります。Pickupが正しく付いているか確認します
- 物が震える、飛んでいく → Rigidbodyの設定を見ます。複数のスクリプトが同じ物の位置を触っていないかも確認します
- 戻すボタンが効かない → 所有権を取ってから
Respawn()を呼んでいるか確認します - 光だけ共有されない → 仕様です。位置以外の状態は自分で同期します
おまけ:先に知っておくと良いこと
- 数を絞る: Object Syncは位置を送り続けるので、数が増えるほど通信が増えます。転がる小物は数個までにして、動かない飾りには付けません
- 動かない物には付けない: 家具や壁に付けても意味がなく、通信だけが増えます
- スイッチや椅子には合わない: 「全部にObject Syncを付ければ共有できる」という誤解がありますが、位置を送る部品なので、状態を共有したいものには向きません。そちらは同期変数です
- 物理は完全には一致しない: 各自のPCで物理計算が走るので、細かい転がり方は少しずつ違い ます。位置は定期的にそろえられますが、途中の動きは厳密には同じになりません
- 使いどころはいろいろ: ボードゲームの駒、投げて遊ぶボール、カフェのコップ、脱出ゲームの鍵。「動かして、その位置を共有したい」ものすべてです
まとめ
Object Syncは、位置を運ぶ専用の部品です。
- 運ぶのは位置と回転だけ。点灯などの状態は運ばれない
- Pickupと組み合わせると、掴んだ人が自動で所有者になる
Respawn()で最初の位置へ戻せる。所有権を取ってから呼ぶ- 位置以外の状態は、同期変数で自分で共有する
付ける前の問いかけは、「これは、動いた位置を共有したい物か」 です。状態を共有したいだけなら、同期変数のほうが向いています。
各自の設定を次回まで残すなら PlayerDataで音量を保存する、決まった数の物を貸し出すなら VRCObjectPool へ進んでください。