Unity Mirror联网游戏开发(8) 同步篇三

649 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情

仅同步给拥有者

在上面的例子里面,Player的Inventory更新后被同步给所有玩家,但是往往这类数据并不应该发送给其他玩家。首先只有拥有者的客户端需要同步的Inventory数据来刷新UI等操作,而其他玩家并不会看到该玩家的背包,所以传输Inventory数据给他们是一种浪费。另外这也有安全问题,Inventory数据应该是对于玩家私有的,如果这数据被同步到其他玩家,那么通过一个破解的客户端就可以看到其他玩家的Inventory。当然我们这儿讨论的只是一个例子,不排除有的游戏就是要大家互相能看到别人的Inventory,但是一般情况,对于玩家私有的数据都不需要同步给其他人,当玩家数量很多时,发送这些数据也会占用很多带宽。

设置方法

image.png

在Inspector中,将Network Sync Mode从默认的Observers改成Owner

高级状态同步

尽管大部分情况下SyncVars已经足够使用了,你还是可以使用自定义序列化函数功能来实现自定义的状态同步。

自定义序列化函数

在NetworkBehaviour中,可以实现虚函数来实现变量同步,这些虚函数是:

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

其中,initialState表示这是game object第一次序列化并发送给客户端,此时需要传输当前完整的状态;如果initialState为False,则表示是之后的更新,可以使用增量更新。 OnSerialize返回true表示此次更新数据需要发送。Mirror会将该脚本的dirty bits设置为0。如果OnSerialize返回false则dirty bits不改变。这允许该脚本的多次修改被累积,然后等系统准备好了一起发送,而不是每帧发送。另外关于NetworkBehaviour的dirty bits,当SyncVar或SyncList从上次OnSerialize调用以来改变了才算dirty。数据发送后,NetworkBehaviour等待syncInterval之后才会dirty,或者使用SetDirtyBit手动设置为dirty。

序列化流程

附加了Network Identity组件的game objects可以附加多个NetworkBehaviour脚本,序列化这些game objects的流程如下:

在服务器端

  • 每个 NetworkBehaviour有一个dirty mask. 该 mask 在 OnSerialize 内部可通过 syncVarDirtyBits 访问。
  • NetworkBehaviour中的每个SyncVar在dirty mask中被赋予一个bit。
  • 修改SyncVars的值导致dirty mask中的SyncVar对应的bit被设置。
  • 或者可使用 SetDirtyBit手动设置drity mask。
  • 在服务器的update循环中,会检查 NetworkIdentity game objects。
  • 如果 NetworkIdentity game object上的任何NetworkBehaviour是dirty的,则会为该game object创建一个 UpdateVars包。
  • UpdateVars包通过调用game object上的每个NetworkBehaviour的OnSerialize方法填充。
  • 没有dirty的NetworkBehaviour会在OnSerialize中将UpdateVars中他们对应的dirty bits写入0。
  • dirty的NetworkBehaviour会写入他们的dirty mask
  • 如果某个NetworkBehaviour的OnSerialize返回true,则它的dirty mask会被重置,因此他的数据不会再次被发送直到数据值改变。
  • 最终UpdateVars包会被发送给所有观察该game object的客户端。

在客户端

  • 针对一个game object的UpdateVars包被接收到。
  • 该game object上的所有Network Behaviour的OnDeserialize函数被调用。
  • 每个NetworkBehaviour脚本读取dirty mask。
  • 如果该NetworkBehaviour的dirty mask为0,则 OnDeserialize函数不会再读取任何数据直接返回。
  • 如果dirty mask不是0,OnDeserialize函数为dirty mask标记的SyncVars读取值(更新SyncVar)
  • 如果有SyncVar hook函数,hook函数会被触发,参数带上从流中读取的值。