Unity+Mirror中NetworkServer.Spawn()客户端生成对象位置异常
Unity Mirror框架中NetworkServer.Spawn()客户端位置异常的解决方案
核心原因
Mirror框架中,NetworkServer.Spawn()默认不会自动同步对象的初始位置与旋转,客户端需要通过NetworkTransform组件来接收服务器同步的变换数据;同时原代码中使用SyncList<GameObject>存储网络对象的方式不符合Mirror的同步规则,导致客户端无法正确关联服务器生成的对象。
具体解决方案
1. 给卡牌预制体添加NetworkTransform组件
- 给所有
PrefabsActionCards中的卡牌预制体挂载NetworkTransform组件,该组件负责同步对象的位置、旋转和缩放信息。 - 调整组件的Sync Mode:根据需求选择同步类型(例如
Sync Transform同步全部,或Position Only仅同步位置),减少不必要的网络开销。
2. 修复SyncList的存储类型
SyncList<GameObject>无法在客户端正确同步,因为客户端与服务器的GameObject实例是独立的,应改用SyncList<uint>存储卡牌对象的NetworkIdentity.netId:
// 替换原有SyncList<GameObject>定义 public SyncList<uint> cards = new SyncList<uint>();
3. 修改卡牌生成与同步逻辑
调整GenerateDeckInitial方法,确保先完成对象初始化再Spawn,并存储正确的网络标识:
public SyncList<uint> GenerateDeckInitial() { SyncList<uint> cardsList = new SyncList<uint>(); foreach (var prefab in PrefabsActionCards) { Type scriptCardType = Type.GetType(prefab.name + ",Assembly-CSharp"); var script = prefab.GetComponent(scriptCardType); int quantidade = (int)script.GetType().GetField("Quantidade").GetValue(script); for (int j = 0; j < quantidade; j++) { // 实例化卡牌到目标位置与父物体 GameObject card = Instantiate(prefab, gameObject.transform.position, gameObject.transform.rotation, gameObject.transform); // 确保卡牌预制体有NetworkIdentity NetworkIdentity cardIdentity = card.GetComponent<NetworkIdentity>(); if (cardIdentity == null) { Debug.LogError($"卡牌预制体 {prefab.name} 缺少NetworkIdentity组件,请添加!"); Destroy(card); continue; } // 设置卡牌名称 card.name = prefab.name.Replace("(Clone)", ""); // Spawn网络对象 NetworkServer.Spawn(card); // 将netId添加到同步列表 cardsList.Add(cardIdentity.netId); } } return cardsList; }
4. 客户端同步处理
在客户端通过netId查找对应的本地对象,避免使用GameObject.Find:
public override void OnStartClient() { base.OnStartClient(); // 监听同步列表变化 cards.Callback += OnCardsSyncChanged; // 初始化已同步的卡牌 foreach (uint netId in cards) { if (NetworkIdentity.spawned.TryGetValue(netId, out NetworkIdentity identity)) { GameObject clientCard = identity.gameObject; Debug.Log($"客户端加载卡牌:{clientCard.name}"); // 此处可添加客户端专属逻辑(如UI显示、动画等) } } } private void OnCardsSyncChanged(SyncList<uint>.Operation op, int index, uint oldNetId, uint newNetId) { if (op == SyncList<uint>.Operation.Add) { if (NetworkIdentity.spawned.TryGetValue(newNetId, out NetworkIdentity identity)) { GameObject newCard = identity.gameObject; // 处理新增卡牌的客户端逻辑 } } }
5. 额外检查项
- 确保所有卡牌预制体已添加到NetworkManager的
Spawnable Prefabs列表中,否则客户端无法实例化对象。 - 若卡牌需要继承父物体的变换,确保父物体(挂载该脚本的gameObject)拥有
NetworkIdentity组件,否则客户端的父层级不会同步。
内容的提问来源于stack exchange,提问作者Leonardo Moreira
相关产品推荐
相关产品推荐

