携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情
仅同步给拥有者
在上面的例子里面,Player的Inventory更新后被同步给所有玩家,但是往往这类数据并不应该发送给其他玩家。首先只有拥有者的客户端需要同步的Inventory数据来刷新UI等操作,而其他玩家并不会看到该玩家的背包,所以传输Inventory数据给他们是一种浪费。另外这也有安全问题,Inventory数据应该是对于玩家私有的,如果这数据被同步到其他玩家,那么通过一个破解的客户端就可以看到其他玩家的Inventory。当然我们这儿讨论的只是一个例子,不排除有的游戏就是要大家互相能看到别人的Inventory,但是一般情况,对于玩家私有的数据都不需要同步给其他人,当玩家数量很多时,发送这些数据也会占用很多带宽。
设置方法
在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函数会被触发,参数带上从流中读取的值。