【VRChat】PlayerObjectで各自の状態を持つ:自分だけのスタンプカードを作る

作成: 2026-09-08最終更新: 2026-09-09

VRCPlayerObjectで、入室した人ごとに専用のオブジェクトを配ります。テンプレートとコピーの関係、VRCEnablePersistenceで保存する範囲、自分のコピーの探し方を解説します。

来場するたびにスタンプが1つ増えるカードを作りたい。10個たまったら特別な部屋が開く、といった仕掛けです。

数字を残すだけなら PlayerData でできます。ただ、人ごとに スクリプトも変数も表示もひとそろい 欲しくなると、キーを何本も並べる形は苦しくなります。

そこで使うのが VRCPlayerObject です。この記事では、入室した人ごとに専用のスタンプカードが配られる ようにします。

2人がそれぞれ自分のスタンプカードを持ち、数が違っている

この記事でわかること

  • テンプレートとコピーの関係
  • VRCEnablePersistenceが保存する範囲
  • 自分のコピーを探す方法
  • PlayerDataとの使い分け

PlayerDataで音量を保存する を試した状態から始めます。

Sponsored


紙をしまうか、机ごと配るか

PlayerDataは、ロッカーに紙をしまう仕組みでした。「スタンプは7個」と書いた紙が1枚あるだけで、ワールドに物は増えません。

PlayerObjectは、ロッカーそのものを1台ずつ配ります。 入室した人ごとに、専用のオブジェクトがワールドに現れます。数字だけでなく、スクリプト、表示、同期変数まで、まるごとその人のものになります。

紙をしまうか、机ごと配るか

使い分けの目安はこうです。

持ちたいもの向いているもの
音量、字幕のオンオフPlayerData
チュートリアルを見たかPlayerData
各自のスコアと、その計算をする処理PlayerObject
各自の持ち物リストと、その表示PlayerObject

値が数個ならPlayerDataで足ります。「その人専用の仕掛けを1式そろえたい」ときがPlayerObjectの出番 です。

Networking.SetOwner() で所有権を取り合う必要がないのも大きい点です。自分のコピーは最初から自分のものなので、書き込みの順番を心配せずに済みます。

Sponsored

型は1つ、コピーは人数分

シーンに置くのは 型(テンプレート) です。実行時にプレハブを複製する話ではなく、シーン上に1つ置いたオブジェクトが型になります。

テンプレート1つから、プレイヤーごとのコピーが作られる

流れはこうです。

  1. スタンプカードのオブジェクトをシーンに置く
  2. それに VRC Player Object を追加する
  3. プレイヤーが入室すると、その人用のコピーが作られる
  4. 型のほうは実行中は無効になる
  5. 退室すると、その人のコピーは消える

型を直接いじっても、誰のデータにもなりません。 読み書きするのはコピーのほうです。ここを取り違えると、「動いているように見えるのに何も保存されない」という状態になります。

もう1つ、VRC Enable Persistence という部品があります。役割は名前のとおりです。

部品することないとどうなるか
VRC Player Object入室した人ごとにコピーを作る共有の1個のまま
VRC Enable Persistenceそのコピーの同期変数を次回まで残すいる間だけの値になる

保存されるのは同期変数だけ です。ふつうのフィールドや、テキストに表示されている文字そのものは残りません。数字を [UdonSynced] で持って、表示はその数字から作り直す形にします。

実践:自分だけのスタンプカードを作る

ボタンを押すと自分のスタンプが1つ増えて、退室しても数が残る仕掛けを作ります。

押した人のカードだけスタンプが増える

1. 保存を有効にする

まず、ワールド全体で保存を使えるようにします。

Hierarchyの VRCWorld を選び、VRC Scene Descriptor の設定にある Persistence の項目を有効にします。これがオフだと、保存に関する仕掛けはすべて動きません。

2. テンプレートを作る

「Create Empty」で StampCardTemplate を作り、(0, 0, 0) に置きます。

「Add Component」で次の2つを追加します。

  1. VRC Player Object
  2. VRC Enable Persistence

見た目は付けません。表示は共有の掲示板でやって、コピーには数字だけを持たせます。 コピーは全員分が同じ位置に作られるので、見えるものを入れると重なってしまいます。

Assets/ScriptsStampCard を作り、StampCardTemplate に付けます。

using UdonSharp;
using VRC.SDKBase;

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
public class StampCard : UdonSharpBehaviour
{
    // VRC Enable Persistence によって次回まで残る
    [UdonSynced] public int stamps;

    public void AddStamp()
    {
        // 自分のコピーだけを触る
        if (!Networking.IsOwner(gameObject)) return;

        stamps++;
        RequestSerialization();
    }
}

