加藤祐介ブログ

VRChatのワールドでオブジェクトと変数の同期を行う

記事投稿日: 2025年11月09日(日)

最終更新日: 2025年11月09日(日)

目次

はじめに

この記事では、VRChatのワールド作成における各ユーザ間の同期処理について紹介します。

オブジェクトを配置するだけの静的なワールドの場合は同期処理は必要ありませんが、 複数人のユーザが参加するゲームや、 動的なギミックがあるワールドの場合は、同期処理が必要になるかと思います。

同期処理について何かしら参考になれば幸いです。

ワールド作成中の様子

開発環境

開発環境は下記の通りです。

UnityやVRChat SDKのバージョンが異なると同じように動かない場合があります。

前回の記事

前回はVRChatのAndroidアプリ用にアバターをアップロードする方法を紹介しました。

VRChatのスマホアプリ(Android)用にアバターをアップロードする

ワールドについて

VRChatのワールドでは複数人のユーザが同じインスタンスに入っていても、 厳密にはそれぞれのユーザのローカル環境に別々のワールドが構築されるようです。 つまり、同期処理を実装していない場合は、 それぞれのユーザがローカル環境のワールドで何か変更を加えても、 その変更は他のユーザに共有されないことになります。

したがって、各ユーザでワールド内のオブジェクトの動きを共有したい場合などは、 同期処理の機能が必要になります。

また、ワールドの仕様として、 ワールドのインスタンスに一番最初に入ったユーザが「Master」となり、 オブジェクトのオーナー権限(所有権)を初めに取得します。 オブジェクトのオーナー権限をMasterユーザ以外に移したい場合は、 UdonスクリプトやVRChatのコンポーネントなどで移すことができます。

同期処理においては、オブジェクトのオーナー権限を持っているユーザがそのオブジェクトに変更を加えた時に、その変更が他のユーザに共有されます。 つまり、同期処理を考える際は、同期したいオブジェクトのオーナー権限を誰が持っているかを意識する必要があります。

このあたりの話は下記の公式サイトに記載があります。

VRChat Creation - Networking

Udonについて

UdonはVRChat用のプログラミング言語です。 基本的にはもともとUnityで使用できるC#がベースで、その上でVRChat用のライブラリも使えるみたいな感じかと思います。 Unityエディタ上で扱えるGUIも用意されているので、プログラムを書かなくても様々な処理を実装できます。

Udonを使えばインタラクティブなワールド(ユーザがオブジェクトを操作したり、ワールド内でゲームが遊べたりなど)を作成できます。

Udonの公式情報は下記です。

VRChat Creation - Udon

ワールド用プロジェクトを作成

VCCやALCOMでVRChatのワールド用のUnityプロジェクトを作成します。 この時、VRChat SDKが追加されていることを確認しておきます。

ワールド作成

オブジェクトの位置と姿勢を同期させる

オブジェクトの位置(position)と姿勢(rotation)を同期させる方法を紹介します。

まず適当な3Dオブジェクト(ここではsphere)を作成します。 そして、そのオブジェクトのInspector画面で「Add Component」ボタンを押して、「VRC Pickup」と「VRC Object Sync」を追加します。

VRC Pickupを追加するとそのオブジェクトをユーザが持てるようになります。 また、このVRC Pickupを付けると、そのオブジェクトを持ったユーザが自動的にそのオブジェクトのオーナー権限を取得できます。

VRC Object Syncはオブジェクトに追加するだけで、そのオブジェクトの位置や姿勢が同期されます。 ただし、オブジェクトのオーナー権限を持っているユーザがそのオブジェクトを動かした時のみに同期されるので注意が必要です。 今回はVRC Pickupをオブジェクトに付けているため、そのオブジェクトを持ったユーザがオーナー権限を取得して同期が成功します。

VRC Pickupを使わない場合は、Udonのスクリプトを書くなどして、オブジェクトのオーナー権限を移動させる処理の実装が必要になるかと思います。

コンポーネントをアタッチ

