언어 지원

지금 컴파일되는 것과, Udon이 결코 실행할 수 없는 C#의 영역.

업데이트 2026-09-08

Udonite는 일상적인 C#의 상당 부분을 컴파일합니다. 아래의 모든 구문은 출력물을 어셈블해 실제 Udon 가상 머신에서 실행하는 테스트로 확인하고 있습니다.

컴파일되는 것

형식과 멤버

class struct interface enum record(선언 하나 필요) 중첩 형식 직접 만든 클래스를 필드와 지역 변수로 제약 조건이 있는 제네릭 인터페이스 기본 구현 속성과 인덱서 확장 메서드 선택적 매개변수와 명명된 인수 params outref 매개변수 튜플과 분해 값으로서의 typeof 지역 함수 정적 도우미 클래스

레코드: 위치 기반 생성, with, 값 동등성, ToString, 분해. 다만 먼저 자신의 스크립트에 선언을 하나 넣어야 합니다. recordinit 세터는 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 멤버 virtualoverride base. 호출 abstract behaviour

기반 형식을 통한 호출은 가장 파생된 재정의에 도달하고, base.Method()는 정확히 한 단계 위에 도달합니다. 평범한 클래스에서도, GameObject에 올라간 behaviour에서도 같습니다.

컴포넌트끼리 계약을 공유하는 방법은 abstract 기반 클래스입니다. Unity가 인터페이스 필드를 직렬화하지 못하기 때문입니다. 인터페이스를 참고하세요.

문과 식

제어 흐름 switch 식 패턴 매칭 null 연산자 문자열 보간 산술·비트·시프트 연산자 플래그 열거형 배열과 다차원 배열 범위와 인덱스 Array 도우미 List<T> Dictionary<K,V> HashSet<T> Queue<T> Stack<T> LINQ 델리게이트와 이벤트 람다 문자열과 StringBuilder 널 허용 값 형식 열거형 파싱과 이름 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로 비교하며 IsAssignableFromIsInstanceOfType도 부를 수 있습니다. 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 도우미에 넘긴 람다는 호출 지점에 인라인됩니다.

델리게이트는 ActionFunc 필드, Action done = Cleanup; 같은 메서드 그룹, 멀티캐스트 +=-=, C# event 선언, 다른 behaviour의 public 메서드를 가리키는 델리게이트입니다. 람다는 값으로 쓸 수 있으며, 변수를 캡처하는 람다에는 한 가지 제약이 있어 아래에서 다룹니다. 문자열에는 Format, Split, Join, Substring, Replace, Trim, PadLeft가 있습니다. 널 허용 값 형식에는 HasValueGetValueOrDefault가 있습니다. 열거형에는 Parse, TryParse, GetNames, GetValues, HasFlag가 있고, typeof(T).Name도 컴파일됩니다. throw는 메시지를 로그에 남기고 behaviour를 멈추므로 단언문처럼 쓸 수 있습니다. 잡을 것은 없습니다. 직접 재귀와 상호 재귀는 컴파일되며, 호출 전후로 메서드의 변수가 스택에 대피됩니다.

Unity와 VRChat

컴포넌트 조회 InstantiateDestroy Transform과 물리 Mathf와 값 형식 RandomTime 코루틴 Invoke 계열 asyncawait VRCPlayerApi Networking 픽업과 스테이션 UdonBehaviour 참조 다른 behaviour의 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>가 있습니다. 다른 behaviour의 gameObject, transform, enabled는 직접 읽고 씁니다. Unity 형식, 컴포넌트, SDK 열거형의 배열은 여느 필드처럼 씁니다. 네트워크에 있는 모든 것.

셰이더와 머티리얼에 쓰기

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, 그리고 MaterialPropertyBlockEnableKeyword. material 대신 sharedMaterial을 쓰세요. material을 건드리면 런타임에 머티리얼이 복제되고, 런타임 머티리얼은 씬 디스크립터에 등록하지 않으면 빌드에서 제거됩니다.

2026-09-08, 컴파일만이 아니라 업로드한 월드에서 두 경로 모두 확인했습니다.

Udon이 결코 실행할 수 없는 것

이것들은 UDN0011로 거부됩니다. 거부 메시지가 그 사실과 대안을 함께 알려 줍니다.

구문 이유와 대신 쓸 것
catch Udon에는 잡을 예외가 없습니다. 오류가 나면 behaviour가 멈춥니다. catch를 없애고 조건을 먼저 확인하세요. tryfinally는 컴파일되고, throw는 메시지를 로그에 남기며 behaviour를 멈춥니다.
변경 가능한 static 필드 behaviour마다 자기 힙을 가지므로, static은 공유된 하나의 값이 아니라 behaviour마다 하나씩이 되어 버립니다. 한 behaviour의 필드로 두고 그것을 참조하거나, 저장 공간이 없는 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 프로퍼티입니다.
behaviour가 아닌 일반 객체의 메서드를 가리키는 델리게이트, 그리고 Unity가 직렬화하는 public 필드에 놓인 델리게이트 둘 다 월드까지 가지고 갈 수 없습니다.
자신을 만든 메서드 밖까지 살아남는 캡처 람다(필드에 대입, 반환, 인수로 전달) 캡처된 변수는 behaviour의 단일 힙에 있으므로, 저장된 클로저는 다음 호출에서 낡은 상태를 보게 됩니다. 캡처하지 않는 람다는 자유롭게 저장할 수 있고, 캡처하는 람다는 같은 메서드 안에서 호출하는 지역 변수로는 잘 동작합니다.
checked 연산 Udon의 연산자 extern은 오버플로에서 예외를 내지 않으며, 애초에 Udon에는 낼 예외가 없습니다. 연산 전에 피연산자를 확인하세요.
이 프로그램이 컴파일하는 클래스를 형식으로 갖는 필드(배열 포함)를 인스펙터에서 직렬화하는 것 인스턴스는 프로그램이 도는 동안에만 존재하므로 Unity가 인스펙터에 넣을 것도 저장할 것도 없고, 그 필드는 빈 채로 월드에 도착합니다. private으로 하거나 [NonSerialized]를 붙이고 Start에서 채우세요. 이것은 형식이 아니라 필드에 대한 이야기입니다. 클래스 자체는 지역 변수, 매개변수, private 필드로는 잘 동작합니다. 기본 형식, Unity 형식, SDK 열거형, behaviour 참조의 필드와 배열은 괜찮습니다.
델리게이트를 거치는 재귀(UDN0012) 직접 재귀와 상호 재귀는 컴파일됩니다. 델리게이트 호출을 지나는 순환에는 값을 대피시킬 호출 대상이 정해져 있지 않으므로, 순환 안에서는 메서드를 직접 호출하세요.

아직 지원하지 않는 것

UDN0002로 거부됩니다. 이것은 Udon이 아니라 Udonite 쪽의 빈틈이며, 요청이 있는 것부터 풀립니다.

  • 반복문 안에서 선언한 캡처 람다.

거부 때문에 막히면 그 거부 줄과 함께 이슈를 남겨 주세요. 무엇인지만 특정되면 보통 하루면 됩니다.

문제가 발생했습니다 새로 고침