このスクリプトには、探す処理も表示する処理もありません。持っているのは数字だけ です。

3. 受付を作る

「3D Object → Cube」で StampDesk を作り、(0, 1, 2) に置きます。その上に World Space の Canvas と TextMeshPro を置いて、StampText と名付けます。

Assets/ScriptsStampDesk を作ります。

using UdonSharp;
using UnityEngine;
using TMPro;
using VRC.SDKBase;

[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class StampDesk : UdonSharpBehaviour
{
    [SerializeField] private TextMeshProUGUI stampText;

    public override void Interact()
    {
        StampCard card = FindMyCard();
        if (card == null) return;

        card.AddStamp();
        Refresh();
    }

    // 保存データが戻ってきたら、表示を作り直す
    public override void OnPlayerRestored(VRCPlayerApi player)
    {
        if (player.isLocal) Refresh();
    }

    private void Refresh()
    {
        StampCard card = FindMyCard();
        if (card == null || stampText == null) return;

        stampText.text = "スタンプ " + card.stamps + " 個";
    }

    // 自分用に作られたコピーを探す
    private StampCard FindMyCard()
    {
        GameObject[] objects = Networking.GetPlayerObjects(Networking.LocalPlayer);
        if (objects == null) return null;

        for (int i = 0; i < objects.Length; i++)
        {
            StampCard card = objects[i].GetComponentInChildren<StampCard>();
            if (card != null) return card;
        }
        return null;
    }
}

Networking.GetPlayerObjects() が、そのプレイヤーに配られたコピーを返します。名前で探さないでください。 コピーの名前は環境によって変わりますし、複数の人が入ると当てになりません。

StampDesk に Udon Behaviour を追加し、Stamp Textをドラッグして、Interaction Text を スタンプを押す にします。

4. 確かめる

Build & Test で Number of Clients を 2 にして起動します。

順序操作期待する結果
1AとBが入るどちらの画面も「スタンプ 0 個」
2Aが3回押すAの画面は「スタンプ 3 個」
3Bが1回押すBの画面は「スタンプ 1 個」。Aの画面は 3 のまま
4Unityの Hierarchy を見るテンプレートの近くに、人数分のコピーが増えている
5アップロードして入り直す前回の数から続く

3番目が、この仕組みの手ざわりです。 同じ受付を押しているのに、増えるのは自分のカードだけです。所有権を取り合う処理を1行も書いていないことに注目してください。

4番目も見ておくと安心できます。コピーが本当に人数分あることが目で確認できます。

Sponsored

うまくいかないときは

  • 押しても増えない → テンプレートを直接操作しています。GetPlayerObjects でコピーを探します
  • 数が保存されない → VRC Enable Persistence が付いていないか、変数が [UdonSynced] になっていません
  • 何も動かない → VRC Scene Descriptor の Persistence が無効になっています
  • 入室直後だけ 0 と出るOnPlayerRestored で表示を作り直しているか確認します
  • 全員のカードが重なって見える → コピーに見た目を入れています。表示は共有の掲示板側でやります
  • 他の人の数が増えてしまうNetworking.IsOwner() の判定が抜けています

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

  • コピーは同じ場所に作られる: 全員分が重なるので、見えるものを入れるなら位置をずらす処理が要ります。入門では、コピーには数字だけを持たせるのが安全です
  • 保存されるのは同期変数だけ: ボタンの押し込み具合や、テキストに書かれた文字は残りません。数字から表示を作り直す形にしておけば、これで困りません
  • 他の人のカードも読める: GetPlayerObjects に他のプレイヤーを渡せば、その人のコピーが取れます。全員のスタンプ数を並べた掲示板は、これで作れます
  • PlayerDataと併用してよい: 音量のような単純な設定はPlayerData、持ち物のような一式はPlayerObject。片方に寄せる必要はありません
  • 使いどころはいろいろ: スタンプラリー、各自のインベントリ、個人スコア、進行度に応じて開く扉。「人ごとに違っていて、次回も残ってほしいもの」が対象です

まとめ

PlayerObjectは、入室した人ごとに専用のオブジェクトを配る仕組みです。

  • シーンに置くのは型。実行中に配られるコピーを読み書きする
  • VRC Player Object がコピーを作り、VRC Enable Persistence が同期変数を残す
  • 自分のコピーは Networking.GetPlayerObjects() で探す。名前では探さない
  • コピーの所有者は本人。所有権を取り合う必要がない

選ぶときの問いかけは、「値が数個で足りるか、仕掛けを1式そろえたいか」 です。1式ならPlayerObjectです。

動画を流す仕掛けなら 動画プレイヤーを設置する、外から文章を読み込むなら 文字列を読み込む へ進んでください。

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