ちなみに補足として、3DオブジェクトにはデフォルトでRigidbodyコンポーネントが追加されており、「Use Gravity」がONになっています。 このままだとオブジェクトに重力が働き、地面に落ちます。 オブジェクトを空中に浮かせたい場合はこのUse Gravityをオフにしておきます。

Use Gravity設定

設定が完了したらUnityエディタの再生ボタンを押してVRChatワールドのシミュレーションを行います。 オブジェクトをクリックすると持つことができるかと思います。 同期処理がうまくいっているかは実際にVRChatにワールドをアップロードして動作確認するとよいかと思います。

Unity上でも複数ユーザのシミュレーションができそうですが、まだよく分かっていません。 また、実際にVRChat上で動作確認したほうが確実かと思います。

Unity上で動作確認

以上でオブジェクトの位置と姿勢の同期は完了です。

Udonのスクリプトの変数を同期させる

Udonには変数を同期させる機能が備わっています。

まず、Udonのスクリプトを作成します。 先ほどの3DオブジェクトのInspector画面で「Add Component」ボタンを押して、 「Udon Behaviour」を追加します。 それからUdon Behaviourで「Udon C# Program Asset」を選択してから「New Program」ボタンを押します。 スクリプト名を入力するとUdonのスクリプトが作成できます。

Udon Behaviour

ここでは「chance_number」というスクリプトを作成しています。

スクリプト

作成したスクリプトをエディタで開くと下記のようなプログラムになっているかと思います。

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class chance_number : UdonSharpBehaviour
{
    void Start()
    {
    }
}

プログラムを下記のように書き換えます。

処理の内容は、ワールド作成時に0~15の範囲でランダムにIDを取得しています。

変数の前に[UdonSynced]を付けると、その変数を同期させることができます。

