网络组件
本文档涵盖可在 Udon 程序中使用的网络组件、属性和事件。
网络属性
可以从 Networking 获取的特殊属性:
| 属性 名称 | 描述 |
|---|---|
| LocalPlayer | 返回本地玩家 VRC Player API 对象。 |
| IsInstanceOwner | 在邀请、邀请+、好友和好友+实例中,为实例创建者返回 true。在群组实例、公共实例和 SDK 的"Build & Test"模式下始终返回 false。 |
| InstanceOwner | 返回拥有该实例的玩家的 VRC Player API 对象。如果拥有者当前不在实例中,则返回 null。如果拥有者返回,则再次返回实例拥有者。实例拥有者具有特殊的审核权限。实例所有权永远不会改变。 |
| IsMaster | 如果本地玩家是实例主控者,则返回 true。主控者是网络游戏对象的默认拥有者。不应将其用于安全或限制对世界的访问。应使用 IsInstanceOwner 或实现审核系统。 |
| Master | 返回当前实例主控者的 VRC Player API 对象。始终有效。 |
| IsNetworkSettled | 如果实例中的所有数据都已反序列化、应用并准备就绪,则返回 true。 |
| IsClogged | 如果有太多数据试图发出,则返回 true。你可以使用此方法等待直到网络畅通或调整逻辑。 |
| SimulationTime | 返回玩家或具有网络组件的对象的当前模拟时间。详见下文。 |
模拟时间
模拟时间是一个时间戳,指对 象在时间上被回溯模拟的程度。该值在内部用于 VRCObjectSync 和玩家,但也可以在 Udon 脚本中使用。例如,如果你的 Time.realtimeSinceStartup 是 45,而对象的 SimulationTime 是 44.5,则 VRChat 认为需要 500ms 的延迟来平滑复制该对象。你可以使用该数字来了解 VRCObjectSync 正在做什么,或创建自己的类似系统。例如,如果你执行 Time.realTimeSinceStartup - SimulationTime(player),则会准确告诉你该玩家此刻的延迟。
模拟时间会根据网络条件频繁调整,包括延迟、可靠性和接收数据包的频率等因素。调整的目标是尽可能接近实时以减少延迟,但留出足够的空间以防止卡顿。有多种因素可能导致卡顿,但一个例子是从拥有者接收到的数据包不足。
网络事件
以下是网络系统提供的用于控制数据同步方式的事件。
OnPreSerialization
此事件在序列化数据即将发送前触发,是设置要与其他玩家更新的同步变量的好时机。
OnDeserialization
当同步数据从字节转换回可用变量时触发 此事件。它不会告诉你哪些数据已更新,而是作为一个起点,更新所有监视同步变量的内容,或检查新数据与旧数据并做出特定更新。
OnDeserialization(DeserializationResult)
与 OnDeserialization 相同,但包含关于请求发送和接收时间的额外信息。
DeserializationResult
DeserializationResult 包含三个属性:
sendTime:此消息发送的时间(秒)。receiveTime:此消息接收的时间(秒)。isFromStorage:如果为 true,则包含的数据是从存储中恢复的,而不是从其他实时客户端接收的。
sendTime 和 receiveTime 都基于 VRChat 启动以来的时间(秒),从你的视角计算(参见 Time.realtimeSinceStartup)。这意味着,如果你想知道某个 Deserialization 是在多少秒前发送的,可以用 Time.realtimeSinceStartup - sendTime 计算。
请注意,每个用户的 Time.realtimeSinceStartup 都不同,因此一个玩家的 sendTime 将与另一个玩家的 sendTime 不同。因此,如果你想将特定的 sendTime 同步给其他玩家,需要减去你的 Time.realtimeSinceStartup 来计算偏移量。然后,当其他玩家接收到该偏移量时,他们可以将自己的 Time.realtimeSinceStartup 加回偏移量,以确定相对于自己时钟的绝对时间。
如果消息是在你启动 VRChat 之前由其他人发送的,SendTime 可能为负数。
此事件在尝试发送序列化数据之后立即触发。它返回一个 SerializationResult 结构体,包含 success 布尔值和 byteCount 整数,表示发送的字节数。
OnSpawn
此事件已弃用——如果你希望在对象从池中"Spawn"时执行某些操作,请使用常规的 OnEnabled 事件。
OnOwnershipRequest
当有人请求获取所有权时触发此事件。它包含请求者和被请求拥有者的 PlayerObject。要批准或拒绝更改,请在"Set Return Value"节点中设置布尔值。此逻辑在请求者和拥有者双方本地运行,因此请注意,双方逻辑不一致会导致不同步。这最可能表现为所有权转移被拥有者意外拒绝。
OnOwnershipTransferred
当对象的所有权发生变更时,此事件会对实例中的所有人触发,并包含新拥有者的 PlayerObject。
OnMasterTransferred
当实例主控者因前一实例主控者离开实例而发生变化时,此事件会对实例中的所有人触发。
它包含一个参数 newMaster,即成为主控者的玩家的 VRC Player API 对象。此参数始终有效。
对于第一个加入新实例的用户,此事件将在 OnPlayerJoined 之后触发,表明主控者状态从"无人"转移而来。
OnVariableChanged
这是一种特殊类型的事件,你可以为任何变量创建。在 Udon Graph 中,你可以通过按住 alt 键将变量拖入图表来创建。此事件会在变量发生变化时检测到,包括从其他玩家接收到同步变量时。
- 更改数组的内容不会触发更改,因为数组本身仍然是同一个。
- OnVariableChanged 在变量本身被写入时立即触发,这与 OnDeserialization 不同,后者在完成所有同步变量的写入后才触发。这意味着,如果你从一个同步变量使用 OnVariableChanged 并尝试获取另一个同步变量的内容,不能保证该变量已使用最新的同步数据进行了更新。
VRC Object Sync
此组件将自动同步其所在对象的 Transform(位置、旋转、缩放)和 Rigidbody(物理)。它有几种特殊的方法和属性供你访问:
FlagDiscontinuity
当你想要传送对象时触发此方法——你在此帧所做的更改将立即应用,无需平滑过渡。
Set/Get Gravity
启用重力时,此刚体受重力影响并会落向地面。通常,重力是刚体的一个属性。但是,当你使用 VRCObjectSync 时,此属性必须由 VRCObjectSync 组件控制。你可以使用这些函数来实现这一点。这实际上表现为一个同步变量,因此只有拥有者可以设置重力。
Set/Get Kinematic
启用运动学模式时,此刚体忽略力、碰撞和关节。通常,运动学是刚体的一个属性。但是,当你使用 VRCObjectSync 时,此属性必须由 VRCObjectSync 组件控制。你可以使用这些函数来实现这一点。这实际上表现为一个同步变量,因此只有拥有者可以设置运动学。
Respawn
将此对象传送回其起始位置和旋转,并移除其速度。 具体来说,它将 DiscontinuityHint 设置为 true,以使以下更改立即生效而非平滑过渡。然后:
- 将 transform.position 设置为初始位置
- 将 transform.rotation 设置为初始旋转
如果对象有刚体:
- 将 rigidbody.velocity 设置为 Vector3.zero
- 将 rigidbody.angularVelocity 设置为 Vector3.zero
- 将 rigidbody.position 设置为初始位置
- 将 rigidbody.rotation 设置为初始旋转
VRC Object Pool
VRC Object Pool 提供了一种管理游戏对象数组的轻量级方法。对象池将管理并同步其持有的每个对象的激活状态。
要使对象激活,对象池的拥有者触发 TryToSpawn 节点,该节点将返回已激活的对象,如果没有可用对象则返回空对象。对象池拥有者可以通过 Return 节点将对象返回到池中,并自动禁用该对象。
延迟加入的玩家将自动看到对象处于适当的激活或非激活状态。