1、序列化的对象不能含有环状引用,一个典型的例子是单例模式,它的instance静态变量肯定要引用到本体,所以需要注意。如果拿这种对象去spawn,即使在NetworkManager里注册了这个物体 也一样会spawn失败,因为中途涉及到序列化,传输,反序列化的步骤。解决这种问题的简单方法目前想到的是设置一个数据类型,保证其中不包含环状引用 然后在客户端拿这个类型的对象来收集无法序列化的类的对象成员,然后进行序列化或者反序列化,或者去spawn
2、没有Spawn过的对象上调用ClientRPC方法会被忽略,而Spawn的对象需要和服务端同步,所以需要被序列化。mirror的序列化不可以包含环状引用,因此单例对象上是无法调用ClientRPC的,需要把这些单例去掉,才能在上面使用ClientRPC。
3、如果没有NetworkManager在场景里的话,挂上NetworkIdentity标签的物体会在场景启动时被禁用,还不知道为什么这么设计,猜测可能是为了避免不同时生成物体造成的跨端(服务端客户端)id混乱,毕竟全都spawn出来的话,生成的物体的identity的netid是一致的。
4、一个物体刚在服务端spawn的时候对它调用Rpc方法会失效,可能是客户端还没来得及生成对应的物体。这种情况需要用协程来延迟,也可以用UniTask库的async await轮询器和封装好的等待对象。
5、NetworkIdentity 的Server only属性被勾选的时候就不会在Spawn之后自动在客户端生成对应物体。
6、默认NetworkTransform的缩放同步是未开启的。
7、对一个对象使用NetworkServer.Spawn的时候,会同时在客户端创建其子对象树结构,但是未使用NetworkBehaviour的对象是否会一起创建?答案是会的,有一个例外就是一个spawn出来的transform树形体系只能在根对象有一个IdentifyComponent。其他子级不能有Identify或者从NetworkBehaviour派生的component,因为会自动引入IdentifyComponent。这种情况就需要写代码自行完成spawn之后的对象体系的挂接工作。
8、在一个跟节点有IdentifyComponent的体系上,被序列化的体系内不能有序列化不支持的类型,包括Component或者GameObject。但是我们可以在生成的体系中把层级关系让unity内置的transform树形结构来维护,这可以解决大部分引用不含IdentityComponent的子级节点的引用会妨碍序列化的问题。另外含有IdentifyComponent的子级组件的引用不会妨害序列化,因为这部分会自动被过滤掉不参加序列化,需要自行Spawn再挂接。
9、序列化只能保持在prefab就有的transform层级结构,从场景中的物体进行序列化的话,用代码后续添加的子级不会跟着被序列化传递到客户端。因为本质上spawn就是把用于寻找资源的AssetID发送到客户端,然后客户端同样用AssetID来找到预制件,然后实例化预制件,赋予同时传递来的NetID,用于接收后续发来的同步消息的分发凭据。所以服务端的物体,实例化后,spawn之前的对象层级变化,都不会在客户端同步,甚至可能造成不存在的NetID的问题。
10、command方法里再调用command方法可能造成服务端执行不完整,应该避免。
11、SyncXXX系列容器里同步一样是用Waves,所以元素类型不能是GameObject或者Component。可以通过存储NetId来变通实现远程同步。
12、isServer isClient isServerOnly isLocalPlayer
13、NetworkServer和NetworkClient是Transporter上第二层的连接和通信
14、NetworkServer.AddPlayer(conn,gameObject)把一个gameObject关联到一个conn作为Player的表示物体。conn的identity属性被赋予gameObject的identity组件。
15、调用NetworkManager游戏场景切换时,服务端先进行切换,然后广播场景切换消息到所有客户端,在LateUpdate里轮询检测场景切换是否完成:
,然后各自调用状态同步代码。
替换roomPlayer到gamePlayer和传递参数
LateUpdate UpdateScene NetworkManager.FinishLoadScene FinishLoadSceneHost OnServerSceneChanged FinishLoadSceneServerOnly OnServerSceneChanged OnClientSceneChanged FinishLoadSceneClientOnly) OnClientSceneChanged NeworkManager.OnClientSceneChanged() NetworkRoomManager.OnClientSceneChanged() NetworkClient.AddPlayer() NetworkManager.OnClientSceneChanged() NetworkClient.Ready() NetworkClient.connection.send(ReadyMessage) ReadyMessage NetworkManager.OnServerReadyMessageInternal() NetworkManager.OnServerReady()//此处conn参数为null导致后续没有调用 NetworkRoomManager.SceneLoadedForPlayer() NetworkRoomManager.OnRoomServerSceneLoadedForPlayer()
创建RoomPlayer
public static bool NetworkClient.AddPlayer()//3个调用的地方都先设定了NetworkClient.Ready() AddPlayerMessage void NetworkManager.OnServerAddPlayerInternal(NetworkConnectionToClient conn, AddPlayerMessage msg) public override void NetworkRoomManager.OnServerAddPlayer(NetworkConnectionToClient conn) //创建RoomPlayer public virtual void NetworkRoomManager.OnRoomServerAddPlayer(NetworkConnectionToClient conn) public static bool NetworkServer.AddPlayerForConnection(NetworkConnectionToClient conn, GameObject player)//是否会更新identity.netId 是否调用spawn在客户端生成玩家对象 internal static void NetworkClient.InternalAddPlayer(NetworkIdentity identity);//host模式 static void NetworkServer.SpawnObserversForConnection(NetworkConnectionToClient conn) public static void NetworkServer.SetClientReady(NetworkConnectionToClient conn) static void NetworkServer.Respawn(NetworkIdentity identity)//netId==0发消息客户端生成playerObject,否则更新playerObject
NetworkRoomManager.OnClientEnterRoom添加RoomPlayerObject
NetworkRoomPlayer的OnGUI绘制了自己的准备状态和按钮
NetworkRoomManager的OnGUI绘制了进入房间后的UI底版
设置了DontDestroyOnLoad的对象如果重新设置setParent会导致不销毁标记失效
点击数:131