携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情
Mirror中数据类型相关的场景
在Mirror中,客户端和服务器之间可能发送消息的任何场景,都和数据类型相关。这包含Command, ClientRpc, TargetRpc,Network MEssage等远程调用,SyncVar等状态同步。这些场景中,为了将数据发送到远端,必须对数据进行序列化和反序列化,而Mirror默认支持的数据类型就是已经在Mirror中实现了他们的序列化和反序列化方法的类型。对于不支持的数据类型,如果自己实现了他们的序列化和反序列化,则也可以在Mirror的远程调用和状态同步中使用。
Mirror支持的数据类型
- 基础的C#类型,如byte, int, char, uint, long, float, string等
- Unity内置的类型,主要是一些数学和几何相关的类型,如Vector3, Quaternion, Rect, Plane, Vector3Int等
- URI类型
- Mirror的NetworkIdentity
- 添加了NetworkIdentity组件的Game Object的引用可以被支持同步(但是引用同步后有可能是null,下面详细解释)
- 包含以上任意类型的结构体,建议实现IEquatable接口以避免装箱,另外最后设置为readonly以避免修改成员导致的重新同步
- class类型如果每个成员都是支持的数据类型则也可以支持
- ScriptableObject类型,只要每个成员是支持的数据类型
- 以上类型的数组
- 需要注意的是,对于class, ScriptableObject类型需要在堆上分配对象,因此每次发送他们都会增加GC的负担。
Game Object引用的同步问题
SyncVars, SyncList, SyncDictionary中的Game Object引用需要特别注意,因为同步后的引用可能是无效的。如果game object同时存在于服务器上和客户端,则引用不会有问题。当同步数据到达客户端,如果被引用的game object在客户端上已经不存在了或者还没有生成,则同步后的数据中该对象的引用为null。这是因为,对于包含NetworkIdentity的game object引用,实际上通过序列化在网络上传递的是NetworkIdentity组件的netId属性。然后在远端(客户端或服务器)通过这个netId在NetworkIdentity.spawned字典中查找远端的game object。如果该对象存在,则同步后的数据的game object引用就指向该对象,否则设为null。另外一种对象不存在的可能是该对象对当前客户端不可见。
直接同步 netID
相比同步Game Object引用,直接同步netID更稳妥,然后在NetworkIdentity.spawned中查找该对象。例如:
public GameObject target;
[SyncVar(hook = nameof(OnTargetChanged))]
public uint targetID:
void OnTargetChanged(uint oldValue, uint newValue)
{
if(NetworkIdentity.spawned.TrgGetValue(targetID, out NetworkIdentity identity))
{
target = identity.gameObject;
}
else
{
StartCoroutine(SetTarget());
}
}
IEnumerator SetTarget()
{
while(target == null)
{
yield return null;
if(NetworkIdentity.spawned.TrgGetValue(targetID, out NetworkIdentity identity))
{
target = identity.gameObject;
}
}
}