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()事件。
   也就是说:但如果你在脚本中Override了OnDeserialization()事件,如果你希望所有人(包括主机)的表现一致。你应该在同步之后在主机手动触发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:事件同步很好用,非常好用,而且对网络没有任何负担(相比于其他)。

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