支持的同步数据类型
1.数据状态的同步方向是 Server to Client。 支持的数据类型分别是:
从 Client to Server 的同步需要使用 Command。
2.声明了同步变量的脚本类需要继承 NetworkBehaviour。同一个物体(NetworkIdentity标记)可以同时挂载多个 NetworkBehaviour 脚本。
3.NetworkBehaviour 的子类的同步变量支持继承。
4.单个 NetworkBehaviour 的同步变量的 dirty 标志位用 ulong 表示,因此单个脚本能支持最多 64个同步变量。
如果多层继承的话,已声明的sync变量数量也应该包括被继承类声明的 sync变量。
同步的目标
1.通常同步变量变化的时候,所有客户端都能接收到广播通知。但是有些情况是只需要向变量的拥有者同步的,比如玩家的背包数据,从安全和性能考虑,这个时候只需要向背包的拥有者同步就可以了。
将 Sync Mode 从 Observers 改为 Ower。
自定义同步行为
1.Mirror 提供了定制同步行为的能力,这是通过修改序列化和反序列化同步数据的行为实现的。
public virtual bool OnSerialize(NetworkWriter writer, bool initialState);
public virtual void OnDeserialize(NetworkReader reader, bool initialState);
同步分为全量同步行为和增量同步行为。initialState 为 true 的时候,属于全量同步;否则,
序列化和反序列化 只需要包含变化的数据就可以了。
OnSerialize 只有在 Sync 变量变化的时候,才会被调用。调用的间隔由 Sync Interval决定。
序列化和反序列化的细节
1.服务器端调用 OnSerialize 简化示意图:
----- 服务器端调用 OnDeSerialize 简化示意图:
2.比如下面的脚本拥有同步变量:
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
}
}
— 序列化过程:
public override bool SerializeSyncVars(NetworkWriter writer, bool initialState)
{
// Write any SyncVars in base class
bool written = base.SerializeSyncVars(writer, forceAll);
if (initialState)
{
// The first time a game object is sent to a client, send all the data (and no dirty bits)
writer.WritePackedUInt32((uint)this.int1);
writer.WritePackedUInt32((uint)this.int2);
writer.Write(this.MyString);
return true;
}
else
{
// Writes which SyncVars have changed
writer.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;
}
}
— 反序列化过程:
public override void DeserializeSyncVars(NetworkReader reader, bool initialState)
{
// Read any SyncVars in base class
base.DeserializeSyncVars(reader, initialState);
if (initialState)
{
// The first time a game object is sent to a client, read all the data (and no dirty bits)
int oldInt1 = this.int1;
this.int1 = (int)reader.ReadPackedUInt32();
// if old and new values are not equal, call hook
if (!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();
// is 1st SyncVar dirty
if ((dirtySyncVars & 1) != 0)
{
int oldInt1 = this.int1;
this.int1 = (int)reader.ReadPackedUInt32();
// if old and new values are not equal, call hook
if (!base.SyncVarEqual(num, ref this.int1))
{
this.OnInt1Changed(num, this.int1);
}
}
// is 2nd SyncVar dirty
if ((dirtySyncVars & 2) != 0)
{
this.int2 = (int)reader.ReadPackedUInt32();
}
// is 3rd SyncVar dirty
if ((dirtySyncVars & 4) != 0)
{
this.MyString = reader.ReadString();
}
}