手動処理について解説
自動同期と手動同期の違い
また同期には自動同期と手動同期が存在し、双方にメリット・デメリットが存在します。
自動同期は作成が簡単ですが、どのタイミングで同期が発生するか分からず、
手動同期は作成が面倒ですが、任意のタイミングで同期を発生させることができます。
あまりにも自動同期する値が多すぎると、同期するタイミングの奪い合いが発生し、ユーザー同士で値がズレている間隔が長くなります。そのため、手動同期の作成が推奨されています。
自動同期は下記記事で解説していますので、本記事では公式の手動同期を作成していきます。
Udonスクリプト作成
サンプルのButtonSyncOwnerをU#に書き直し
Udon Graph
U#
public class ButtonSyncOwnerBySharp : UdonSharpBehaviour
{
public Text uiText;
[UdonSynced]
private int clickCount = 0;
public void OnClick()
{
if (Networking.IsOwner(this.gameObject)) // オブジェクトのオーナーでのみ下記処理を発生させる
{
clickCount++; // この値はオブジェクトのオーナー以外が変更してはいけない。
uiText.text = clickCount.ToString();
RequestSerialization(); // このメソッドが呼び出された時だけ同期処理が発生する。
}
}
// 同期処理。値の代入などの処理を記述するメソッド
public override void OnDeserialization()
{
uiText.text = clickCount.ToString();
}
}
どういうわけか、サンプルのUdon GraphではNetworking.IsOwnerの引数はnullだったのですが、U#側で同じようにnullを指定するとうまく動かなかったのでthis.gameObjectにしています。
シーン作成
どこでも良いのでUdon Behaviourコンポーネントを追加して、上記のコードを登録。Synchronization Methodの値をContinuousからManualに変更して、自動同期から手動同期に変更します。
Canvasにボタンを追加してOnClickイベントに先ほど作ったUdonスクリプトのOnClickメソッドを呼び出すようにします。ボタンを追加するとTextオブジェクトも一緒に生成されるので、このテキストに同期した値を表示するようにUdon BehaviourのUi Text値に代入すれば完成。
完成・・・?
これでボタンを押したときにだけ同期処理が発生して、変更が発生した時にだけ同期が発生するので通信リソースを浪費しなくなりました。しかし、値の変更はオブジェクトの所有権を持っている「オーナー」と呼ばれるユーザーでしか許可していません。これはVRChatの仕様上のため、全ユーザーに値の変更をするためには、もう少し手を加えます。
すべてのユーザーに値の変更を可能にするには?
2通りの手段があり、
オーナーに値の変更を依頼するか
オーナーに所有権の変更を依頼するか
ので値を変更する必要があります。
オーナーに値の変更を依頼する
下記のコードはオブジェクトの所有者にOnClickメソッドを処理するように依頼するコードです。
SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(OnClick));
このコードを追記した全体のコードはこちら
public class ButtonSyncOwnerBySharp : UdonSharpBehaviour
{
public Text uiText;
[UdonSynced]
private int clickCount = 0;
public void OnClick()
{
if (Networking.IsOwner(this.gameObject))
{
clickCount++;
uiText.text = clickCount.ToString();
RequestSerialization();
}
else
{
// オブジェクトのオーナーでなければ、オーナーに変更してもらうようにお願いする
SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(OnClick));
}
}
public override void OnDeserialization()
{
uiText.text = clickCount.ToString();
}
}
ちなみに、SendCustomNetworkEvent の第二引数はstring型のメソッド名だが、直接string型で記述するより、nameofを使えばメソッド名のスペルミスをコンパイルエラーが教えてくれるので開発が楽になります。
実装してみた感じではオーナー側の値が変更されてから他のユーザーの値が同期されるので、値の変更を依頼したユーザー視点だと、反応が遅くもっさりしています。
自分がオーナーになる
Networking.SetOwnerメソッドでオブジェクトの所有権を譲ってもらうようにオーナーにお願いします。オーナーは所有権の変更を許可することで権限の受け渡しが完了します。
public class ButtonSyncBecomeOwnerBySharp : UdonSharpBehaviour
{
public Text uiText;
[UdonSynced]
private int clickCount = 0;
public void OnClick()
{
Networking.SetOwner(Networking.LocalPlayer, gameObject); // オブジェクトの所有権を貰えないか尋ねる
if (Networking.IsOwner(this.gameObject))
{
clickCount++;
uiText.text = clickCount.ToString();
RequestSerialization();
}
}
public override void OnDeserialization()
{
uiText.text = clickCount.ToString();
}
// オブジェクトの所有権を譲る許可を返すメソッド trueで許可 falseで拒否
public override bool OnOwnershipRequest(VRCPlayerApi requestingPlayer, VRCPlayerApi requestedOwner)
{
return true;
}
}