基础网络同步知识——VRChatNetworking到底是如何工作的?

同步模式:如何同步?


VRChat的同步分为4种同步模式 :”自动“,”手动变量同步“,”连续变量同步“,”事件同步“


连续同步(Continuous Variable):

连续同步是Udon两种参数同步模式的其中一种,VRChat会尝试在每一次变量同步帧中同步变量。但是:
1:连续同步不一定会每一次都同步,有可能会因为节省带宽等原因减少同步频率,或者不同步其中的某几次。
2:连续同步不保证每一次都会到达,当丢包发生时,连续同步不会尝试重新发送同步包。直到主机下一次发生变化。
3:连续同步保证所有玩家都能收到最新的同步结果(在不丢包的情况下)——包括后加入的玩家。
4:连续同步的带宽极低,整个游戏的连续同步带宽仅200字节。
5:连续同步仅支持最多126个字符的字符串/字符数组。
连续同步比较常用与对数据精度要求不高,数据量不高但是对即时性要求很高的场景。即使是连续同步同步延迟也大概率大于1second,所以需要搭配手写平滑函数来减少同步频率低带来的卡顿感。

在通常情况下,当一个脚本是连续同步的。你需要通过在Update中检查变量结果或者使用变量CallBack来处理同步逻辑。因为连续同步不会触发OnDesSerialization事件

连续同步会在Owner的变量发生改变后的下一帧尝试进行同步,发送数据包。

连续同步会很容易受到丢包影响,导致行为不一致。


手动同步(Manual Variable)

手动同步是Udon两种参数同步模式的其中一种,VRChat会立即序列化所有的同步变量,并且在下一次同步帧到来时将同步数据发送出去。
手动同步是最常用的同步模式,也是我最推荐的同步模式。它有如下的特点:
1:可控—手动同步的发送,打包,解包都有相对应的事件存在,你可以自定义同步的流程
2:可靠—手动同步相对于连续同步,没有VRChat的介入,不会因为各种各样的原因导致包被丢弃,可以认为是可靠的。
3:低速—虽然手动同步和连续同步共享了同一个网络同步通道,但是由于需要手动触发,等待网络帧,所以速度相对较低。
4:高带宽—连续同步的单次最高同步为49千字节只要低于这个值都可以在一个网络帧内发送,如果高于则会分两次进行同步。


在后续的设计模式中,我们也会大多数以手动同步为同步方案。

手动同步需要任意玩家触发RequestSerialization()事件,才会开始网络同步。

手动同步会首先触发OnPreSerialization()事件

然后脚本会序列化数据并发送网络包,然后在OnPostSerialization()事件返回发送结果。

其他玩家接收到网络包会立即开始解包更新变量,并且在完成所有更新后触发OnDesSerialization()

OnDesSerialization()允许玩家在代码中直接调用,设计的时候经常会在Rquest后直接触发OnDesSerialization来保证行为一致。


特殊同步:事件

事件同步是Udon的一个独立的同步模式,其允许脚本使用SendCustomNetworkingEvent()方法来触发自身以及远端玩家(新版本可以选择4种不同目标,分别是Owner,ALL,Others,Self)的事件。在3-8-1版本,VRChat官方为事件系统提供了带参网络事件,使得事件同步已经成为了一个完整的同步方案。


事件同步有以下特点:
1:可靠—事件同步通过TCP协议进行同步,保证事件一定抵达。
2:缓慢—事件同步需要等待网络事件帧到来才会开始同步。
3:即时—只有在发送的那一刻存在在房间内的玩家才会收到事件同步。后续到来的玩家不会收到同步。
事件同步时独立于Udon参数同步的同步方式,可以与手动同步和连续同步一同使用。


特殊同步:自动

自动同步是VRChat最底层的同步模式,自动同步的每一个改变都会立即被同步,同步速度仅取决于网络延迟。


这个是VRChat最快的同步模式,但是只有两个可以享受这种待遇
Avatars :包括其碰撞体,音效和 IK 动作 (译者注:就是骨骼的位置)
VRCObjectSync:包括物体的变换组件(Transform) 和刚体组件 (Rigidbody)内的所有信息,不包括其他组件(不包括Udon)

而且VRCObjectSync内部自带平滑函数,使得同步看上去十分流畅快速,这点从带有SYNC的PickUp脚本中可以体现。在实践中,Sync脚本大概率会和PickUp,刚体这两个组件一同出现。

(当然,也有时候会使用Sync的特点。将Sync挂载的物体的Transform值作为9float的同步变量,为手动同步或者没有同步的脚本提供连续快速同步的功能。)

需要注意:VRCObjectSync要求与其在同一物体上的Udon组件必须要以Continuous(连续同步)模式运行。


所有者机制:谁来同步?

在VRChat中,每一个网络组件都需要一个玩家作为主机,来进行同步操作。在VRChat中,我们称这个人为Owner。

 

在连续同步中,Owner会在自身变量发生改变的时候,将变量同步给其他玩家。

在手动同步中,无论是Owner,还是其他玩家触发RequestSerialization()事件。都会由Owner执行打包数据并且发送给其他玩家。

在事件同步中,玩家只能选择给"全部玩家“或者给”Owner“发送事件。

————

对于特殊的机制,如PlayerData,PlayerObject,本质都是一个”不能被转移Owner“的脚本。每一名玩家进入游戏的时候生成一个Owner固定为这个玩家的脚本来实现的。VRChat也允许我们使用OnOwnershipRequest()来控制是否允许Owner转移。

————

我们可以通过SetOwner()来请求Owner转移。如果我们没有在代码中定义OnOwnershipRequest()事件,那么这SetOwner()将会被放行。

流程如下

1:B玩家为Owner,

2.A玩家SetOwner发送请求,停止帧生成等待Owner转移。

3.B玩家接收到请求,调用OnOwnershipRequest()。

4.OnOwnershipRequest()返回True,B玩家向所有玩家发送OnOwnershipTransferred()事件。

5.A玩家继续帧生成。

如果OnOwnershipRequest()没有定义,则A玩家会直接发送OnOwnershipTransferred()事件。

只有当所有玩家都受到了OnOwnershipTransferred()之后,A玩家才会继续执行下一个步骤。

如果B玩家拒绝了这次更改,则A玩家会继续执行剩下的部分并且在OnOwnershipTransferred()得知自己的请求被拒绝


持久化机制:如何存储?

VRChat为我们提供了两种不同的持久化方法

1:将某个脚本定义为持久化脚本,该脚本的所有同步参数将会变成持久化参数。在每一次进入实例时会自动回复。

2:Udon提供了一个键值对数据库,存入数据库的值将会被持久化。
所有的持久化数据都要求是网络数据,只有当数据更新成功,持久化才会正常运作。
两种持久化方法的优点和缺点都十分明显

1:优点在于本身就是脚本,搭配PlayerObject可以实现多种不同玩法。而且持久化不需要写过多的代码,丝滑接入。
2:优点在于其本身可以被任何脚本在任何地方访问,调用,设置,并且设置即同步,在多脚本协作时效果很好。



Revision #7
Created 8 March 2026 15:28:11 by Ark-Magellan
Updated 13 March 2026 14:50:52 by Ark-Magellan