语言支持
现在能编译哪些写法,以及 Udon 永远无法运行的那部分 C#。
Udonite 能编译日常 C# 中相当大的一部分。下面列出的每一项都有测试覆盖:这些测试会汇编输出,并在真正的 Udon 虚拟机上运行。
能编译的部分
类型与成员
class
struct
interface
enum
嵌套类型
把自己的类用作字段和局部变量
带约束的泛型
带默认实现的接口方法
属性
自动属性
表达式体成员
索引器
扩展方法
params
可选参数
命名参数
out / ref 参数
元组与解构
局部函数
静态工具类
record 和 record struct:位置构造、with、值相等性、ToString、解构。静态字段必须是 const 或 readonly(见下文)。
语句与表达式
if
switch 语句
switch 表达式
for
foreach
while
do
break
continue
模式匹配
类型模式
is not null
关系模式
属性模式
when 守卫
?.
??
??=
带格式说明符的字符串插值
nameof
算术运算符
位运算符
移位运算符
[Flags] 枚举
HasFlag
char 运算
数组
多维数组
范围与索引
Array.Sort
Find
FindAll
Exists
IndexOf
Resize
Copy
Fill
ConvertAll
List<T>
Dictionary<K,V>
HashSet<T>
Queue<T>
Stack<T>
LINQ
Action / Func 字段
方法组
多播 += / -=
C# event
指向另一个 behaviour 上 public 方法的委托
作为值的 lambda
StringBuilder
string.Format
Split
Join
Substring
Replace
Trim
PadLeft
可空值类型
HasValue
GetValueOrDefault
关系模式写作 is > 5 and < 10。范围与索引指 arr[^1] 和 arr[1..3]。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 辅助方法的 lambda 会在调用处内联展开。方法组写作 Action done = Cleanup;。指向另一个 behaviour 上 public 方法的委托,以及作为值的 lambda,两者都能编译;会捕获变量的 lambda 有一条限制,下文会说明。
Unity 与 VRChat
GetComponent<T>()
TryGetComponent<T>(out T)
Instantiate
Destroy
SetActive
Transform
物理查询
Mathf
Vector3
Quaternion
Color
Random
Time
协程
yield return null
WaitForSeconds
WaitUntil
嵌套协程
StartCoroutine(Run())
StartCoroutine(nameof(Run))
StopAllCoroutines
Invoke
InvokeRepeating
CancelInvoke
async 方法
Task
UniTask
UniTaskVoid
await
VRCPlayerApi
Networking
拾取物
座位
UdonBehaviour 引用
SendCustomEvent
SetProgramVariable
GetProgramVariable<T>
Invoke、InvokeRepeating、CancelInvoke 可以用在 public 方法上。async 方法可以返回 void、Task、UniTask 或 UniTaskVoid,可以 await Task.Delay(...),也可以等待其他 async 方法。网络一页中的全部内容。
Udon 永远无法运行的部分
这些会被 UDN0011 拒绝。提示信息会说明原因,并给出替代写法。
| 写法 | 原因,以及应该改写成什么 |
|---|---|
try/catch/throw |
Udon 没有异常机制。请先判断条件。 |
可变的 static 字段 |
每个 behaviour 都有自己的堆,所以静态字段会变成「每个 behaviour 一份」,而不是共享的一份。请把它放在某一个 behaviour 的字段上,然后引用它。 |
Awake |
Udon 不会派发它。请使用 Start。 |
CompareTag 和 gameObject.tag |
Udon 不暴露 tag。请比较层级或名称,或者直接持有引用。 |
IsInvoking、Application.isPlaying、Time.timeScale、AudioListener、new GameObject()、ScriptableObject 的派生类 |
Udon 均未暴露。 |
| 在 Inspector 字段里序列化自定义类或结构体的数组 | Udon 无法反序列化。请使用基元类型、Unity 类型或 behaviour 引用的数组。 |
| 指向普通对象(而非 behaviour)方法的委托,以及放在 Unity 会序列化的 public 字段中的委托 | 两者都无法带进世界里。 |
| 超出创建它的方法之后仍然保留的捕获型 lambda(赋值给字段、作为返回值、作为参数传出,或在循环中创建) | 被捕获的变量位于 behaviour 唯一的堆上,因此保存下来的闭包在下次调用时会看到过时的状态。不捕获的 lambda 可以随意保存;捕获型的,只要作为局部变量在同一方法内调用就没问题。 |
暂时还不支持
这些会被 UDN0002 拒绝。它们是 Udonite 的缺口,而不是 Udon 的限制;有人提出需求的通常会被补上。
- 列表模式(
is [1, ..])。 - 作为独立表达式的
typeof(T),以及非泛型的GetComponent(typeof(T))。 - 递归(
UDN0012)、循环内的捕获型 lambda、checked运算。
如果某个拒绝挡住了你,请带上那一行拒绝信息提交 issue。只要说清楚是哪种写法,通常一天就能补上。