C#の入門書どおりに List<string> を書きました。Unityのエディタは赤くなり、見たことのないエラーが出ます。
書き方は間違っていません。Udonが対応していないだけです。
この見分けがつかないと、正しいコードを何度も書き直すことになります。この記事では、よく踏む制約と、その避け方 を扱います。
この記事でわかること
- 制約がある理由
- よく踏む4つの制約
DataListによる代替の書き方- 使えるかどうかを自分で調べる方法
メソッドとカスタムイベント を試した状態から始めます。
制約があるのは、翻訳しているから
UdonSharpで書いたC#は、そのまま動いているわけではありません。

書いたコードは、まず Udon という仕組み向けの命令に翻訳されます。動いているのは翻訳後のほうです。
だから、翻訳先に対応する命令がないものは持っていけません。 C#として正しいかどうかは関係ありません。辞書に載っていない単語は訳せない、という話です。
この理解があると、エラーの受け止め方が変わります。「自分の書き方が悪い」ではなく、「これは翻訳できるものか」 と考えられるようになります。
よく踏む4つ
制約の一覧を覚える必要はありません。実際によく踏むのは4つです。

| 使えないもの | 代わりに |
|---|---|
List<T> Dictionary<K,V> | DataList DataDictionary |
LINQ(Where Select など) | for で書く |
try / catch | 事前に null や範囲を確かめる |
| 配列の長さを変える | 新しい配列を作って詰め替える |
いちばん多いのが1行目です。 持ち物リスト、参加者の一覧、スコアの記録。長さが変わるものを扱おうとすると、必ずここに来ます。
VRChatはこのために DataList と DataDictionary を用意しています。使い勝手はほとんど同じ なので、置き換えは難しくありません。
残りの3つも、対処は単純です。
// LINQ は for に書き換える
int total = 0;
for (int i = 0; i < scores.Length; i++)
{
if (scores[i] > 0) total += scores[i];
}
// try-catch の代わりに、先に確かめる
if (target != null && index >= 0 && index < items.Length)
{
// 安全に使える
}
try / catch が使えないのは、慣れるまで不安に感じます。 ただ、ワールド制作で扱うのは自分が用意したオブジェクトと値です。null かどうかと範囲を先に見ておけば、実用上は困りません。
実践:持ち物リストをDataListで作る
床に置いた3つのアイテムを拾うと、掲示板に持ち物が並ぶ仕掛けを作ります。
1. アイテムと掲示板を置く
床のあるシーンに、次を置きます。
| 名前 | 作り方と設定 |
|---|---|
KeyItem MapItem LampItem | Cube。床の上に3つ並べる。Scale (0.2, 0.2, 0.2) |
BagCanvas | UI Canvas(World Space)。(0, 1.8, 3)、Scale (0.004, 0.004, 0.004) |
BagText | BagCanvas の子に TextMeshPro |
BagRoot | Create Empty。(0, 0, 0) |

3つのアイテムには、それぞれ VRC Pickup を追加します。掴めるようになります。
2. 入れ物を書く
Assets/Scripts で ItemBag を作ります。
using UdonSharp;
using UnityEngine;
using TMPro;
using VRC.SDK3.Data;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class ItemBag : UdonSharpBehaviour
{
[SerializeField] private TextMeshProUGUI display;
private DataList items; // List<string> の代わり
private void Start()
{
items = new DataList();
Refresh();
}
public void AddItem(string itemName)
{
items.Add(itemName); // 長さを気にしなくてよい
Refresh();
}
private void Refresh()
{
string text = "持ち物 " + items.Count + " 個\n";
for (int i = 0; i < items.Count; i++)
{
// 取り出すときは DataToken を経由する
if (items.TryGetValue(i, TokenType.String, out DataToken token))
{
text += "・" + token.String + "\n";
}
}
if (display != null) display.text = text;
}
}
List<string> を DataList に変えただけです。 Add() も Count も同じ名前で、for の書き方も変わりません。
違うのは、取り出すときに DataToken を通ること です。DataList はいろいろな型を入れられるので、「これは文字列として取り出したい」と指定します。
型が合わなければ TryGetValue が false を返します。try / catch が使えない分を、この形が受け持っています。
3. アイテム側を書く
Assets/Scripts で BagItem を作ります。
using UdonSharp;
using UnityEngine;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class BagItem : UdonSharpBehaviour
{
[SerializeField] private ItemBag bag;
[SerializeField] private string itemName = "鍵";
public override void OnPickup()
{
if (bag != null) bag.AddItem(itemName);
gameObject.SetActive(false); // 拾ったら消える
}
}
3つのアイテムそれぞれに付けて、Bagに BagRoot を、Item Nameに 鍵 地図 ランタン を入れます。
BagRoot には ItemBag を付けて、Displayに BagText をドラッグします。
4. 確かめる
Playして、3つのアイテムを順に拾います。
| 順序 | 操作 | 掲示板の表示 |
|---|---|---|
| 1 | 何も拾わない | 持ち物 0 個 |
| 2 | 鍵を拾う | 持ち物 1 個 と ・鍵 |
| 3 | 地図を拾う | 持ち物 2 個 と ・鍵 ・地図 |
| 4 | ランタンを拾う | 持ち物 3 個 と3行 |
配列の長さを気にする処理を、1行も書いていません。 List<T> でやりたかったことが、そのままできています。
使えるかどうかを自分で調べる
新しいメソッドを使いたいとき、先に調べられます。

Unityのメニューから VRChat SDK → Utilities → Class Exposure Tree を開きます。Udonから触れるクラスとメソッドの一覧です。
使い方は単純です。
- 検索欄にクラス名かメソッド名を入れる
- 出てくれば使える。出てこなければ使えない
エラーの原因を切り分けるときにも役立ちます。
| エラーの様子 | 疑うもの |
|---|---|
| 型名やメソッド名が出ていて、C#としては正し い書き方 | Udonの制約 |
つづり違い、; の抜け、括弧の対応 | 自分のミス |
インスペクタの割り当てが None | 制約でもミスでもなく設定 |
見分けがつかないときは、その1行だけを消してみてください。 通るなら制約、通らないなら他に原因があります。
おまけ:先に知っておくと良いこと
- DataDictionary もある: 名前で引きたいときは
DataDictionaryを使います。ContainsKeyやTryGetValueがあって、使い方はDictionaryに近いものです - 文字列の連結は控えめに:
+でつなぐのは使えますが、毎フレーム作ると重くなります。表示が変わったときだけ作ってください - 配列で足りることも多い: 数が最初から決まっているなら、普通の配列がいちばん軽くて確実です。長さが変わるときだけ
DataListにします - 公式のサンプルを読む: SDKに入っているサンプルは、当然すべて動く書き方です。迷ったら、そこにある形をまねるのが早道です
- 制約は緩んでいくもの: SDKの更新で使えるようになるものもあります。古い記事で「使えない」とあっても、いま試すと通ることがあります
まとめ
Udonの制約は、翻訳できるかどうかの問題です。
- C#として正しくても、Udonに命令がなければ動かない
- よく踏むのは、
List・LINQ・try-catch・可変長配列の4つ ListとDictionaryはDataListとDataDictionaryで置き換えられる- 使えるかどうかは Class Exposure Tree で調べられる
エラーを見たときの問いかけは、「これはC#の書き方の問題か、翻訳できるかの問題か」 です。ここを分ければ、直す先がすぐ決まります。
動かない原因を追いたいなら 動かないときの直し方、仕掛けを軽くするなら Udonを軽くする へ進んでください。