【VRChat】DataDictionaryとJSON:設定データを読んでライトに反映する

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

配列では足りなくなったときのDataListとDataDictionary。JSONで書いたライトの設定を読み込み、名前で値を引いて反映します。型を確かめて取り出す書き方も解説します。

3灯のライトを配列でまとめられるようになりました。でも、こういう設定を持たせたくなります。

「1つ目のライトは名前が『入口』で、明るさが2、色は暖色」。名前も明るさも色も、ライトごとに違います。配列を3本並べれば書けますが、対応がずれても気づけません。

この記事では、名前で値を引ける入れ物 と、設定を1つの文字にまとめて渡す方法 を解説します。

外部の設定データを読んで、3つのランプの明るさを変える

この記事でわかること

  • 配列で足りなくなる場面
  • DataListとDataDictionaryの違い
  • JSONを読んで値を取り出す手順
  • 読み込みに失敗しても壊れない書き方

配列入門 を試した状態から始めます。

Sponsored


配列で足りなくなるとき

配列は「同じ種類のものを番号で並べる」入れ物でした。足りなくなるのは、2つの場面です。

1つ目は、実行中に増減させたいとき。 配列は要素数を先に決めておく必要があり、あとから増やせません。「参加した人を順に記録する」ような用途では困ります。

2つ目は、番号ではなく名前で引きたいとき。 「入口のライトの明るさ」を取り出すのに、それが何番目かを覚えているのは大変です。

VRChatは、この2つに専用の入れ物を用意しています。

設定データの構造。名前と値の組み合わせで持つ
入れ物できることいつ使うか
配列番号で引く。要素数は固定個数が決まっている
DataList番号で引く。実行中に増減できる記録がたまっていく
DataDictionary名前で引く設定に名前を付けたい

通常のC#にある ListDictionary は、UdonSharpでは使えません。VRChatが用意したこの2つを使います。

もう1つ覚える言葉があります。DataToken です。これらの入れ物に入っている値は、すべてDataTokenという「包み」に入っています。中身が数字なのか文字なのかは、取り出すときに確かめます。

包む理由は、1つの入れ物に違う種類のものを入れられるようにするため です。同じ設定の中に、名前(文字)と明るさ(数字)を並べられます。

Sponsored

実践:JSONの設定を読んでライトに反映する

3つのライトの設定を1つの文字列にまとめ、それを読んで反映します。

1. ライトを3つ置く

床のあるシーンに、Point Lightを3つ置きます。名前は Lamp0 Lamp1 Lamp2 で、(-1.5, 2.2, 1) (0, 2.2, 1) (1.5, 2.2, 1) に配置します。ModeはRealtimeです。

読み込みのきっかけにするボタンも1つ置きます。Cubeを LoadButton として (0, 1, -1) に置いてください。

シーンに置くライトとボタンの構成

2. JSONで設定を書く

JSON は、データを文字で書き表す決まりです。名前と値を : でつなぎ、{ } でまとめます。

今回使うのはこれです。

{
  "lamps": [
    { "name": "entrance", "intensity": 2.5 },
    { "name": "center",   "intensity": 1.0 },
    { "name": "window",   "intensity": 0.4 }
  ]
}

lamps という名前に、3つのまとまりが並んでいます。それぞれが nameintensity を持っています。

この形なら、設定を増やしても構造が壊れません。 4つ目のライトを足したければ、1行足すだけです。

3. コードを書く

Assets/Scripts で「Create → U# Script」を選び、LampConfigLoader を作ります。

using UdonSharp;
using UnityEngine;
using VRC.SDK3.Data;

