プラベのVRChatブログ

VRChatで楽しく過ごすためのTipsをまとめてます。

【U#;UdonSharp】Manual同期のUdonSynced変数を完全に理解する【VRChat/SDK3】

概要

この記事では、UdonBehaviourSyncMode Manual(手動同期モード)でのUdonSynced同期変数をかんぜんにりかいする(隠語)ための内容をまとめています。

UdonSynced同期変数とは

プレイヤー間で同期させることのできる変数のこと。現状使用できる型は以下。

bool, char, byte, sbyte, short, ushort, int, uint, long, ulong, float, double, string, Vector2, Vector3, Vector4, VRCUrl, Quaternion, VRCUrl, Color and Color32.

Array単位でUdonSyncedにできる型は以下。

bool, char, byte, sbyte, short, ushort, int, uint, long, ulong, float, double, Vector2, Vector3, Vector4, Quaternion, Color and Color32.

docs.vrchat.com

どういう時に使えるの

既に一緒のインスタンスにいるプレイヤーと処理を同期させたいときは SendCustomNetworkEvent で大体なんとかなりますが、Late-joiner(後からインスタンスに入ってきたプレイヤー)と同期させたい場合は UdonSynced が必要です。
私の場合ですと、「開始時刻を過ぎてから遅れて入室した人も問題なく受講できる教室」のためのプレゼンテーション表示システムを作るにあたり、いま表示しているのが何ページかをLate-joinerに同期させるべく UdonSynced を学びました。つらかった。

UdonSyncedの使い方

UdonSynced変数を使いこなすには、変数を変更できる条件と、変更後の同期処理の流れをきちんと理解する必要があります。

UdonSynced変数を変更できるプレイヤー

UdonSynced変数を変更できるのは、Owner権限を持っているプレイヤーだけです。 UdonSynced変数を変更したいときは、以下のような処理を駆使してOwner権限を委譲させるか、

//プレイヤーがオーナでなければオーナ切り替え
if (!Networking.IsOwner(Networking.LocalPlayer, this.gameObject))
Networking.SetOwner(Networking.LocalPlayer, this.gameObject);

現在オーナであるプレイヤーの権限で処理させるよう SendCustomNetworkEvent を発行します。

SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.Owner, "SomeMethodName");

上記 SomeMethodName と同じ名前の関数を定義しておき、その中でUdonSynced変数を変更する処理をしておけば更新される(はず)です。

UdonSyncedの同期の流れ

  1. Owner権限でUdonSynced変数を更新する。この時点では同期されない
  2. RequestSerializationメソッドを実行する(Manualでは記述必須)
  3. UdonSynced変数を更新したOwnerプレイヤーだけ OnPreSerializationメソッドが呼び出される
  4. UdonSynced変数の値が同期される
  5. UdonSynced変数を更新したOwnerプレイヤー以外の、同期された変数を受信したプレイヤーだけ OnDeserializationメソッドが呼び出される

コード例

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

//Manualに固定する
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class SampleUdon : UdonSharpBehaviour
{
    [UdonSynced(UdonSyncMode.None)] private int udonSyncedInt = 0;

    public override void Interact()
    {
        //オーナ権限を委譲
        if (!Networking.IsOwner(this.gameObject))
            Networking.SetOwner(Networking.LocalPlayer, this.gameObject);

        //UdonSynced変数を更新
        udonSyncedInt = 1;

        //同期をリクエスト
        RequestSerialization();

        //付随するオーナ側の更新
        SomeUpdate();
    }

    public override void OnPreSerialization()
    {
        //同期時にオーナ側(発信側)でやりたい処理
    }

    public override void OnDeserialization()
    {
        //同期変数を受信した側でも更新する
        SomeUpdate();
    }

    public override void OnPlayerJoined(VRCPlayerApi player)
    {
        //プレイヤーがjoinするとこのメソッドが全プレイヤーで実行される
        RequestSerialization(); //これが通るのはオーナのプレイヤーだけ
        //新規joinプレイヤーにも同期変数が受信される
    }

    private void SomeUpdate()
    {
        //なにかUdonSynced変数更新に付随した処理
        return;
    }
}

注意点

  • 同一インスタンスに他に誰もいない場合、RequestSerialization, OnPreSerialization, OnDeserialization はいずれも実行されない
    上記コード例で OnPreSerialization にSomeUpdateを入れてしまうと、他にプレイヤーが居てくれないと更新されないバグが起きます。 この話、なんとドキュメントに記載されていません。確かに同期する必要はないんだけどさ!
  • RequestSerializationで同期されるUdonSynced変数は、呼び出しメソッドと同じクラスに定義されているものだけ(たぶん)
  • 一度の RequestSerialization で同期できるデータ量は49KB程度
  • RequestSerializationが使えるのはManual Syncだけ