You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 16:48:20