英文原文:

https://mirror-networking.gitbook.io/docs/guides/synchronization

  状态同步是指对属于脚本的整数、浮点数、字符串和布尔值等值进行同步。

  状态同步是从服务器到远程客户端完成的。本地客户端没有序列化的数据。它不需要它,因为它与服务器共享场景。但是,在本地客户端上调用 SyncVar 挂钩。

  数据不会以相反的方向同步 - 从远程客户端到服务器。为此,您需要使用命令。

  • SyncVars
    SyncVars 是继承自 NetworkBehaviour 的脚本变量,从服务器同步到客户端。

  • SyncEvents (Obsolete)
    SyncEvents 是类似于 ClientRpc 的网络事件,但它们不是在游戏对象上调用函数,而是触发事件。重要提示:在版本 18.0.0 中已删除,有关详细信息,请参阅此问题。

  • SyncLists
    SyncLists 包含值列表并将数据从服务器同步到客户端。

  • SyncDictionary
    SyncDictionary 是一个关联数组,其中包含键值对的无序列表。

  • SyncHashSet
    一组不重复的无序值。

  • SyncSortedSet
    一组排序的不重复的值。


同步到所有者

  当您不希望其他玩家看到某些玩家数据时,通常会出现这种情况。在检查器中,将“网络同步模式”从“观察者”(默认)更改为“所有者”,让 Mirror 知道仅与拥有的客户端同步数据。

  例如,假设您正在制作库存系统。假设玩家 A、B 和 C 在同一区域。全网共有12个对象:

  • 客户端 A 有玩家 A(他自己)、玩家 B 和玩家 C
  • 客户端 B 有玩家 A、玩家 B(他自己)和玩家 C
  • 客户端 C 有玩家 A、玩家 B 和玩家 C(他自己)
  • 服务器有玩家 A、玩家 B、玩家 C

他们每个人都有一个库存组件

  假设玩家 A 捡到了一些战利品。服务器将战利品添加到玩家的 A 库存中,该库存将有一个 SyncLists of Items。

  默认情况下,Mirror 现在必须在各处同步玩家 A 的库存,这意味着向客户端 A、客户端 B 和客户端 C 发送更新消息,因为他们都有一个玩家 A 的副本。这很浪费,客户端 B 和客户端 C 没有需要了解玩家 A 的库存,他们永远不会在屏幕上看到它。这也是一个安全问题,有人可以破解客户端并显示其他人的库存并利用它为自己谋利。

  如果您将 Inventory 组件中的“Network Sync Mode”设置为“Owner”,则 Player A 的库存将仅与 Client A 同步。

  现在,假设一个地区有 50 个人而不是 3 个人,其中一个人捡起战利品。这意味着您只需发送 1 条消息,而不是向 50 个不同的客户端发送 50 条消息。这会对您的游戏中的带宽产生很大影响。

  其他典型用例包括任务、纸牌游戏中玩家的手牌、技能、经验或您不需要与其他玩家共享的任何其他数据。


高级状态同步

  在大多数情况下,使用 SyncVars 足以让您的游戏脚本将其状态序列化给客户端。但是在某些情况下,您可能需要更复杂的序列化代码。此页面仅适用于需要超出 Mirror 正常 SyncVar 功能的自定义同步解决方案的高级开发人员。


自定义序列化函数

  要执行您自己的自定义序列化,您可以在 NetworkBehaviour 上实现虚拟函数以用于 SyncVar 序列化。这些函数是:

public virtual bool OnSerialize(NetworkWriter writer, bool initialState);
public virtual void OnDeserialize(NetworkReader reader, bool initialState);

  使用 initialState 标志来区分第一次序列化游戏对象和何时可以发送增量更新。第一次将游戏对象发送到客户端时,它必须包含完整状态快照,但后续更新可以通过仅包含增量更改来节省带宽。

  OnSerialize 函数应返回 true 以指示应发送更新。如果它返回 true,则该脚本的脏位设置为零。如果它返回 false,则不更改脏位。这允许对脚本的多次更改随着时间的推移累积并在系统准备好时发送,而不是每一帧。

  OnSerialize 函数仅在 initialState 或 NetworkBehaviour 脏时调用。仅当 SyncVar 或 SyncObject(例如 SyncList)自上次 OnSerialize 调用以来发生更改时,NetworkBehaviour 才会是脏的。发送数据后,NetworkBehaviour 将不会再次变脏,直到下一个 syncInterval(在检查器中设置)。也可以通过手动调用 SetDirtyBit 将 NetworkBehaviour 标记为脏(这不会绕过 syncInterval 限制)。

  虽然这可行,但通常最好让 Mirror 生成这些方法并为您的特定字段提供自定义序列化程序。


序列化流程

  附加了网络身份组件的游戏对象可以有多个从 NetworkBehaviour 派生的脚本。序列化这些游戏对象的流程是:

