対応状況

いま何がコンパイルできるのか、そして Udon が決して実行できない C# の部分について。

更新日 2026-09-08

Udonite は日常的な C# のかなり広い範囲をコンパイルします。以下の構文はすべて、出力をアセンブルして実機の Udon 仮想マシンで動かすテストで確認しています。

コンパイルできるもの

型とメンバー

class struct interface enum record(宣言が一つ必要) 入れ子の型 自作クラスをフィールドやローカルに 制約付きジェネリクス インターフェースの既定実装 プロパティとインデクサー 拡張メソッド 省略可能引数と名前付き引数 params out と ref の引数 タプルと分解 値としての typeof ローカル関数 静的ヘルパークラス

レコード:位置指定のコンストラクタ、with、値等価性、ToString、分解。ただし、自分のスクリプトに次の宣言を一つ置く必要があります。record と init セッターは init 専用プロパティにコンパイルされますが、Unity がワールドのコードをビルドする .NET のクラスライブラリには System.Runtime.CompilerServices.IsExternalInit がないためです。これがないと、Udonite が動くより前に Unity 自身のコンパイラが CS0518 でビルドを止めます。

namespace System.Runtime.CompilerServices
{
    internal static class IsExternalInit { }
}

record struct と構造体に対する with は C# 10 の機能で、Unity は C# 9 でコンパイルするため、こちらはまったく使えません。static フィールドは const か readonly である必要があります(後述)。

継承

abstract クラス abstract メンバー virtual と override base. 呼び出し abstract な behaviour

基底型を通した呼び出しは最も派生したオーバーライドに届き、base.Method() はちょうど一段上に届きます。ふつうのクラスでも、GameObject に載った behaviour でも同じです。

コンポーネント同士が契約を共有する手段は abstract な基底クラスです。Unity はインターフェースのフィールドをシリアライズできないからです。インターフェース を参照してください。

文と式

制御フロー switch 式 パターンマッチング null 演算子 文字列補間 算術・ビット・シフト演算子 フラグ enum 配列と多次元配列 範囲とインデックス Array のヘルパー List<T> Dictionary<K,V> HashSet<T> Queue<T> Stack<T> LINQ デリゲートとイベント ラムダ 文字列と StringBuilder null 許容値型 enum の解析と名前 try / finally throw 再帰

制御フローは if、switch、for、foreach、while、do、break、continue です。パターンマッチングは型パターン、is not null、is > 5 and < 10 のような関係パターン、プロパティパターンと位置パターン、when ガードを含みます。null 演算子は ?.、??、??= です。補間文字列は書式指定子を受け付け、nameof もコンパイルできます。範囲とインデックスは arr[^1] や arr[1..3]、そして文字列に対する同じ書き方(text[1..3]、text[^2..]、text[..^1])です。typeof(T) は値として扱えます。ローカル変数、フィールド、引数、配列に保持でき、== や Equals で比較でき、IsAssignableFrom や IsInstanceOfType も呼べます。typeof(T).Name と .FullName はコンパイル時に文字列へ畳み込まれます。Array のヘルパーは Sort、Find、FindAll、FindLast、Exists、IndexOf、LastIndexOf、Resize、Copy、Fill、ConvertAll です。

配列・リスト・ディクショナリに対する LINQ:Select、Where、OrderBy、ThenBy、GroupBy、First、Any、All、Sum、Average、Min、Max、Count、Take、Skip、TakeWhile、SkipWhile、Distinct、Reverse、Concat、Zip、Union、Intersect、Except、SequenceEqual、ToArray、ToList、ToDictionary、Range、Repeat、およびそれらの連結。LINQ や List<T>/Array のヘルパーに渡したラムダは、呼び出し箇所に展開されます。

