【VRChat】同期する扉を作る:全員の画面で同じ開閉状態にする

作成: 2025-12-19最終更新: 2026-09-09

ボタンで開閉する扉を、全員の画面でそろえます。Animatorの状態ではなくboolを同期する理由、途中参加の人にも伝わる仕組み、所有権の扱いを解説します。

ボタンで開く扉を作りました。ところが友達と入ってみると、自分の画面では開いているのに、相手の画面では閉じたままです。

扉のように 全員が同じ状態を見るべきもの は、開閉を共有する必要があります。しかも、あとから入ってきた人にも今の状態が伝わらないと、片方だけ壁があるように見えてしまいます。

この記事では、全員の画面で同じ開閉状態になる扉 を作ります。

片方が押した扉が、両方の画面で開いている

この記事でわかること

  • Animatorの状態ではなく、boolを同期する理由
  • 途中参加の人にも伝える仕組み
  • 押した人が所有権を取る形
  • 同時に押されたときの挙動

ネットワーク同期入門AnimatorとUdonで扉を動かす を試した状態から始めます。

Sponsored


送るのは「開いているか」だけ

扉を同期しようとすると、「アニメーションの状態を全員に送る」という発想が浮かびます。その必要はありません。

送るのは、isOpen という真偽の値1つだけです。

開いているかどうかのboolだけを送り、受け取った各自がAnimatorに反映する

理由は3つあります。

  • 同期できるのは単純な値だけ: Animatorの状態そのものは送れません
  • 通信が軽い: 真偽1つなら、送るデータはごくわずかです
  • 途中参加に強い: 同期変数には最新の値が届くので、あとから入った人も「いま開いている」ことが分かります

流れはこうです。

押した人: isOpen を反転 → 自分のAnimatorに反映 → 送信を頼む
受け取った人: isOpen が届く → 自分のAnimatorに反映

各自が、受け取った値を見て自分の扉を動かします。 これは ネットワーク同期入門 のライトと、まったく同じ形です。対象がライトから扉に変わっただけです。

Sponsored

実践:全員で開く扉を作る

ボタンを押すと、全員の画面で扉が開きます。あとから入った人にも、その時点の状態が伝わります。

1. 扉とボタンを用意する

AnimatorとUdonで扉を動かす と同じ構成を作ります。まだなら、そちらの手順で用意してください。

DoorPivot(空のオブジェクト。扉の端。Animatorが付いている)
└─ DoorPanel(扉の板)

Animator Controllerには、IsOpen というBoolのパラメータと、DoorClosed / DoorOpen の2つの状態があります。遷移のHas Exit Timeは外してあります。

ボタンは DoorButton として、扉のそばに置きます。

2. コードを書く

Assets/Scripts で「Create → U# Script」を選び、SyncedDoor を作ります。

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]   // 変わったときだけ送る
public class SyncedDoor : UdonSharpBehaviour
{
    [SerializeField] private Animator doorAnimator;

    [UdonSynced] private bool isOpen;   // 全員で共有する値

    private void Start()
    {
        ApplyState();   // 手元の値を、まず反映しておく
    }

    public override void Interact()
    {
        if (!Utilities.IsValid(Networking.LocalPlayer)) return;

        // 1. 所有権を取る(押した人が書ける人になる)
        if (!Networking.IsOwner(gameObject))
        {
            Networking.SetOwner(Networking.LocalPlayer, gameObject);
        }
        if (!Networking.IsOwner(gameObject)) return;

        // 2. 値を変えて、自分の画面に反映する
        isOpen = !isOpen;
        ApplyState();

        // 3. 送信を頼む
        RequestSerialization();

        Debug.Log("[SyncedDoor] open=" + isOpen);
    }

    // 値が届いたときに呼ばれる(押した本人では呼ばれない)
    public override void OnDeserialization()
    {
        ApplyState();
    }

    // 値を見て、扉の状態を作る。何度呼んでも同じ結果になる
    private void ApplyState()
    {
        if (doorAnimator != null)
        {
            doorAnimator.SetBool("IsOpen", isOpen);
        }
    }
}