在服务端上:

  • 每个 NetworkBehaviour 都有一个脏掩码。此掩码在 OnSerialize 中作为 syncVarDirtyBits 使用
  • NetworkBehaviour 脚本中的每个 SyncVar 都在脏掩码中分配了一个位。
  • 更改 SyncVars 的值会导致在脏掩码中设置该 SyncVar 的位
  • 或者,调用 SetDirtyBit 直接写入脏掩码
  • 作为update循环的一部分,在服务端上检查 NetworkIdentity 游戏对象
  • 如果 NetworkIdentity 上的任何 NetworkBehaviours 是脏的,则为该游戏对象创建一个 UpdateVars 数据包
  • 通过在游戏对象上的每个 NetworkBehaviour 上调用 OnSerialize 来填充 UpdateVars 数据包
  • 不脏的网络行为在数据包中的脏位写一个零。
  • 脏的 NetworkBehaviours 写入其脏掩码,然后写入已更改的 SyncVars 的值
  • 如果 OnSerialize 为 NetworkBehaviour 返回 true,则为该 NetworkBehaviour 重置脏掩码,因此在其值更改之前不会再次发送。
  • UpdateVars数据包被发送给正在观察游戏对象的准备好的客户端

在客户端:

  • 收到游戏对象的 UpdateVars 数据包
  • 为游戏对象上的每个 NetworkBehaviour 脚本调用 OnDeserialize 函数
  • 游戏对象上的每个 NetworkBehaviour 脚本都会读取一个脏掩码。
  • 如果 NetworkBehaviour 的脏掩码为零,则 OnDeserialize 函数返回而不读取任何内容
  • 如果脏掩码是非零值,则 OnDeserialize 函数读取与设置的脏位对应的 SyncVars 的值
  • 如果有 SyncVar 挂钩函数,则使用从流中读取的值调用这些函数。

所以对于这个脚本:

public class data : NetworkBehaviour
{[SyncVar(hook = nameof(OnInt1Changed))]public int int1 = 66;[SyncVar]public int int2 = 23487;[SyncVar]public string MyString = "Example string";void OnInt1Changed(int oldValue, int newValue){// do something here}
}

  以下示例显示了 Mirror 为在 NetworkBehaviour.OnSerialize 中调用的 SerializeSyncVars 函数生成的代码:

public override bool SerializeSyncVars(NetworkWriter writer, bool initialState)
{// 在基类中写入任何 SyncVarsbool written = base.SerializeSyncVars(writer, forceAll);if (initialState){// 第一次将游戏对象发送到客户端时,发送所有数据(并且没有脏位)writer.WritePackedUInt32((uint)this.int1);writer.WritePackedUInt32((uint)this.int2);writer.Write(this.MyString);return true;}else {// 写入已更改的 SyncVarswriter.WritePackedUInt64(base.syncVarDirtyBits);if ((base.get_syncVarDirtyBits() & 1u) != 0u){writer.WritePackedUInt32((uint)this.int1);written = true;}if ((base.get_syncVarDirtyBits() & 2u) != 0u){writer.WritePackedUInt32((uint)this.int2);written = true;  }if ((base.get_syncVarDirtyBits() & 4u) != 0u){writer.Write(this.MyString);written = true;     }return written;}
}

  以下示例显示了 Mirror 为在 NetworkBehaviour.OnDeserialize 中调用的 DeserializeSyncVars 函数生成的代码:

public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
{// 读取基类中的任何 SyncVarsbase.DeserializeSyncVars(reader, initialState);if (initialState){// 第一次将游戏对象发送到客户端时,读取所有数据(并且没有脏位)int oldInt1 = this.int1;this.int1 = (int)reader.ReadPackedUInt32();// 如果新旧值不相等,则调用 hookif (!base.SyncVarEqual(num, ref this.int1)){this.OnInt1Changed(num, this.int1);}this.int2 = (int)reader.ReadPackedUInt32();this.MyString = reader.ReadString();return;}int dirtySyncVars = (int)reader.ReadPackedUInt32();// 是第一个 SyncVar 脏if ((dirtySyncVars & 1) != 0){int oldInt1 = this.int1;this.int1 = (int)reader.ReadPackedUInt32();//如果新旧值不相等,则调用 hookif (!base.SyncVarEqual(num, ref this.int1)){this.OnInt1Changed(num, this.int1);}}// 第二个 SyncVar 脏了if ((dirtySyncVars & 2) != 0){this.int2 = (int)reader.ReadPackedUInt32();}// 第三个 SyncVar 脏了if ((dirtySyncVars & 4) != 0){this.MyString = reader.ReadString();}
}

  如果 NetworkBehaviour 的基类也具有序列化函数,则还应调用基类函数。