デリゲートは Action と Func のフィールド、Action done = Cleanup; のようなメソッドグループ、マルチキャストの += と -=、C# の event 宣言、他のビヘイビアの public メソッドへのデリゲートです。ラムダは値として使えます。変数をキャプチャするラムダには一つだけ制約があり、後述します。文字列には Format、Split、Join、Substring、Replace、Trim、PadLeft があります。null 許容値型には HasValue と GetValueOrDefault があります。enum には Parse、TryParse、GetNames、GetValues、HasFlag があり、typeof(T).Name もコンパイルできます。throw はメッセージをログに出してビヘイビアを停止するので、アサーションとして使えます。捕まえるものはありません。直接の再帰と相互再帰はコンパイルでき、呼び出しの前後でメソッドの変数がスタックに退避されます。

Unity と VRChat

コンポーネントの取得 Instantiate と Destroy Transform と物理 Mathf と値型 Random と Time コルーチン Invoke 系 async と await VRCPlayerApi Networking ピックアップとステーション UdonBehaviour の参照 他のビヘイビアの Unity メンバー Unity と SDK の型の配列 Unity オブジェクト型どうしのキャスト

コンポーネントの取得は GetComponent<T>() とその派生、非ジェネリックの GetComponent(typeof(T))、TryGetComponent<T>(out T)、それに SetActive です。値型は Vector3、Quaternion、Color をはじめとする一般的な Unity の構造体です。コルーチンは yield return null、WaitForSeconds、WaitUntil、入れ子のコルーチンを使え、StartCoroutine(Run()) か StartCoroutine(nameof(Run)) で開始し、StopCoroutine(handle) か StopAllCoroutines で停止します。Invoke、InvokeRepeating、CancelInvoke は public メソッドに使えます。async メソッドは void、Task、UniTask、UniTaskVoid を返せます。await Task.Delay(...) や他の async メソッドの await も可能です。UdonBehaviour の参照では SendCustomEvent、SetProgramVariable、GetProgramVariable<T> が使えます。他のビヘイビアの gameObject、transform、enabled は直接読み書きできます。Unity の型、コンポーネント、SDK の enum の配列は、他のフィールドと同じように使えます。ネットワークにあるものすべて。

シェーダーとマテリアルへの書き込み

VRCShader のグローバル material.SetFloat など MaterialPropertyBlock EnableKeyword

UnityEngine.Shader は公開されていません。グローバルは VRCShader.PropertyToID(name) と VRCShader.SetGlobalFloat、SetGlobalVector、SetGlobalColor、SetGlobalMatrix、 SetGlobalTexture、そしてそれぞれの配列版を使います。SetGlobalInt はありません。SDK が持っていないからです。

グローバルの名前は _Udon で始めなければなりません。 そうでないものを VRChat は無視します。しかも黙って無視します。 呼び出しは成功し、シェーダーは古い値のまま、ログには何も出ません。グローバルはマテリアルのプロパティではないので、 シェーダー側では Properties ブロックの外で宣言してください。

マテリアルのプロパティは普通に扱えます。名前でも id でも SetFloat、SetColor、SetVector、GetColor、 それに MaterialPropertyBlock と EnableKeyword。material ではなく sharedMaterial を使ってください。 material に触れると実行時にマテリアルが複製され、実行時マテリアルはシーンディスクリプタに登録しない限り ビルドから取り除かれます。

2026-09-08、コンパイルだけでなくアップロードしたワールドで両方の経路を確認済みです。

Udon が決して実行できないもの

これらは UDN0011 で拒否されます。拒否のメッセージがその旨と、代わりにどうすべきかを示します。

