VRChat 同期カウンターを作る

# 1.この記事は

下記の仕様を持ったカウンターオブジェクトの実装方法を説明します。

(1) インスタンス内のすべてのユーザーがカウントアップできる。
(2) カウントアップ値がインスタンス内のすべてのユーザーに共有される。
(3) 後からインスタンスに入室したユーザーも最新のカウントアップ値が確認できる。

![348.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/6eacdd46-558a-4863-b5e5-5c712321e438.jpeg)

(注) 下記リンク先の解説資料に対してオブジェクトの作成方法やアタッチメントの方法を追記した内容になっています。
https://hatuxes.hatenablog.jp/entry/2021/08/18/215521

# 2.内容

# 2-1 prehabの設計

下記リンク先の情報に従い、本記事で使用するオブジェクトを設計します。
https://qiita.com/NT1123/items/cbd114854b39facbd5fb#2-1-%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%81%BE%E3%81%99

![349.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/781afb36-7235-2b4a-dbf4-e812341d56cd.jpeg)

②Udon Behaviourを挿入し、Program scriptを作成する。 
③On click()を追加し、下記図記載のとおり各項目を設定する。
④⑤⑥➆各変数に対するアタッチメントを行う。


![352.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/cd6dc7fb-8de2-2bc3-a7c5-9f1c8d0eb216.jpeg)

# 2-2 オブジェクトの設計

上記で作成したprefabを作業画面にドラッグアンドドロップする。

![350.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/b3f54780-4b4e-3079-7905-68b48e2b2233.jpeg)

①Udon Behaviorの挿入を行い、Program scriptを作成する。作成したProgram scriptには下記#2-3に記載
されたコーディングのコードを書く。
②On click()を挿入し、各項目を設定する。
③各変数のアタッチメントを行う。

![351.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/acb8ced3-a460-36f6-3afd-ddc03786c5f9.jpeg)


# 2-3 コーディング

```c#:example
using UdonSharp;
using UnityEngine.UI;
using VRC.SDKBase;

//このコードは
//ボタンをクリックすると、カウントアップされる。
//直近にカウントアップしたユーザーの名前が表示される。
//後からワールドに入った人にも最新のカウント値が表示される。


//[UdonSynced(UdonSyncMode.None), FieldChangeCallback(nameof(CountData))]
//OnValueChangedイベントを適用する変数にFieldChangeCallback属性を付ける
// 適用する変数に対応させるプロパティを記述する
//適用する変数のFieldChangeCallback属性の引数に、2番目で作ったプロパティの名前を設定する

//(1)同期変数を使用し、かつ手動同期を用いるため、クラスの属性に[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]を書く。
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class COUNTUP : UdonSharpBehaviour
{

    //(2)変数クラスCountData,(メンバ変数_countData)の記述の前に[UdonSynced(UdonSyncMode.None), FieldChangeCallback(nameof(CountData))]の宣言をする。
    [UdonSynced(UdonSyncMode.None), FieldChangeCallback(nameof(CountData))] private int _countData;      // メンバ変数 _countDataが同期変数になる。
    public Text DisplayDataText;  // データを表示するText
    public Text OptionText;       // 誰がOwnerかを表示するText

    //(3)CountDataプロパティーにメンバ変数(同期変数)_countDataを定義する。セッターとゲッターを使用している。
    public int CountData
    {
        get => _countData;
        set
        {
            _countData = value; //valueキーワードが使用でき、これにアクセス元から渡された値が格納されています。
            DisplayCountData();  // Owner以外のデータ表示処理
        }
    }


    private void Start()
    {
        //オブジェクトのオーナーを表示させる。
        SetOptionalText(Networking.LocalPlayer);
    }



    // ボタンをクリックした時に呼ばれる
    public void Countupact()
    {
        // ボタンをクリックしたユーザーを取得する。
        var player = Networking.LocalPlayer;
        // オーナー権限のリクエストをする。無条件でオーナー権限の移管を行う。
        Networking.SetOwner(player, this.gameObject);


        if (player.IsOwner(this.gameObject))
        {
            // カウントアップ処理
            CountUp();
        }
    }

    // Ownerが移行した際の処理　OnOwnershipTransferredイベントがインスタンス内のすべてのユーザーのGameObjectで呼び出される。
    // このイベントはインスタンスにいる全員に発行されるので、Network.LocalPlayerを用いる
    // 引数のplayerは新たなオーナーを指す
    public override void OnOwnershipTransferred(VRCPlayerApi player)
    {
        SetOptionalText(Networking.LocalPlayer);
    }

    // Ownerが値を+1する処理
    public void CountUp()
    {
        CountData++;             // データ更新
        RequestSerialization();   // 同期更新
    }

    // 同期変数の値をUIに表示する処理
    public void DisplayCountData()
    {
        DisplayDataText.text = _countData.ToString();   // データ表示更新
    }

    // 対象オブジェクトのオーナーを表示させる。
    public void SetOptionalText(VRCPlayerApi player)
    {

        OptionText.text = $"<color=red>{player.displayName} is Owner!</color>";
      
    }
}
```

★上記コードの解説、フローチャート


![355.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/9f5d8a15-6117-ce0b-bcc2-ee3a0f6af82d.jpeg)

# 3-1 実行結果

ユーザー1がインスタンスに入室し、Buttonをクリックしてカウンターのカウントアップを行った。
![356.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/31e438d5-8f3a-5f35-4330-b0a373738715.jpeg)

ユーザー2が後からインスタンスに入室した。ユーザー1がカウントアップした結果が同期されている。
![357.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/60e9974a-afa0-91d6-bca4-238994a07d34.jpeg)

ユーザー2がカウンターをカウントアップした。
![358.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/4fcedda5-2622-a0f1-55f6-8afafce7d292.jpeg)

意図通りアプリが設計できていることが確認できた。