Skip to main content

在你正式开始网络同步之前——你最好知道的一些事

本文是我【ARK-Magellan】在学习网络同步开发过程中,对遇到的各类问题及解决方案的整理记录。内容整合了日本及其他技术网站的知识,虽非原创但经过系统性梳理——这些知识点在网络同步开发中至关重要,希望你能认真阅读,并在实际开发中遵守相关核心原则。

一切的开始

你必须要保证你已经会写本地的逻辑了再来考虑学习网络同步——但是在你从创建脚本开始,就应该考虑号网络同步的问题。

因为将一个网络同步变成本地脚本,只需要将同步状态改为 None,但是把一个本地脚本变成网络同步脚本,无异于重新写一遍……

很好你已经准备好了

你需要知道的是

网络同步本身是一个复杂且易出问题的系统:

  • Manual 同步的 OnDeserialization() 发生时不一定所有的变量已经完成了同步,VRCSDK 有概率把你的同步分布两帧甚至更多,这取决于你的脚本的网络同步变量大小……
  • Continuous 同步模式会因为丢包导致不同步。
  • 使用 NoVariableSync 同步模式的事件可能不可达。

等等等等……

所以!

在设计网络同步的时候,我十分建议你注意以下的事情:

1、不要太多使用Continuous同步模式!

为什么要特地提这个呢?因为真的有很多人在滥用连续同步的连续,甚至为了使用连续同步把一些逻辑写进了 Update 里面。连续同步的更新速度很慢,做不到一秒一次,而且连续同步不会触发 OnDeserialization,你无法得知你的变量什么时候同步了。

而且连续同步是有问题的,他并不保证变量最后是同步的,如果中途丢包了——他并不会重新同步。

但是手动同步是会保证抵达的,所以请不要太多使用 Continuous 同步模式!(或者说丢包概率更低,但并不是没有)

如果你执意使用,Udon 其实可以对一个【同步变量】(注意,这个要求这个变量必须是网络同步变量)使用【FieldChangeCallback】
去做一个监控事件,在其被读取或者被修改的时候触发指定的逻辑。

他的写法如下:
  
[UdonSynced, FieldChangeCallback(nameof(SyncedVariable))] private int _syncedVariable;

public int SyncedVariable
{
    get => _syncedToggle;
    set
    {
        _syncedVariable = value;
        //你可以在下面调用一个函数,或者任何你想干的事情
    }
}
实际使用方法:
  1. 将 FieldChangeCallback 属性应用到应用在变量上,并且指定 CallBack 的函数
  2. 写出对应指定 CallBack 的函数
  3. 在你想应用的变量的 FieldChangeCallback 属性参数参数中设置第二个属性的名称
冷知识,你可以通过 None 同步方法让你的本地脚本也支持这个功能。

2、在你使用 Manual 同步时,请尽量压缩你的同步变量,哪怕你觉得其实不是很大

因为当你拥有多个同步变量的时候,OnDeserialization 会在网络完成同步后触发,但是不代表所有的变量都完成了同步,所以你有责任压缩每一个脚本的同步变量大小,使他们可以在一帧中完全同步。否则我建议你为每个变量都建立监控事件(因为你的 OnDeserialization 事件已经变得不可靠了)

3、OnDeserialization() 可以让你的网络同步变得简单,你知道像如下的方法来做……

当你的主机进行了同步请求之后,你可以手动触发 OnDeserialization() 事件。

也就是说:但如果你在脚本中 OverrideOnDeserialization() 事件,如果你希望所有人(包括主机)的表现一致。你应该在同步之后在主机手动触发 OnDeserialization(),并且把所有的和这些参数有关的逻辑写在 OnDeserialization() 里面

[udonsync]public int Variable;

public void SetsyncedVariable()
  {
  Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
  RequestSerialization();
  OnDeserialization()
  }

public override void OnDeserialization()
  {
       //网络同步后的逻辑
  }

4、请务必提前使用U#的 [UdonBehaviourSyncMode()] 属性把你的同步模式写死在脚本里!

using UdonSharp;
using UnityEngine;

[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] 
public class Udon123 : UdonSharpBehaviour
{
  // ...
}


5、针对一些需要同步但不需要全面同步(手动同步)参数。你应该使用VRC最新的带参同步。

这个问题比较简单,这里直接教你该怎么写:

[NetworkCallable]
public void Network(int A , int B)
  {
  //AABBCC
  }

请注意:使用这个你需要占用上传带宽。我不保证在 NoVariableSync 同步模式这个能正常运行。

6、有些人可能会教你使用 NoVariableSync 同步模式,但是答应我,请不要用他

这个同步模式有很都很奇怪的Bug,请答应我只使用VRCSDK本来有的三个同步方式,手动(Manual),连续(Continuous),不同步(None)

7、请尽量不要将 String 变成同步变量。

原因:String 的性质导致了你不能确定 String 的大小——他很方便,也很浪费性能。

8:不要通过同步 DisplayName 来同步玩家信息!

你应该通过 GetPlayerCount 获得玩家的全局唯一 ID,然后把这个 ID 传递到其他玩家,并让他们使用 VRCPlayerApi.GetPlayerById() 方法来获取 PlayerAPI,再获取 Displayname

我知道你会有很多诸如:内存消耗,性能问题等等,但是我需要提到的是:

在网络同步面前,这一切都不应该放在首位,网络同步的稳定才是首位,你应该优先你的网络同步稳定,然后再是其他的问题。

9:事件同步很好用,非常好用,而且对网络没有任何负担(相比于其他)。

如果可以你应该多用用,真的。