構文 理由と、代わりに書くこと
catch Udon には捕まえる例外がありません。フォルトはビヘイビアを停止します。catch を外し、先に条件を確認してください。try/finally はコンパイルでき、throw はメッセージをログに出してビヘイビアを停止します。
書き換え可能な static フィールド ビヘイビアごとに独立したヒープを持つため、static は「共有された一つの値」ではなく「ビヘイビアごとに一つの値」になってしまいます。どれか一つのビヘイビアのフィールドを参照する形にするか、記憶領域を持たない get 専用の static プロパティを使ってください(シングルトン)。
Awake、Reset、OnValidate、OnGUI、OnDrawGizmos、OnApplicationQuit、OnApplicationPause、OnApplicationFocus、OnAudioFilterRead Udon はディスパッチしません。Awake の処理は Start に移してください。残りはエディタ専用か、ワールドではなくアプリケーションのためのものです。OnApplicationQuit で意図していたのは、たいてい OnPlayerLeft です。
CompareTag、gameObject.tag、FindGameObjectWithTag Udon はタグを公開していません。レイヤーや名前を比べるか、参照を持つか、GameObject.Find を使ってください。
Camera.main 公開されていません。インスペクタでカメラをフィールドに割り当ててください。
SendMessage、IsInvoking、その他 Udon にない MonoBehaviour のメンバー コルーチン、Invoke、CancelInvoke は Udonite が変換します。それ以外にはワールドで動く形がありません。
Application.isPlaying、isFocused、targetFrameRate、platform、Time.timeScale、AudioListener 公開されていません。ワールドは常に実時間で再生中です。VR かデスクトップかは Networking.LocalPlayer.IsUserInVR() で判定し、リスナーではなく各 AudioSource を設定してください。
PlayerPrefs、Resources.Load、SceneManager、UnityEvent、Shader、Input.mousePosition、new GameObject()、GameObject.CreatePrimitive、Mesh.CombineMeshes、TMP_Text.SetText それぞれの拒否メッセージが VRChat 流の書き方を示します。永続化は PlayerData、アセットはインスペクタのフィールド、シーン読み込みの代わりに VRCPlayerApi.TeleportTo、UnityEvent の代わりにインスペクタで結線した public メソッド、グローバルなシェーダープロパティは VRCShader、ポインタの代わりに頭のトラッキングデータ、生成はプレハブかテンプレートオブジェクト、メッシュはエディタで結合、そして text プロパティです。
素のオブジェクト(ビヘイビアでないもの)のメソッドを指すデリゲート、および Unity がシリアライズする public フィールドに置いたデリゲート どちらもワールドまで持ち込めません。
生成したメソッドの外まで持ち越すキャプチャ付きラムダ(フィールドへの代入、戻り値、引数として渡す) キャプチャした変数はビヘイビアの単一のヒープにあるため、保存したクロージャは次の呼び出しで古い状態を見てしまいます。キャプチャしないラムダは自由に保存できます。キャプチャするものは、同じメソッド内で呼ぶローカルとしてなら動きます。
checked 演算 Udon の演算子 extern はオーバーフローで例外を出さず、そもそも Udon には投げる例外がありません。演算の前にオペランドを確認してください。
このプログラムがコンパイルするクラスを型に持つフィールド(配列も含む)を、インスペクタでシリアライズすること インスタンスはプログラムの実行中にしか存在しないため、Unity にはインスペクタに出すものも保存するものもなく、そのフィールドは空のままワールドに届きます。private にするか [NonSerialized] を付けて、Start で埋めてください。これはフィールドの話であって型の話ではありません。クラス自体はローカル変数、引数、private フィールドとしては問題なく使えます。プリミティブ、Unity の型、SDK の enum、ビヘイビア参照のフィールドや配列も問題ありません。
デリゲートを経由する再帰(UDN0012) 直接の再帰と相互再帰はコンパイルできます。デリゲート呼び出しを含む循環には退避先を決められる呼び出し先がないため、循環の中ではメソッドを直接呼んでください。

まだ対応していないもの

UDN0002 で拒否されます。これは Udon ではなく Udonite 側の不足で、要望のあったものから解除されていきます。

  • ループ内で宣言したキャプチャ付きラムダ。

拒否で行き詰まったら、その拒否の行を添えて Issue を立ててください。名前さえ分かれば、たいてい一日ほどで対応できます。

問題が発生しました 再読み込み