Unity Mirror联网游戏开发(11) Mirror数据类型支持

612 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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;
        }
    }
}