[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class LampConfigLoader : UdonSharpBehaviour
{
    [SerializeField] private Light[] lamps;

    [TextArea(4, 10)]
    [SerializeField] private string configJson =
        "{\"lamps\":[{\"name\":\"entrance\",\"intensity\":2.5}," +
        "{\"name\":\"center\",\"intensity\":1.0}," +
        "{\"name\":\"window\",\"intensity\":0.4}]}";

    public override void Interact()
    {
        LoadConfig();
    }

    private void LoadConfig()
    {
        // 1. 文字をデータへ読み解く
        if (!VRCJson.TryDeserializeFromJson(configJson, out DataToken root))
        {
            Debug.LogWarning("[LampConfig] JSONを読めませんでした: " + root.ToString());
            return;   // 失敗したら、いまの設定のまま何もしない
        }

        // 2. いちばん外側が辞書であることを確かめる
        if (root.TokenType != TokenType.DataDictionary) return;
        DataDictionary rootDict = root.DataDictionary;

        // 3. "lamps" という名前で、並びを取り出す
        if (!rootDict.TryGetValue("lamps", TokenType.DataList, out DataToken listToken)) return;
        DataList list = listToken.DataList;

        // 4. 1件ずつ読んで、対応するライトへ反映する
        int count = Mathf.Min(list.Count, lamps.Length);
        for (int i = 0; i < count; i++)
        {
            if (!list.TryGetValue(i, TokenType.DataDictionary, out DataToken itemToken)) continue;
            DataDictionary item = itemToken.DataDictionary;

            if (!item.TryGetValue("intensity", TokenType.Double, out DataToken value)) continue;
            if (lamps[i] == null) continue;

            lamps[i].intensity = (float)value.Double;
        }

        Debug.Log("[LampConfig] " + count + "件を反映しました");
    }
}

長く見えますが、やっていることは4段階です。

  1. 文字をデータに読み解くTryDeserializeFromJson
  2. いちばん外側の形を確かめる
  3. 名前で中身を取り出すTryGetValue
  4. 1件ずつライトへ反映する

Try で始まる名前が並んでいます。これは 「できたかどうかを戻り値で返す」 という意味です。失敗しても止まらず、false が返るだけなので、if (!...) return; で受け止められます。

失敗したときに何もしない のがポイントです。設定が読めなかったからといってライトを全部消しては困ります。前の状態のまま残すほうが、ワールドとして安全です。

4. 割り当てて確かめる

LoadButton に Udon Behaviour を追加し、LampConfigLoader を指定します。Lampsのサイズを 3 にして、Lamp0 から順に割り当てます。

Playして押してみてください。

操作期待する結果
ボタンを押す3つの明るさが 2.5 1.0 0.4 になる。Consoleに 3件を反映しました
Inspectorで 2.55 に書き換えて押す1つ目だけが明るくなる
JSONの } を1つ消して押すJSONを読めませんでした と出て、ライトは変わらない

3つ目の確認が大事です。 壊れた設定を渡しても、ワールドは壊れません。エラーを出して、そのまま動き続けます。

型を確かめてから取り出す

TryGetValueTokenType.Double のような指定があるのは、中身の種類を確かめるため です。

名前で引き、型を指定して取り出す。型が違えば取り出せない

DataTokenは「何かが入った包み」なので、開けてみるまで中身が分かりません。文字が入った包みから数字を取り出そうとすれば、失敗します。

型を指定して取り出せば、中身が違ったときに false が返るだけ で済みます。指定せずに取り出すと、予期しない値をそのまま使ってしまいます。

外部から来るデータでは、特にこの確認が効きます。誰かが設定を書き換えたときに、数字のはずが文字になっていることは十分あります。

壊れた入力でも、確認していれば安全に弾ける

もう1つ知っておくことがあります。JSONの数値は Double として入ってきます。 2.51 も、区別なく小数として扱われます。Unityの intensityfloat なので、(float) を付けて変換しています。

うまくいかないときは

  • VRCJson が見つからないusing VRC.SDK3.Data; が抜けています
  • TryGetValue が常に失敗する → 名前のつづりと、型の指定を確認します。数値は TokenType.Double です
  • JSONを読めないと出る → 括弧やカンマの数を確認します。文字列の中でダブルクォートを使うときは \" と書きます
  • ライトが変わらない → Lampsの要素が空になっていないか。JSONの件数とライトの数が合っているか
Sponsored

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

  • DataListは実行中に増やせる: list.Add(...) で追加、list.Count で件数が取れます。訪問者の操作を順に記録する用途に向きます
  • JSONに書き出すこともできる: VRCJson.TrySerializeToJson で、DataDictionaryを文字列に戻せます。保存や送信に使います
  • 外部のURLから読み込める: この記事ではコードに書いた文字列を使いましたが、Web上のファイルを読むこともできます。外部テキストで掲示板を更新する で扱います
  • 深いところの不正は後で分かる: 読み込みが成功しても、奥のほうが壊れていると、取り出すときに初めて失敗することがあります。TryGetValue の戻り値は毎回確認します
  • そのままでは同期できない: DataListとDataDictionaryを [UdonSynced] で共有することはできません。共有したいときは、文字列に変換して送ります

まとめ

配列で足りなくなったら、名前で引ける入れ物を使います。

  • DataListは番号で引いて増減できる、DataDictionaryは名前で引ける
  • 中身はDataTokenという包み。取り出すときに型を確かめる
  • JSONは、設定を1つの文字にまとめる書き方
  • 読み込みに失敗したら何もしない。前の状態を残すほうが安全

設計するときの問いかけは、「この値は、番号で選ぶのか、名前で選ぶのか」 です。名前で選びたいならDataDictionaryです。

外部のファイルから設定を読みたくなったら 外部テキストで掲示板を更新する へ、設定を人ごとに保存するなら PlayerDataで音量を保存する へ進みます。

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