关于 unity-vrc-udon-sharp
面向 VRChat 世界中 UdonSharp 编码的开发技能,说明运行时限制、同步状态所有权和晚加入者行为,帮助避免编译失败与无提示的网络错误。
主要功能
- 01Udon 运行时限制指南
- 02同步变量所有权规则
- 03晚加入者状态设计
- 04序列化与反序列化模式
- 05编辑器求值的初始化器指南
适用场景
- 01规划网络状态的所有权
- 02处理玩家晚加入时的状态
- 03避免在 Udon 运行时使用不支持的 C# 构造
- 04通过字段初始化器生成烘焙值
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ niaka3dayo/ agent-skills-vrc-udon --skill unity-vrc-udon-sharp安装后在 Claude Code 中输入/unity-vrc-udon-sharp确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
CHEATSHEET.mdLICENSE.txtassets/templates/ArrayUtils.csBasicInteraction.csBatchedSync.csCloggedRetrySync.csContactReceiver.csCustomInspector.csDataPersistence.csDualCopySync.csEventBus.csMasterManagedPlayerPool.csPackedStateSync.csPlayerSettings.csRateLimitedSync.csStateMachine.csSyncedObject.csUdonSharpProgramAssetAutoGenerator.csUndoableGameManager.cshooks/validate-udonsharp.ps1validate-udonsharp.shreferences/api.mdassembly-definitions.mdconstraints.mdcontext-preservation.mddynamics.mdeditor-scripting.mdevents.mdimage-loading-vram.mdnetworking-antipatterns.md详细介绍
UdonSharp 技能
为什么这项技能很重要
UdonSharp 看起来就像常规的 Unity C# 脚本编写,直到你撞上它隐藏的限制。许多标准 C# 功能(List<T>、async/await、try/catch、LINQ、泛型)在 Udon 运行时中运行的代码里会静默失败或无法编译。由编辑器求值的字段初始化器属于另一种上下文:它们可以使用某些普通 C# 功能来生成 Udon 能保存的最终值。网络同步则更容易出问题:修改同步变量时若不具有所有权,不会报错,只会什么也不做。忘记调用 RequestSerialization,意味着状态变化永远不会离开你的机器。标准的单人本地测试完全无法暴露这些网络同步错误,因为场景中只有一名玩家。
这项技能中的每条规则,都是因为 UdonSharp 的默认行为是静默失败。生成任何代码前,请先阅读规则。
编写网络代码前
在选择同步模式或编写任何同步变量之前,必须先做出四项架构决策。实现过程中途更改这些决策通常需要彻底重写:
- 谁拥有此状态? 由一名所有者写入,其他人读取。如果两名玩家都能写入(例如,共享开关),就需要所有权转移协议——不具有所有权时的写入会被静默丢弃。
- 何时转移所有权? 抓取时?交互时?游戏事件发生时?还是
OnPlayerLeft时?调用Networking.SetOwner后,所有权会立即在本地生效——调用方客户端在该调用返回后同步执行Networking.IsOwner(gameObject)会得到true;因此,在IsOwner检查的保护下,紧接着写入[UdonSynced]字段并调用RequestSerialization()是安全的。多个客户端并发调用SetOwner时,会按网络到达顺序处理——客户端之间不会进行仲裁,因此要接受落败一方的写入会被覆盖。 - 迟加入者会看到什么? 仅通过一次性事件(
SendCustomNetworkEvent)设置的状态,对迟加入者不可见。迟加入者可见的状态必须存储在[UdonSynced]变量中,这些变量会通过OnDeserialization自动传递;加入时无需手动调用RequestSerialization()。 - 所有者在会话中途离开怎么办? VRChat 会自动将所有权转移给一名剩余玩家(选择规则尚未公开说明),并在所有客户端触发
OnOwnershipTransferred。同步变量会保留,因此状态不会被冻结;请提前决定是保留当前值、重置为已知默认值,还是在OnOwnershipTransferred中重新应用/广播派生状态。
上下文保留
对于复杂的同步系统、与所有权相关的重构,或在压缩/交接后恢复的工作,可以考虑加载 references/context-preservation.md。
它提供了一份轻量级任务上下文记录,用于说明事实来源、传输方式、同步模式、存储、所有权、迟加入者行为以及验证依据。
这只是复杂工作的可选指导,并非小型机械式修改的步骤。
请勿在任何记录中放入私人数据或原始对话记录。
对于涉及世界场景设置,而非 UdonSharp 编译器限制的 VRChat SDK Build Panel 验证提醒、红色/黄色/白色警告或 Auto Fix 副作用,请使用 unity-vrc-world-sdk-3 并阅读 references/build-validation.md。
核心原则
- 约束优先 — 对于 Udon 运行时代码,在确认之前,先假设标准 C# 功能不可用。将由 Editor 计算的字段初始值设定项单独处理,并确保最终值受 Udon 支持。使用任何 API 前,请先检查
udonsharp-constraints.md。 - 修改前先取得所有权 — 只有对象的所有者才能修改其同步变量。始终按
SetOwner→ 修改 →RequestSerialization的顺序操作。 - 确保后来加入者状态正确 — 对于在事件发生后加入的玩家,状态也必须正确。应针对重新序列化进行设计,而不只是支持实时更新。
- 尽量减少同步 — 每个同步变量都会占用带宽(参见
udonsharp-sync-selection.md中的数据预算)。能在本地推导的就本地推导;只同步唯一可信的数据源。 - 事件驱动,而非轮询 — 对于状态变化响应,请使用
OnDeserialization、[FieldChangeCallback]和SendCustomEvent,而不是在Update()中检查状态;对于热路径或周期性工作,请参见事件分发与跨行为调用开销等级。
同步数组:始终在 OnDeserialization() 中应用数组内容。数组元素的变化不会可靠地触发 FieldChangeCallback 信号;相同长度的内容变化、数组重新赋值和长度变化也遵循相同原则。让所有者在修改后立即调用同一个幂等的应用方法,然后只请求一次手动序列化。如果历史一次性副作用受版本号检查保护,后来加入者首次收到的 OnDeserialization() 会包含当前版本号,否则可能会重放该副作用。首次收到版本号时,应记录基准值而不触发副作用,但在检查基准值之前仍要运行持久化的 ApplyValues();只有后续版本号才应触发一次性操作。版本号不能用于排序或防止过期数据包。
SDK 3.10.4 事件接收器参数
SDK 3.10.5 是此 Skill 当前有效且已验证的目标版本。
SDK 3.10.4:UdonSharpBehaviour 直接实现 IUdonEventReceiver。
因此,需要接收器的 API 可以直接接收 this:
VRCStringDownloader.LoadUrl(dataUrl, this);
仍然必须提供接收器参数;只有在当前 SDK 中才不需要显式的 (IUdonEventReceiver) 强制转换。仅在历史迁移参考中保留任何 3.10.4 之前的强制转换。这符合官方 SDK 3.10.4 发布说明和 SDK 源代码声明。
公共字段:Inspector 可见性不等于持久化策略
[HideInInspector] 只会在 Inspector 中隐藏公共字段;Unity 仍会序列化该字段。当 Editor 时期的 DI、烘焙或自动装配必须将值持久化到场景/Prefab 中时,请使用 [HideInInspector] public。对于其他 UdonBehaviour 必须通过直接访问或 SetProgramVariable 访问的纯运行时值,请使用 [System.NonSerialized] public。有意使用 [HideInInspector] 时,请留下注释说明为何必须持久化该值。
编辑器求值的字段初始化器
字段初始化器会在 Unity/Editor 端作为普通 C# 代码求值,为编译后的 Udon 程序生成初始数据;其中的表达式不会在 Udon 运行时执行。初始化器中的 Random.Range 调用会在 Editor 中求值并存储为烘焙后的默认值,而不是运行时随机值。因此,即使 LINQ、lambda 或使用 List<T> 的同一行为静态辅助方法无法在 Start()、Interact() 或其他 Udon 运行时方法中使用,它们仍可用于生成数组初始化器。最终字段类型和值必须受 Udon 支持。生成过程应独立于场景、玩家和运行时状态,并且不要调用仅限主线程的 Unity API,因为字段初始化器和构造函数可能会在加载线程上运行。两种受支持的形式及其边界,请参阅 references/constraints.md。
仅对本地或每个客户端的随机性使用 Start() 或延迟初始化保护。对于对象或会话共享的种子/状态,由所有者生成并存储到 [UdonSynced] 字段中;使用 Manual 同步时,应先取得所有权,再写入并调用 RequestSerialization()。接收方可在需要时于 OnDeserialization() 中应用派生状态,但字段同步本身不要求使用此回调,且后来加入的用户会收到当前同步状态。
常见错误(绝对不要这样做)
这些 Udon 运行时和 Unity 序列化限制会导致编译时错误或静默数据错误。编写 UdonSharp 代码或序列化初始值之前,请先检查此列表。
| # | 绝对不要这样做 | 为什么会悄无声息地失败 | 改用以下方式 |
|---|---|---|---|
| 1 | 在 Udon 运行时代码中使用 List<T>、Dictionary<T,K> 或任何泛型集合 | 编译错误——Udon 编译器会阻止此操作 | 使用 T[] 数组、DataList、DataDictionary(DataDictionary.EnsureCapacity / 自定义容量要求 SDK 3.10.4+);上文提到的编辑器求值字段生成是有限的例外 |
| 2 | 使用 async/await、System.Threading 或协程 | Udon 是单线程的;这些功能并不存在 | 使用 SendCustomEventDelayedSeconds() |
| 3 | 在不拥有对象的情况下修改 [UdonSynced] 字段 | 更改看起来已在本地生效,但会在下一次反序列化时悄然还原 | 修改前调用 Networking.SetOwner(),然后调用 RequestSerialization() |
| 4 | 修改同步字段(Manual 同步)后忘记调用 RequestSerialization() | 状态更改永远不会离开本地客户端——没有错误,也没有警告 | 修改 [UdonSynced] 字段后务必调用 RequestSerialization() |
| 5 | 使用 try/catch/finally/throw | 编译错误——异常处理会被阻止 | 进行防御性空值检查并提前返回 |
| 6 | 在字段初始化器中访问 Networking.LocalPlayer | 编辑器端初始值生成时没有玩家或 Udon 运行时状态 | 在 Start() 中初始化,或使用延迟初始化保护 |
| 7 | 使用 static 字段保存每个实例的状态 | 同一客户端上的所有实例会共享静态字段,且这些字段不会同步 | 需要同步时,使用带有 [UdonSynced] 的实例字段 |
| 8 | 在 Manual 同步中每帧调用 RequestSerialization() | 会耗尽约 11 KB/s 的网络带宽预算,导致整个世界发生拥塞 | 通过变更检测将频率限制在 1-10 Hz;检查 Networking.IsClogged |
| 9 | 在 Udon 运行时代码中使用 LINQ(.Where、.Select 等)或 lambda 表达式 | 编译错误——Udon 编译器不支持 | 使用带命名方法的手动 for 循环;上文提到的编辑器求值字段生成是有限的例外 |
| 10 | 使用 Button.onClick.AddListener() | Udon 不提供此功能——不支持运行时委托 | 在 Inspector OnClick 中配置 SendCustomEvent |
| 11 | 在一个行为中混用 Continuous 和 Manual 同步相关逻辑 | 会浪费带宽(Continuous 同步离散值),或失去控制(Continuous 中多余的 RequestSerialization) | 分开使用不同的行为:位置/旋转使用 Continuous,离散状态使用 Manual |
| 12 | 在没有 IsOwner 保护的情况下写入 [UdonSynced] 字段 | 非所有者的写入仅在本地生效,并会在下一次从实际所有者反序列化时悄然还原 | 如有需要,先调用 Networking.SetOwner(本地立即生效),然后在 IsOwner 条件下写入并调用 RequestSerialization() |
| 13 | 在低于 3.8.1 的不受支持 SDK 上使用 [NetworkCallable](仅限历史迁移) | 编译错误——该属性和带参数的网络事件 API 不可用 | 使用当前 SDK 目标版本 3.10.5;对于历史迁移说明,请改用同步变量,并在 OnDeserialization/FieldChangeCallback 中响应,而不是将其与网络事件配对使用 |
| 14 | 在低于 3.10.0 的不受支持 SDK 上使用 PhysBones/Contacts API(OnPhysBoneGrabbed、OnContactEnter 等)(仅限历史迁移) | 可以编译,但在运行时会被悄然忽略——3.10.0 之前不存在世界端 Dynamics,因此回调永远不会触发 | 使用当前 SDK 目标版本 3.10.5;对于历史迁移,请确认项目使用的 SDK 至少为 3.10.0 |
| 15 | 在低于 3.7.4 的不受支持 SDK 上使用 PlayerData 持久化 API(仅限历史迁移) | 编译错误——缺少符号;PlayerData、PlayerObject 和 OnPlayerRestored 是在 3.7.4 中添加的,在此之前不属于 Udon 白名单 | 使用当前 SDK 目标版本 3.10.5;对于历史迁移,请确认项目使用的 SDK 至少为 3.7.4 |
| 16 | 在 UdonSharpBehaviour 外使用 Unity .asmdef,却没有匹配的 U# Assembly Definition | Unity 会编译 C# 程序集,但 UdonSharp 会报告该脚本不属于 U# 程序集 | 对于简单的世界脚本,避免使用 asmdef;对于包/asmdef 工作流,请创建对应的 U# Assembly Definition,并将 Source Assembly 设置为 Unity .asmdef(参见 references/assembly-definitions.md) |
| 17 | 创建 .cs 脚本,却没有对应的 .asset 文件 | 脚本不会被识别为 UdonBehaviour——出现“The associated script cannot be loaded”错误,且不会进行 Udon 编译 | 每次创建 .cs 时:确认 Assets/Editor/UdonSharpProgramAssetAutoGenerator.cs 存在;若缺失,请按照 references/editor-scripting.md 安装,并通知用户(参见 rules/udonsharp-constraints.md 中的规则 8) |
| 18 | 在 Update()、PostLateUpdate() 或任何逐帧事件中调用 Debug.Log() | VRChat 的客户端日志速率限制器会悄然丢弃超出的日志条目;每帧隐式分配字符串会造成持续的 GC 压力,导致帧率骤降。ClientSim 和 Unity Editor 会掩盖这两种问题 | 使用 if (debugMode && Time.frameCount % 60 == 0) 添加保护条件,或将所有日志移到事件驱动的回调中 |
| 19 | 在 GameObject、Transform、UdonBehaviour 或任何组件引用上使用 [UdonSynced] | 只有基本类型、值类型(Vector3、Quaternion、Color 等)、string、VRCUrl 及其简单数组可以同步。组件引用会因 SDK 版本不同而导致编译失败或永远不会被序列化 | 同步玩家 ID(int)或场景对象索引(int),并在每个客户端本地解析实际引用 |
| 20 | 使用 VRCUrl.Empty 初始化任何需要独立初始值的 UdonSharp VRCUrl 字段 | VRCUrl.Empty 是共享实例,因此 Inspector 数据可能会在使用同一 UdonSharp 程序的字段、数组元素或 GameObject 之间泄漏 | 为 [SerializeField]、[UdonSynced] 和普通私有字段,以及每个数组元素,分别设置其自己的 new VRCUrl("");仅将 VRCUrl.Empty 用作运行时哨兵值 |
同步模式快速决策
Changing every frame (position, rotation)? -> Continuous sync
Changing on user action (toggle, score)? -> Manual sync + RequestSerialization()
No sync needed (local UI, effects)? -> NoVariableSync
Need reliable one-shot calls with params? -> [NetworkCallable] (introduced in SDK 3.8.1; active target 3.10.5)
Temporary effect for all players, no state? -> SendCustomNetworkEvent (no synced vars)
如需查看详细决策树、数据预算和最小化原则,请参阅
rules/udonsharp-sync-selection.md。
3.8.1 之前的 SDK 未定义 NetworkCallable 特性或带参数的网络事件 API,因此使用这些功能的代码通常会编译失败。
[NetworkCallable] 方法必须返回 void。
同步调试快速决策
当同步“在本地看起来正确,但对其他人不起作用”时:
Remote players don't see my state change?
├── Did I call RequestSerialization() after writing? (Manual sync) → Add it
├── Does the local player own the object? → Networking.SetOwner() first
└── Using Continuous sync for button/toggle state? → Switch to Manual + RequestSerialization()
RequestSerialization() called but still not syncing?
├── Is Networking.IsClogged == true? → Throttle; retry after delay
└── Non-owner writing the field? → Acquire ownership first — a non-owner RequestSerialization() is a silent no-op (see NEVER #12)
Late joiners don't see current state?
├── State set only on event (e.g., player trigger)? → Verify the state lives in a [UdonSynced] field — synced values are delivered to late joiners automatically; SendCustomNetworkEvent calls before join are never replayed
└── Using SendCustomNetworkEvent for persistent state? → Use [UdonSynced] variables instead
OnOwnershipTransferred not firing on a remote client?
└── On the caller, the callback fires synchronously inside SetOwner — confirm the calling client called Networking.SetOwner(LocalPlayer, gameObject), and that remote clients resolve the same gameObject reference (scene path or prefab GUID)
参考资料加载指南
只加载所需内容。加载过多会浪费 token;加载不足则会导致关键错误。
| 任务 | 必须阅读 | 可选 | 不要加载 |
|---|---|---|---|
| 编写网络/同步代码 | networking.md、networking-antipatterns.md | networking-bandwidth.md、sync-examples.md | dynamics.md、web-loading.md、image-loading-vram.md |
| 构建 UI/菜单 | patterns-ui.md、events.md | patterns-core.md、api.md | networking-bandwidth.md、dynamics.md、web-loading.md |
| 实现持久化(保存/加载) | persistence.md | patterns-networking.md、events.md | dynamics.md、web-loading.md、image-loading-vram.md |
| 从 Web 下载字符串/图像 | web-loading.md | web-loading-advanced.md、image-loading-vram.md | dynamics.md、persistence.md、networking-bandwidth.md |
| 使用 VRCTween、可取消的延迟调用或补间清理 | vrctween.md | patterns-utilities.md、api.md | dynamics.md、web-loading.md、persistence.md |
使用 PhysBones/Contacts/Constraints、Box Contacts、Global Avatar PhysBone Colliders 或世界中的 VRCPhysBoneCollider Udon 访问 | dynamics.md、events.md | patterns-networking.md、api.md | web-loading.md、image-loading-vram.md、persistence.md |
| 在运行时读取/写入 VRCQualitySettings | api.md | events.md;场景默认值:unity-vrc-world-sdk-3 | networking.md、dynamics.md、web-loading.md |
调整 DataList/DataDictionary 容量或使用 DataDictionary.EnsureCapacity | api.md | constraints.md、patterns-utilities.md、web-loading.md | dynamics.md、persistence.md、networking-bandwidth.md |
| 优化性能(Update 循环) | patterns-performance.md | patterns-utilities.md、api.md | dynamics.md、web-loading.md、persistence.md |
| 构建视频播放器 | patterns-video.md | events.md、web-loading.md | dynamics.md、persistence.md、image-loading-vram.md |
| 调试/故障排除 | troubleshooting.md | constraints.md、networking.md、testing.md | patterns-*.md、dynamics.md、web-loading.md |
| 调试所有权/同步冲突 | networking.md、troubleshooting.md | networking-antipatterns.md | dynamics.md、web-loading.md |
| 在 SDK 版本间迁移/修复升级后的问题 | sdk-migration.md | troubleshooting.md、networking.md、dynamics.md | web-loading.md、image-loading-vram.md |
| 在压缩/交接/所有权敏感的多文件重构后恢复复杂工作 | 当前任务的主要参考资料 | context-preservation.md | 不相关领域的参考资料 |
| 编写新的 UdonSharp 脚本(不确定是否需要同步) | constraints.md | networking.md | dynamics.md、web-loading.md、image-loading-vram.md |
| 设置新的脚本文件(.cs/.asset 连接、程序资源生成) | editor-scripting.md | troubleshooting.md | networking.md、dynamics.md |
| VPM/包/asmdef 工作流、Assembly Version Defines、U# Assembly Definition 连接、Auto Referenced 决策 | assembly-definitions.md | editor-scripting.md、troubleshooting.md | networking.md、dynamics.md、web-loading.md |
| 构建编辑器设置工具/放置 UX(自定义检查器、场景连接辅助工具、IEditorOnly) | editor-scripting.md | constraints.md、assembly-definitions.md | networking.md、dynamics.md、web-loading.md |
模式选择指南
六个模式文件涵盖不同领域。使用以下快速指引选择合适的文件:
Building a UI, menu, or HUD? -> patterns-ui.md
VR finger/touch interaction on Canvas? -> patterns-ui.md
Modular app with multiple screens? -> patterns-ui.md
Syncing state across players? -> patterns-networking.md
Multiple identical rooms from one model? -> patterns-networking.md (distant-room)
Optimizing Update() or heavy loops? -> patterns-performance.md
Heavy rebuild, replay, or reset/cancel? -> patterns-performance.md
Playing or streaming video? -> patterns-video.md
Need array helpers, event bus, or -> patterns-utilities.md
pseudo-delegates?
Basic interactions, timers, audio, -> patterns-core.md
pickups, or teleportation?
Station + trigger zone detection? -> troubleshooting.md
涉及多个方面?加载主要模式文件及其依赖项。例如,同步视频播放器需要同时使用
patterns-video.md和patterns-networking.md。
模板选择指南
17 个模板涵盖常见起点。选择最接近的模板并进行调整:
| 起点 | 模板 | 主要特性 |
|---|---|---|
| 交互与对象 | ||
| 可交互对象(点击/使用) | BasicInteraction.cs | 冷却、切换、音频反馈 |
| 同步切换/共享对象 | SyncedObject.cs | 所有权保护、FieldChangeCallback、加入较晚的玩家初始化 |
| 每位玩家的移动设置 | PlayerSettings.cs | 通过触发区域设置行走/奔跑/跳跃速度 |
| 基于接触的碰撞检测 | ContactReceiver.cs | OnContactEnter/Exit、头像与世界对象区分、防抖(在 SDK 3.10.0 中引入;当前目标版本为 3.10.5) |
| 状态与游戏逻辑 | ||
| 状态机/游戏流程 | StateMachine.cs | 定时转换、同步状态、确保加入较晚的玩家安全 |
| 支持撤销/历史记录的游戏 | UndoableGameManager.cs | byte[] 历史记录、NetworkCallable _OwnerProcessMove/_OwnerUndo/_OwnerReset |
| 对象池(玩家槽位) | MasterManagedPlayerPool.cs | FIFO 环形缓冲区、由主控管理、OnPlayerJoined/Left |
| 持久化与数据 | ||
| 保存/加载玩家数据 | DataPersistence.cs | PlayerData API、OnPlayerRestored、自动保存(在 SDK 3.7.4 中引入;当前目标版本为 3.10.5) |
| 网络模式 | ||
| 限速同步(拖动滑块) | RateLimitedSync.cs | 0.15s 冷却、最后写入优先 |
| 批量同步(快速事件) | BatchedSync.cs | 幂等调度、0.2s 延迟、单个数据包 |
| 拥塞感知重试 | CloggedRetrySync.cs | IsClogged 检查、线性退避、MaxRetries |
| 本地副本与同步副本并存 | DualCopySync.cs | 本地工作副本 + 同步传输、脏标记 |
| 将多个值打包到一个字段 | PackedStateSync.cs | 在一个 Vector3 中打包 3 个整数,减少同步开销 |
| 实用工具 | ||
| 数组辅助工具(List<T> 的替代方案) | ArrayUtils.cs | 为数组提供 Add、Remove、Contains、FindIndex、Shuffle |
| 事件总线(发布/订阅) | EventBus.cs | 订阅者列表(最多 32 个)、RegisterListener/RaiseEvent |
| 自定义编辑器检查器 | CustomInspector.cs | UdonSharpGUI、Undo、代理同步 |
| 为新脚本自动生成 .asset | UdonSharpProgramAssetAutoGenerator.cs | AssetPostprocessor、仅域重载时运行、自动编译 |
有多个需求? 从最符合主要需求的模板开始,再从其他模板中选取模式。例如,支持撤销的同步游戏需要以
UndoableGameManager.cs为基础,并加入RateLimitedSync.cs中用于限流的模式。
规则(约束与网络)
编译约束和网络规则定义在始终加载的规则中:
| 规则文件 | 内容 |
|---|---|
rules/udonsharp-constraints.md | 受限功能、代码生成规则、特性、可同步类型 |
rules/udonsharp-networking.md | 所有权、同步模式、RequestSerialization、NetworkCallable、网络事件发送者授权 |
rules/udonsharp-sync-selection.md | 同步模式选择、数据预算、最小化原则 |
安装后,将这些文件放入 Agent 的规则目录,以便自动加载。
SDK 版本
当前支持/最后验证版本:SDK 3.10.5
从 v4.0.0 起,策略是仅支持最新稳定版 SDK;只有在本仓库验证新的稳定版后,支持版本才会更新。新稳定版不会自动获得支持。目前最后验证的目标版本:3.10.5。
下表保留了功能引入历史,供迁移参考。SDK 3.7.1-3.10.4 的条目仅为历史信息;它们不是此 Skill 当前支持或验证的目标版本。这是此 Skill 的支持范围,并不代表 VRChat 自身的 SDK 政策。除非参考资料明确标注为历史迁移案例,否则主要生成示例均以 SDK 3.10.5 为目标。
| SDK 版本 | 主要功能 | 状态 |
|---|---|---|
| 3.7.1 | 新增 StringBuilder、RegularExpressions、System.Random | 历史版本 |
| 3.7.4 | 新增 Persistence API(PlayerData/PlayerObject) | 历史版本 |
| 3.7.6 | 多平台构建与发布(同时支持 PC + Android) | 历史版本 |
| 3.8.0 | PhysBone 依赖项排序、Drone API(VRCDroneInteractable) | 历史版本 |
| 3.8.1 | [NetworkCallable] 特性、带参数的网络事件、NetworkCalling.CallingPlayer/.InNetworkCall、NetworkEventTarget.Others/.Self | 历史版本 |
| 3.9.0 | Camera Dolly API、简化 Auto Hold 拾取功能 | 历史版本 |
| 3.10.0 | VRChat Worlds 动态系统(PhysBones、Contacts、VRC Constraints) | 历史版本 |
| 3.10.1 | 错误修复与稳定性改进 | 历史版本 |
| 3.10.2 | EventTiming 扩展、PhysBones 修复、着色器时间全局变量 | 历史版本 |
| 3.10.3 | VRCPlayerApi.isVRCPlus、VRCRaycast(Avatar)、Mirror 渲染顺序修复 | 历史版本 |
| 3.10.4 | VRCTween、盒形 Contacts、全局 Avatar PhysBone Colliders、世界中的 VRCPhysBoneCollider Udon 访问、DataList/DataDictionary 自定义容量、DataDictionary.EnsureCapacity、UdonSharpBehaviour 实现 IUdonEventReceiver 并接受直接接收器 this | 历史版本 |
| 3.10.5 | WorldQualitySettings、可写的 VRCQualitySettings、Assembly Version Defines、Pickup Outline Renderers、Pipeline Manager 验证 | 当前支持/最后验证 |
发布时请使用 SDK 3.10.5。依赖特定版本的 API 或迁移步骤前,请查看对应版本的发行说明。
官方资源
| 资源 | URL | 内容 |
|---|---|---|
| VRChat Creators | creators.vrchat.com/worlds/udon/ | 官方 Udon/SDK 文档 |
| UdonSharp 文档 | udonsharp.docs.vrchat.com | UdonSharp API 参考 |
| VRChat 论坛 | ask.vrchat.com | 问答与解决方案 |
| VRChat Canny | feedback.vrchat.com | 错误报告、已知问题 |
| GitHub | github.com/vrchat-community | 示例与库 |
参考资料
| 文件 | 内容 | 搜索提示 |
|---|---|---|
constraints.md | UdonSharp 中 C# 功能的可用性;受限功能;可同步类型;属性;DataList 与数组的选择指南;DataList/DataDictionary 容量 API;高级变通方案(object 数组伪结构体);可同步的 VRCUrl 列表 | List、async、try/catch、LINQ、generics、DataList、DataDictionary、DataList capacity、DataDictionary capacity、EnsureCapacity、DataList vs array、when to use DataList、VRCUrl array、VRCUrl sync、pseudo-struct、object array cast、multi-field state container |
networking.md | 所有权模型、同步模式、RequestSerialization、NetworkCallable、网络事件发送者授权、数据限制 | UdonSynced、SetOwner、BehaviourSyncMode、FieldChangeCallback、OnDeserialization、NetworkCalling、CallingPlayer、InNetworkCall、legacy event、underscore、authorization、master leave、ownership cascade |
networking-bandwidth.md | 带宽节流、位打包、同步数据大小示例、调试、以所有者为中心的架构 | IsClogged、bandwidth、throttle、bit packing、data budget、IsMaster |
networking-antipatterns.md | 应避免的 6 种反模式;附模板链接的 5 种高级同步模式 | anti-pattern、race condition、ownership fight、late-joiner、PackedStateSync、BatchedSync |
persistence.md | 存储层决策树(本地/同步/PlayerData/PlayerObject);PlayerData/PlayerObject API(在 SDK 3.7.4 中引入;当前目标版本为 3.10.5);每位玩家的保存数据;存储用量查询 API(在 SDK 3.10.0 中引入;当前目标版本为 3.10.5) | storage layer、decision tree、local variable、PlayerData、PlayerObject、OnPlayerRestored、SetInt、TryGetInt、GetPlayerDataStorageUsage、GetPlayerDataStorageLimit、GetPlayerObjectStorageUsage、GetPlayerObjectStorageLimit、RequestStorageUsageUpdate、OnPersistenceUsageUpdated、storage quota、storage usage、which storage、when to use PlayerData |
dynamics.md | PhysBones、Contacts、VRC Constraints(在 SDK 3.10.0 中引入;当前目标版本为 3.10.5);VRCTween、盒形 Contacts、全局 Avatar PhysBone 碰撞体、通过 Udon 访问 world VRCPhysBoneCollider(SDK 3.10.4+) | PhysBone、ContactReceiver、ContactSender、Box Contact、Global Avatar PhysBone Collider、VRCPhysBoneCollider、VRCTween、VRCConstraint、OnContactEnter |
patterns-core.md | 初始化、交互、玩家检测、计时器、音频、拾取、动画、UI、传送、延迟初始化保护、远程玩家 | Interact、OnEnable、Initialize、AudioSource、VRCPickup、Animator、UI、TeleportTo、remote players、_GetRemotePlayers、exclude local player、FindAll alternative |
patterns-networking.md | 对象池、NetworkCallable 发送者验证模式、持久化集成、动态交互、同步游戏状态、远距离房间伪多房间(状态/呈现分离、自有或由主玩家协调的会话仲裁)、延迟事件去抖、用于数组同步的字符串拼接 | pool、MasterManagedPlayerPool、NetworkCallable、CallingPlayer、InNetworkCall、sender authorization、DamageReceiver、game state、distant room、pseudo multi-room、room assignment、roomIndex、LocalRoomPresenter、RoomAssignment、NoVariableSync、TeleportTo per-client、debounce、state machine、string join、array sync、paragraph separator、U+2029 |
patterns-performance.md | partial class 模式、更新处理程序、PostLateUpdate、空间查询、平台优化、帧预算 Stopwatch、繁重处理架构(重建、重放、重置/取消)、速率限制解析器、GameObject 查找成本等级 | Update、PostLateUpdate、Bounds、AnimatorHash、performance、mobile、PC、Stopwatch、frame budget、SendCustomEventDelayedFrames、heavy processing、rebuild、replay、reset、cancel、operation log、authoritative data、derived state、cursor rebuild、rate limit、URL scheduler、video load queue、GameObject.Find、Find cost、lookup cost tier、SerializeField vs Find、silent failure on rename、SendCustomEvent cost、cross-behaviour call、EventBus hot path、delayed loop spike、public method lookup、event dispatch tier |
patterns-utilities.md | 数组辅助工具(List 替代方案)、事件总线、GameObject 中继通信、伪结构体双重转换、抽象类回调、可取消的延迟事件、重入保护、UdonEvent 伪委托 | ArrayUtils、EventBus、relay、subscriber、FindIndex、ShuffleArray、object array、pseudo struct、double cast、abstract class、callback、interface alternative、cancellable timer、re-entrance、emitting guard、UdonEvent、pseudo delegate |
patterns-ui.md | UI/Canvas 模式:防止移动保护、适配 Avatar 缩放的 UI、响应 FOV 的定位、适配平台的布局、动态玩家列表、滚动输入抽象、查找表本地化、Toggle-Animator 桥接、通过 PlayerObject 持久化设置、基于监听器的菜单事件、手指触摸交互、模块化应用架构 | Canvas、UI、menu、Immobilize、avatar scale、FOV、platform、Quest、VR、desktop、player list、scroll、localization、language、Toggle、Animator、PlayerObject、settings、persistence、listener、broadcast、finger touch、fingertip、haptic、FingerPointer、FingerTouchCanvas、touch canvas、app architecture、AppModule、AppManager、plugin lifecycle、CanvasGroup transition |
patterns-video.md | 视频播放器状态机、基于服务器时间的播放同步、新加入玩家同步、AVPro Blit 缓冲、出错后重试并回退、同步播放列表/队列、按平台选择 URL | video player、AVPro、VRCUnityVideoPlayer、BaseVRCVideoPlayer、playback sync、server time、GetServerTimeInMilliseconds、late joiner、VRCGraphics.Blit、OnVideoReady、OnVideoError、retry、fallback、playlist、queue、shuffle、repeat、Quest URL |
web-loading.md | 字符串/图片下载、VRCJson、受信任的 URL | VRCStringDownloader、VRCImageDownloader、VRCJson、DataDictionary、VRCUrl |
image-loading-vram.md | 图片加载的高级 VRAM 管理:Destroy 与 Dispose、双缓冲渐隐、stock 模式、mipmap 偏差 | VRAM、texture memory、memory leak、Destroy、Dispose、double buffer、fade、mipmap、TextureInfo |
web-loading-advanced.md | 高级数据加载:通过 StringDownloader 嵌入 Base64 纹理、跨平台压缩、URL 双键索引、LRU 解码缓存 | Base64、LoadRawTextureData、StringDownloader texture、DXT1、ETC_RGB4、UNITY_ANDROID、LRU cache、packed resources、binary format |
api.md | VRCPlayerApi、Networking、NetworkCalling 发送者上下文、枚举参考、VRCObjectPool 方法及由 Interact 驱动的所有权模式 | GetPlayers、playerId、isMaster、isLocal、GetPosition、SetVelocity、NetworkCalling、CallingPlayer、InNetworkCall、Drone、VRCDroneApi、VRCObjectPool、TryToSpawn、Return、Shuffle、pool owner、Interact pool、pool forwarded spawn、pool ownership transfer |
events.md | 所有 Udon 事件(包括 OnPlayerRestored、OnContactEnter) | OnPlayerJoined、OnPlayerLeft、OnPlayerTriggerEnter、OnOwnershipTransferred、OnControllerColliderHitPlayer、CharacterController、OnMasterTransferred、OnAvatarChanged、OnSpawn、VRC Economy、OnPurchaseConfirmed、OnAsyncGpuReadbackComplete |
editor-scripting.md | 编辑器脚本、代理系统、自定义检查器、仅限编辑器的设置组件(IEditorOnly)、构建流程回调,以及 UdonSharpProgramAsset 自动生成 | UdonSharpEditor、UdonSharpBehaviourProxy、SerializedObject、UdonSharpProgramAsset、auto-generate、AssetPostprocessor、.asset missing、IEditorOnly、EditorOnly tag、setup helper、setup component、build exclusion、custom inspector、ContextMenu、IVRCSDKBuildRequestedCallback、OnBuildRequested、build callback、IPreprocessCallbackBehaviour、OnPreprocess |
assembly-definitions.md | UdonSharp 程序集定义、Unity .asmdef 与 U# Assembly Definition、VPM 包工作流、Runtime/Editor 分离,以及 Auto Referenced 的权衡 | Version Defines、VRC_ENABLE_、asmdef、Assembly Definition、U# Assembly Definition、Source Assembly、VPM package、Auto Referenced、Runtime、Editor、package layout、prefab-first、code-integration API |
sync-examples.md | 同步模式示例(Local/Events/SyncedVars) | Continuous、Manual、NoVariableSync、sync example |
troubleshooting.md | 常见错误及解决方案 | NullReference、compile error、sync not working、FieldChangeCallback、VRCStation、seated player、trigger zone、OnPlayerTriggerEnter not firing、station collider、position polling、OnStationEntered |
sdk-migration.md | SDK 迁移指南(3.7 至 3.10)、逐版本变更与检查清单 | migration、deprecated、upgrade、3.7、3.8、3.9、3.10 |
testing.md | 测试与调试指南:ClientSim 编辑器测试、Build and Test(单客户端和多客户端)、Debug.Log 模式、发布前清理、测试清单 | ClientSim、Build and Test、multi-client、late joiner test、debug、Debug.Log、ownership test、sync test、testing checklist |
context-preservation.md | 上下文保留指南:在上下文压缩/交接期间记录特定任务的设计意图(真实来源、同步策略、所有权、新加入玩家的行为、验证);最简任务上下文说明;隐私指南;恢复工作检查清单 | context preservation、design intent、compaction、handoff、resume、task context note、why this design、ownership rationale、design-context loss |
模板(assets/templates/)
| 模板 | 用途 |
|---|---|
BasicInteraction.cs | 带有 Interact() 处理程序的可交互对象 |
SyncedObject.cs | 网络同步对象(手动同步、所有权检查、晚加入者初始化标记) |
PlayerSettings.cs | 每位玩家的移动设置(行走/奔跑/跳跃速度) |
StateMachine.cs | 带有同步状态和状态转换的状态机 |
DataPersistence.cs | PlayerData 保存/加载及 OnPlayerRestored(在 SDK 3.7.4 中引入;当前目标版本为 3.10.5) |
ContactReceiver.cs | 用于检测世界侧碰撞的接触接收器(在 SDK 3.10.0 中引入;当前目标版本为 3.10.5) |
CustomInspector.cs | 使用 UdonSharpEditor 的自定义编辑器检查器 |
MasterManagedPlayerPool.cs | 用于非安全性会话仲裁的主控玩家对象池;FIFO 环形缓冲区;OnPlayerJoined/Left;主控权移交后执行 _VerifyAssignments |
EventBus.cs | 订阅者列表事件总线(最多 32 个监听器);RegisterListener/UnregisterListener/RaiseEvent;原地压缩 |
ArrayUtils.cs | List<T> 的替代方法:针对 GameObject[] 的 Add、Contains、AddUnique、Remove、RemoveAt、Insert;针对 int[] 的 FindIndex/ShuffleArray |
UndoableGameManager.cs | 使用 byte[] 状态历史记录进行历史/撤销同步;NetworkCallable _OwnerProcessMove/_OwnerUndo/_OwnerReset |
PackedStateSync.cs | 将 3 个整数打包到一个 Vector3 UdonSynced 字段中;OnPreSerialization/OnDeserialization |
RateLimitedSync.cs | 0.15 秒同步冷却时间,使用 _syncLocked/_changeCounter;_OnSyncUnlock 回调 |
DualCopySync.cs | 本地副本与同步副本,带有 _dirty 标记;严格区分 OnPreSerialization/OnDeserialization |
BatchedSync.cs | 幂等的 ScheduleBatchedSync,BatchDelay 为 0.2 秒;_FlushBatch 延迟回调 |
CloggedRetrySync.cs | 检查 Networking.IsClogged;线性退避(RetryDelay * retryCount);MaxRetries=5 |
UdonSharpProgramAssetAutoGenerator.cs | 为新脚本自动创建 UdonSharpProgramAsset 的 AssetPostprocessor |
钩子
| 钩子 | 平台 | 用途 |
|---|---|---|
validate-udonsharp.ps1 | Windows(PowerShell) | PostToolUse 约束验证 |
validate-udonsharp.sh | Linux/macOS(Bash) | PostToolUse 约束验证 |
Bash 验证器需要 jq。如果该工具不可用,钩子会原样传递输入,并输出
VALIDATOR-WARNING: validation skipped (JQ_UNAVAILABLE),而不是悄悄地报告验证成功。
快速参考
CHEATSHEET.md- 单页快速参考