構造は、同期入門のライトと同じです。変わるのは ApplyState() の中身だけ で、targetLight.enabledSetBool に置き換わっています。

同じ形が使い回せることに気づくと、同期は一気に扱いやすくなります。「状態を1つ持って、それを見て見た目を作る」 という設計にしておけば、対象が何であれ同じ書き方で同期できます。

ApplyState()Start() からも呼んでいるのが、途中参加への対応です。あとから入った人は、届いた値を持った状態で Start() を迎えるか、OnDeserialization() で受け取ります。どちらでも扉が正しい状態になります。

3. 割り当てる

DoorButton に Udon Behaviour を追加し、SyncedDoor を指定します。

指定するもの
Door AnimatorHierarchyの DoorPivot
Interaction Text扉を開ける
扉の軸をAnimatorの欄へドラッグする

Sync Modeの欄は表示されないか、Manualになっています。コードの属性で指定しているためです。

2人で確かめる

ClientSimで自分の画面が動くことを確認したら、実際のVRChatで確かめます。

2つのクライアントで、両方の画面がそろうかを見る

SDKのBuilderで Number of Clients を 2、Force Non-VR を有効にして Build & Test を実行します。

順序操作期待する結果
1AとBが入る両方とも扉が閉じている
2Aがボタンを押すAの扉が開き、少し遅れてBの扉も開く
3Bがボタンを押すBが所有権を取り、両方とも閉じる
4Aが開けたあと、Bが入り直すBは押していないのに、開いた状態で入る

4番目が、この記事でいちばん大事な確認です。 同期変数を使っているので、あとから来た人にも最新の状態が届きます。

もしここで扉が閉じて見えるなら、Start()OnDeserialization()ApplyState() を呼んでいるか確認してください。

うまくいかないときは

  • 自分の画面も動かない → 同期以前の問題です。Animatorのパラメータ名、Door Animatorの割り当てを確認します
  • 自分だけ動いて、相手に届かないManual の指定と RequestSerialization() を確認します
  • 押した人だけ動かないInteract() の中でも ApplyState() を呼んでいるか確認します。OnDeserialization() は押した本人では呼ばれません
  • 途中参加すると閉じているStart()ApplyState() を呼んでいないか、Network Eventだけで伝えています
  • 相手が押すと戻る → 別のスクリプトが同じAnimatorを触っていないか確認します
Sponsored

おまけ:先に知っておくと良いこと

  • 同時に押されたときは、最後の操作が残る: 2人がほぼ同時に押すと、どちらかが先に所有権を取ります。扉なら最終的に全員そろうので実害はありません。取りこぼしが困る用途(得点など)では、別の設計が要ります
  • 自動ドアはもう一段むずかしい: 「範囲に入ったら開く」を同期させるには、「いま範囲に誰かいるか」を全員で共有する必要があります。人数を数える処理と、退出したときの引き継ぎが絡むので、この記事の範囲を超えます
範囲に人がいるかどうかを共有すると、扱う情報が増える
  • 鍵のかかる扉も同じ形: 「開いているか」に加えて「鍵が開いているか」を同期変数に足すだけです。状態が増えても書き方は変わりません
  • 音は各自で鳴らす: 開くときの音は、ApplyState() の中で鳴らせば全員の画面で鳴ります。音そのものを同期する必要はありません
  • 使いどころはいろいろ: 脱出ゲームの扉、イベント会場の入場ゲート、隠し部屋の仕掛け、店舗ワールドのシャッター。どれも同じ形です

まとめ

同期する扉は、同期入門のライトと同じ形で作れます。

  • 送るのは「開いているか」の真偽1つだけ
  • 押した人が所有権を取ってから、値を変えて送信を頼む
  • 見た目への反映は ApplyState() にまとめ、起動時・押したとき・受信時から呼ぶ
  • 同期変数なら、途中参加の人にも最新の状態が届く

作るときの問いかけは、「この扉の状態は、あとから来た人にも見えていてほしいか」 です。イエスなら同期変数です。

持てる小物の位置を共有するなら VRC Object Sync、一瞬の演出を伝えるなら Network Events へ進んでください。

VRChat このセクションのノート63