同期処理はMasterユーザ(このオブジェクトのオーナー権限を持っているユーザ)が変更を加えた時のみ、他のユーザに同期されます。 そのため、 if (Networking.IsMaster) {の条件分岐処理で、 MasterユーザだけがIDの取得処理を実行します。

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

[UdonBehaviourSyncMode(BehaviourSyncMode.Continuous)]
public class chance_number : UdonSharpBehaviour
{
    private const int MAX_OBJECTS = 16;
    [UdonSynced] public int chance_id = 0;

    void Start()
    {
        if (Networking.IsMaster) {
            chance_id = Random.Range(0, MAX_OBJECTS);
        }
    }
}

以上のプログラムを書くことで変数をユーザ間で同期させることができます。

ただし、注意点として、変数の同期は(おそらく)オブジェクトごとにしか行われません。 つまり、各ユーザごとの同じオブジェクトに付いている同じスクリプト内の変数が同期されます。

たとえば、今回のchance_idを他のスクリプトでも参照したい場合は、 下記のように対象のスクリプトから対象の変数にアクセスします。 これはUdonの機能というよりもUnityのC#の書き方になります。

[SerializeField] UdonSharpBehaviour chance_number_script;の行を書くことで、 Unityエディタ上のInspector画面で対象のスクリプト(chance_number)を設定できます。

その後、int chance_id = (int)chance_number_script.GetProgramVariable("chance_id");の行でchance_numberスクリプトからchance_id変数の値を取得します。

補足として、VRCPlayerApi player = Networking.GetOwner(this.gameObject);の行でオブジェクトを持っているユーザの情報を取得しています。 ユーザに対して何か変更を加えたい場合はこのようにユーザ情報を取得する必要があります。 ここではユーザに強制的にジャンプさせています(かなり強くジャンプさせる)。

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class sphere_button : UdonSharpBehaviour
{
    private const float jump_force = 50f;
    [SerializeField] private int button_id = 0;
    [SerializeField] UdonSharpBehaviour chance_number_script;

    public override void OnDrop()
    {
        int chance_id = (int)chance_number_script.GetProgramVariable("chance_id");
        if (button_id == chance_id)
        {
            VRCPlayerApi player = Networking.GetOwner(this.gameObject);
            Vector3 upward_velocity = new Vector3(0f, jump_force, 0f);
            player.SetVelocity(upward_velocity);
        }
    }
}

以上で変数の同期は完了です。

おわりに

今回はVRChatのワールド作成におけるオブジェクトの位置と姿勢の同期方法と、 Udonスクリプトの変数の同期方法を紹介しました。 ワールド作成を行う上で同期処理はかなり重要な機能ではありますが、 意外とややこしかったので、まとめてみました。 何かしら参考になれば嬉しい限りです。 それでは、また。

次回の記事

次回はアバターに座ったり寝っ転がったりするモーションを設定するためにGoGo Locoを導入する方法を紹介します。

VRChatのアバターにGoGo Locoを導入して座ったり寝っ転がったりする

お知らせ

過去のお知らせ

シリーズ記事一覧

各シリーズの記事を下記にまとめてあります。

我が家のインコ「れもん&ぽぽ&ぐぐ&さん」の日記 我が家のインコ「れもん&ぽぽ&ぐぐ&さん」の日記22 : 我が家にさんちゃんがやってきた 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記21 : 2ヶ月ぶりのインコ日記 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記20 : 久しぶりのインコ日記 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記19 : 鳥フェス浅草2025に行ってきました! 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記18 : 2024年に感謝&2025年もよろしくお願いします 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記17 : れもんもぐぐも平和な日々を送っています 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記16 : れもんが本に掲載されたり、愛鳥祭に行ったりなど 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記15 : 落花生、新聞紙、れもんとぐぐの邂逅 インコの飼い方と注意点(2024年版) 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記14 : 大人に近づくれもんと遊ぶ余裕が出てきたぐぐ 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記13 : 生後4ヶ月を迎えたれもんと我が家に慣れてきたぐぐ 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記12 : ぽぽが亡くなりました、そしてぐぐがやってきました インコ仲間「れもん&ぽぽ」の日記11 : ズグロシロハラインコのぽぽ、我が家へ セキセイインコ「れもん」の日記10 : 生後100日のれもん、ついに喋る セキセイインコ「れもん」の日記9 : 換羽期のれもん セキセイインコ「れもん」の日記8 : 病院へ挑戦! セキセイインコ「れもん」の日記7 : 外出用ゲージにチャレンジ! セキセイインコ「れもん」の日記6 : 新宿ことり博に行ってきました! セキセイインコ「れもん」の日記5 : 3週間経って順調に成長している模様 セキセイインコ「れもん」の日記4 : 鳥フェス千葉2024に行ってきました! セキセイインコ「れもん」の日記3 : 2週間経って我が家にも慣れてきた模様 セキセイインコ「れもん」の日記:第2回 セキセイインコの「れもん」が我が家にやってきました!
AlpacaHackで始めるCTF入門 AlpacaHackで始めるCTF入門7:AlpacaHack Round 5 - XorshiftStreamに挑戦 AlpacaHackで始めるCTF入門6:AlpacaHack Round 4 - Simple Flag Checkerに挑戦 AlpacaHackで始めるCTF入門5:AlpacaHack Round 3 - qrimeに挑戦 AlpacaHackで始めるCTF入門4:AlpacaHack Round 2 - Simple Loginに挑戦 AlpacaHackで始めるCTF入門3:初めてのCTFに参加 AlpacaHackで始めるCTF入門2:DreamhackでCTF入門 AlpacaHackで始めるCTF入門1:初めてのCTF
シェル芸 第66回シェル芸勉強会メモ 第64回シェル芸勉強会メモ ChatGPTでシェル芸勉強会の問題が解きたい シェル芸botで遊びたい(Bash)
ROS 2 ROS 2シェル芸:URDFを置換してRVizで表示する 改訂新版『ROS 2ではじめよう』を読了&メモ書き ROS 2 Jazzy公式チュートリアル02: ROS 2のノードとトピックについて ROS 2 Jazzy公式チュートリアル01: ROS 2 Jazzyのインストールから動作確認まで ROS 2 Humble 公式チュートリアル 02: ROS 2 のノード関係のコマンド ROS 2 Humble 公式チュートリアル 01: 環境構築から turtlesim まで
VRChat VRChatでワールドをパブリック(コミュニティーラボ)へアップロードする VRChatのアバターにGoGo Locoを導入して座ったり寝っ転がったりする VRChatのワールドでオブジェクトと変数の同期を行う VRChatのスマホアプリ(Android)用にアバターをアップロードする Modular Avatarを使ってVRChatのアバターのオブジェクトをワールドに固定する VRChatのアバターのオブジェクトにアニメーションを設定する 【VRChat】Modular Avatarを使ってアバターのオブジェクトの表示と非表示を切り替える 公開されているVRMモデルをVRChatで動かす
技術ネタ TouchDesignerを使って曲の音声に合わせた動画を作成してみる Sonic Piで作曲してみる Visual Studio CodeでGitHub Copilot使用時にGitHubにサインインできない問題を解決 Unityでブラウザゲーム(Webアプリ)を作成してレンタルサーバで動かす方法 echoコマンドで"-n"をそのまま出力したい シェル芸オンラインジャッジの紹介 Ubuntu 24.04 LTSにおいてノートPCを電源に接続していない状態だと画面が暗くなる問題の解決方法 HTMLのvideoタグで貼った動画のサムネイルがSafariで表示されない問題を解決 WSL2上のUbuntuのVimで矩形選択するための設定 HTMLとCSSで画像のスライドショーを作成 GitHub Actionsでサーバ上のブログを自動更新 Windows と Ubuntu のデュアルブート
ELDEN RING BLOG ELDEN RING BLOG 9: DLCに挑戦した ELDEN RING BLOG 8: 最後のボスを倒した ELDEN RING BLOG 7: ラニのストーリーを進めた ELDEN RING BLOG 6: 四体目と五体目の大ボスを倒した ELDEN RING BLOG 5: 三体目の大ボスを倒した ELDEN RING BLOG 4: 二体目の大ボスを倒した ELDEN RING BLOG 3: 一体目の大ボスを倒した ELDEN RING BLOG 2: レベル上げと武器強化の旅 ELDEN RING BLOG 1: 今更ながら始める初見ELDEN RING冒険日記
ELDEN RING NIGHTREIGN BLOG ELDEN RING NIGHTREIGN BLOG 3: 喰らいつく顎を倒した ELDEN RING NIGHTREIGN BLOG 2: 最初の標的を倒した ELDEN RING NIGHTREIGN BLOG 1: マルチプレイ初戦
ポケポケブログ ポケポケブログ4:「超克の光」環境でもベトベトンデッキはまだ強い ポケポケブログ3:「時空の激闘」環境でもベトベトンデッキを使いたい ポケポケブログ2:「幻のいる島」環境でもベトベトンデッキが強いです ポケポケブログ1:ベトベトン+マタドガスデッキをおすすめする
読書日記 読書日記『本を読んだことがない32歳がはじめて本を読む』【ネタバレあり】 読書日記:近畿地方のある場所について【ネタバレあり】 改訂新版『ROS 2ではじめよう』を読了&メモ書き
映画日記 映画日記『ラストマイル』感想【ネタバレあり】
料理日記 料理日記:オムライス 料理日記:豆腐春巻き 料理日記:鶏肉とナスのほりにし炒め 料理日記:卵焼き 料理日記:鶏肉の照り焼き&小松菜のおひたし 料理日記:納豆蕎麦 料理日記:ローストビーフ 料理日記:バターガーリックチキン
その他 JR東日本公式のお忘れ物チャットにお世話になった話 『Omega Crafter』プレイ日記: チュートリアル編 自作ブログ大改造計画 HTML数式表示テスト 自作ブログ開始

加藤祐介ブログの著作物はCC BY-NC-ND 4.0で公開されています。
加藤祐介ブログのソフトウェアはApache License 2.0で公開されています。
About License: GitHub - YusukeKatoBlog/LICENSE
© 2023 YusukeKato All Rights Reserved.