  请注意,为游戏对象状态更新创建的 UpdateVar 数据包在发送到客户端之前可能会在缓冲区中聚合,因此单个传输层数据包可能包含多个游戏对象的更新。

[Unity Mirror] 同步相关推荐

  1. Unity+Mirror实现虚拟现实下的多人连接

    实现虚拟现实环境中的多人连接顾名思义分为两步,首先通过Mirror插件实现3D空间下的多人连接基础功能,其后调整虚拟现实相关内容,最后添加虚拟化身并进行匹配.本篇文章也将从以下三个方面依次完成(使用设 ...

  2. unity Mirror使用心得一(玩家角色创建,控制,及其攻击其他玩家的血量同步设置)

    先分享下个人mirrordemo 的github : https://github.com/IsaWinding/MirrorDemo.git mirror 的官方下载地址: https://asse ...

  3. unity Mirror使用笔记

    由于mirror的概念和API同已经弃用的unet很相似.同时mirror的文档在基础概念比较少,而且全英文.所以可以先从unet的官方文档开始入门.UNet mirror入门 1) 运行环境: 从A ...

  4. [Unity Mirror] FAQ

    如何发送/同步自定义数据类型?   Mirror 可以在编译脚本时自动为许多自定义数据类型创建序列化函数.   例如,mirror 会自动为 MyCustomStruct 创建一个函数,以便无需任何额 ...

  5. Unity + Mirror实现原创卡牌游戏局域网联机

    资源下载地址 局域网联机插件 Mirror:Mirror | 网络 | Unity Asset Store 本地客户端测试多人游戏(不用打包)插件 : ParrelSync Mirror官方文档:Ge ...

  6. [Unity Mirror] 作弊与反作弊

    简介   早在 2009-2015 年,在从事 Mirror 和 uMMORPG 工作之前,我尝试通过对 MMO 进行逆向工程和出售Bots来谋生来了解 MMO.我将根据我们 Discord 中的问题 ...

  7. [Unity Mirror] General

      Mirror 是一个为 Unity 游戏构建多人游戏功能的系统.它建立在较低级别的传输实时通信层之上,并处理多人游戏所需的许多常见任务.传输层支持任何类型的网络拓扑,而 Mirror 是服务器权威 ...

  8. [Unity Mirror] 自定义角色生成

    英文原文: https://mirror-networking.gitbook.io/docs/guides/gameobjects/custom-character-spawning   许多游戏需 ...

  9. Unity帧同步和状态同步

    帧同步 适用游戏类型 对于延迟要求较高的游戏,例如:FPS游戏, RTS游戏(即时战略游戏)等. 原理 帧同步不同步状态,只同步玩家的操作指令,操作指令包含当前的帧索引.这里最重要的概念就是 相同的输 ...

最新文章

  1. 财务大数据比赛有python吗-Python大数据与机器学习之NumPy初体验
  2. 非归档模式下重做日志覆盖后的rman恢复
  3. No Fine-Tuning, Only Prefix-Tuning
  4. Java堆排序递归_大顶堆第二弹----堆排序(递归实现)
  5. java实现蛇形输出,Java 输入一个正整数,按蛇形打印。
  6. ANT安装与测试和简明教程
  7. 单列(写了池子pool)用list实现的方法, 与伪单例(写了池子zidianpool),用字典实现的方法,可以存入不同,i名字的物体...
  8. 100天搞定机器学习|Day4-6 逻辑回归
  9. bjui 公共的弹窗确认 取消 改变文字
  10. 半导体器件制造封装材料和生产工艺流程(图文介绍)
  11. 什么是数据运营?数据运营是做什么的?
  12. LTE CAT M1模块为辅助生活系统提供了宽带和智能手机的替代方案
  13. buuctf|ciscn_2019_en_2 1
  14. 【排序算法】基数排序:LSD 与 MSD
  15. 对ABAP程序调优的学习(一)select 改 read table
  16. python简单圣诞树手工折纸_简单手工折纸圣诞树折纸大全图解教程
  17. javascript经典题型:根据用户输入的年份判断是否为闰平年
  18. 2022年全球市场薄膜厚度测量系统总体规模、主要生产商、主要地区、产品和应用细分研究报告
  19. 斗地主洗牌+发牌+排序
  20. Windows程序意外挂掉,但显存依然被占用

热门文章

  1. 点击事件及按钮事件。
  2. AI生成小说 开源项目
  3. VC2010中显示行号
  4. Vue - 解决部署到服务器后Element UI图标不显示问题(50错误)
  5. android 8 平板,不给安卓平板活路?iPad 8正式发布
  6. java怎么找出数组最小值,Java找出数组的最小值元素
  7. 终于有人把口罩说清楚了。
  8. 我的世界起床战争php,教你如何玩转我的世界Hypixel起床战争
  9. 图之邻接表详解(C语言版)
  10. Unity Multiple_Graph_And_Chart_UI_Pack 可视化UI插件