3灯のライトを配列でまとめられるようになりました。でも、こういう設定を持たせたくなります。
「1つ目のライトは名前が『入口』で、明るさが2、色は暖色」。名前も明るさも色も、ライトごとに違います。配列を3本並べれば書けますが、対応がずれても気づけません。
この記事では、名前で値を引ける入れ物 と、設定を1つの文字にまとめて渡す方法 を解説します。
この記事でわかること
- 配列で足りなくなる場面
- DataListとDataDictionaryの違い
- JSONを読んで値を取り出す手順
- 読み込みに失敗しても壊れない書き方
配列入門 を試した状態から始めます。
配列で足りなくなるとき
配列は「同じ種類のものを番号で並べる」入れ物でした。足りなくなるのは、2つの場面です。
1つ目は、実行中に増減させたいとき。 配列は要素数を先に決めておく必要があり、あとから増やせません。「参加した人を順に記録する」ような用途では困ります。
2つ目は、番号ではなく名前で引きたいとき。 「入口のライトの明るさ」を取り出すのに、それが何番目かを覚えているのは大変です。
VRChatは、この2つに専用の入れ物を用意しています。

| 入れ物 | できること | いつ使うか |
|---|---|---|
| 配列 | 番号で引く。要素数は固定 | 個数が決まっている |
| DataList | 番号で引く。実行中に増減できる | 記録がたまっていく |
| DataDictionary | 名前で引く | 設定に名前を付けたい |
通常のC#にある List や Dictionary は、UdonSharpでは使えません。VRChatが用意したこの2つを使います。
もう1つ覚える言葉があります。DataToken です。これらの入れ物に入っている値は、すべてDataTokenという「包み」に入っています。中身が数字なのか文字なのかは、取り出すときに確かめます。
包む理由は、1つの入れ物に違う種類のものを入れられるようにするため です。同じ設定の中に、名前(文字)と明るさ(数字)を並べられます。
実践: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つのまとまりが並んでいます。それぞれが name と intensity を持っています。
この形なら、設定を増やしても構造が壊れません。 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段階です。
- 文字をデータに読み解く(
TryDeserializeFromJson) - いちばん外側の形を確かめる
- 名前で中身を取り出す(
TryGetValue) - 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.5 を 5 に書き換えて押す | 1つ目だけが明るくなる |
JSONの } を1つ消して押す | JSONを読めませんでした と出て、ライトは変わらない |
3つ目の確認が大事です。 壊れた設定を渡しても、ワ ールドは壊れません。エラーを出して、そのまま動き続けます。
型を確かめてから取り出す
TryGetValue に TokenType.Double のような指定があるのは、中身の種類を確かめるため です。

DataTokenは「何かが入った包み」なので、開けてみるまで中身が分かりません。文字が入った包みから数字を取り出そうとすれば、失敗します。
型を指定して取り出せば、中身が違ったときに false が返るだけ で済みます。指定せずに取り出すと、予期しない値をそのまま使ってしまいます。
外部から来るデータでは、特にこの確認が効きます。誰かが設定を書き換えたときに、数字のはずが文字になっていることは十分あります。

もう1つ知っておくことがあります。JSONの数値は Double として入ってきます。 2.5 も 1 も、区別なく小数として扱われます。Unityの intensity は float なので、(float) を付けて変換しています。
うまくいかないときは
VRCJsonが見つからない →using VRC.SDK3.Data;が抜けていますTryGetValueが常に失敗する → 名前のつづりと、型の指定を確認します。数値はTokenType.Doubleです- JSONを読めないと出る → 括弧やカンマの数を確認します。文字列の中でダブルクォートを使うときは
\"と書きます - ライトが変わらない → Lampsの要素が空になっていないか。JSONの件数とライトの数が合っているか
おまけ:先に知っておくと良いこと
- DataListは実行中に増やせる:
list.Add(...)で追加、list.Countで件数が取れます。訪問者の操作を順に記録する用途に向きます - JSONに書き出すこともできる:
VRCJson.TrySerializeToJsonで、DataDictionaryを文字列に戻せます。保存や送信に使います - 外部のURLから読み込める: この記事ではコードに書いた文字列を使いましたが、Web上のファイルを読むこともできます。外部テキストで掲示板を更新する で扱います
- 深いところの不正は後で分かる: 読み込みが成功しても、奥のほうが壊れていると、取り出すときに初めて失敗することがあります。
TryGetValueの戻り値は毎回確認します - そのままでは同期できない: DataListとDataDictionaryを
[UdonSynced]で共有することはできません。共有したいときは、文字列に変換して送ります
まとめ
配列で足りなくなったら、名前で引ける入れ物を使います。
- DataListは番号で引いて増減できる、DataDictionaryは名前で引ける
- 中身はDataTokenという包み。取り出すときに型を確かめる
- JSONは、設定を1つの文字にまとめる書き方
- 読み込みに失敗したら何もしない。前の状態を残すほうが安全
設計するときの問いかけは、「この値は、番号で選ぶのか、名前で選ぶのか」 です。名前で選びたいならDataDictionaryです。
外部のファイルから設定を読みたくなったら 外部テキストで掲示板を更新する へ、設定を人ごとに保存するなら PlayerDataで音量を保存する へ進みます。