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

Unity网络地图实例化同步问题:服务器生成后无法同步至客户端

解决Unity UNet服务器生成Tile Map同步至新客户端的问题

看起来你在Unity旧版UNet的网络同步上踩了几个常见的坑,我来帮你梳理清楚问题出在哪,以及怎么解决:

核心问题分析

  1. 新客户端同步失效:你最初调用的NetworkServer.Spawn(t)只会把对象同步给当前已连接的客户端,新玩家加入时不会自动补发这些对象的生成消息;后来加的OnPlayerConnected方法根本不会被触发——因为这是NetworkManager的回调,不是NetworkBehaviour的,你的脚本里这个方法等于没写。
  2. 客户端重复生成地图:你没有严格隔离服务器和客户端的生成逻辑,导致每个客户端都会执行Start里的地图生成代码,自然会出现各自生成地图的情况。
  3. 预制件配置缺失:如果你的tile、墙等预制件没有添加NetworkIdentity组件,或者没加入NetworkManager的Spawnable Prefabs列表,服务器根本无法同步这些对象。

分步解决方案

第一步:先把预制件配置正确

  • 给所有需要同步的预制件(tile、墙、出生点)添加NetworkIdentity组件,不要勾选"Server Only"(因为客户端需要显示这些对象)。
  • 把这些预制件拖到场景中NetworkManager组件的Spawnable Prefabs列表里,确保服务器能识别并同步它们。

第二步:严格隔离服务器与客户端逻辑

修改Start方法,确保只有服务器才执行地图生成逻辑,客户端直接跳过:

void Start()
{
    // 只有服务器才执行生成逻辑,客户端直接返回
    if (!isServer)
    {
        Debug.Log("客户端不执行地图生成");
        return;
    }

    // 初始化tiles数组(大小要足够装下所有tile)
    tiles = new GameObject[MapSize * MapSize];
    int temp = 0;

    for (int c = MapSize / 2; c > -MapSize / 2; c--)
    {
        for (int r = -MapSize / 2; r < MapSize / 2; r++)
        {
            var t = Instantiate(prefab, new Vector3(r, 0, c), Quaternion.identity);
            tiles[temp] = t;
            // 用SpawnSceneObject替代Spawn,自动同步给后续连接的客户端
            NetworkServer.SpawnSceneObject(t);
            temp++; // 你之前漏了这个,会导致tiles数组覆盖!

            // 你的随机tile逻辑...
            if (Random.Range(0, 3) == 2)
            {
                t.GetComponent<RAISE>().Run();
                map += 1;
            }
            else
            {
                map += 0;
                // 生成出生点时也要同步
                if (c < 0 - MapSize / 4 && r > 0 + MapSize / 4)
                {
                    var red = Instantiate(SpawnLocRed, new Vector3(r, 2, c), Quaternion.identity);
                    NetworkServer.SpawnSceneObject(red);
                }
                if (c > 0 + MapSize / 4 && r < 0 - MapSize / 4)
                {
                    var blue = Instantiate(SpawnLocBlue, new Vector3(r, 2, c), Quaternion.identity);
                    NetworkServer.SpawnSceneObject(blue);
                }
            }
            map += "r";
        }
        map += "c";
    }

    // 墙的生成同理,每个墙实例都要调用SpawnSceneObject
    if (MapSize % 2 == 0)
    {
        GameObject wt = Instantiate(_TopW, new Vector3(-.5f, 0, -MapSize / 2), Quaternion.identity);
        wt.transform.localScale = new Vector3(MapSize, 5, 1);
        NetworkServer.SpawnSceneObject(wt);

        GameObject wb = Instantiate(_Botw, new Vector3(-.5f, 0, MapSize / 2 + 1), Quaternion.identity);
        wb.transform.localScale = new Vector3(MapSize, 5, 1);
        NetworkServer.SpawnSceneObject(wb);

        // 左右墙同理...
    }
}

第三步:确保新玩家自动同步地图

这里推荐用NetworkServer.SpawnSceneObject替代NetworkServer.Spawn——这个方法会把对象标记为场景网络对象,当新客户端连接时,服务器会自动把所有场景网络对象同步给它,不需要手动写回调。

如果不想用场景对象,也可以通过自定义NetworkManager监听玩家连接事件,手动同步:

using UnityEngine.Networking;

public class CustomNetworkManager : NetworkManager
{
    // 拖入你的GenerateLevel脚本实例
    public GenerateLevel levelGenerator;

    public override void OnServerConnect(NetworkConnection conn)
    {
        base.OnServerConnect(conn);
        // 遍历所有已生成的tile,同步给新客户端
        foreach(var tile in levelGenerator.tiles)
        {
            if(tile != null)
            {
                NetworkServer.Spawn(tile, conn); // 指定只同步给新连接的客户端
            }
        }
        // 同步墙、出生点同理
    }
}

把这个脚本挂载到场景的NetworkManager对象上,替换默认的NetworkManager组件即可。


额外注意点

  • 检查你的RAISE脚本,如果涉及到客户端需要同步的状态,要给它添加NetworkBehaviour,并把需要同步的变量标记为[SyncVar]。
  • 确保玩家角色是通过NetworkManager的Player Prefab生成的,这样角色和地图对象会处于同一个网络会话中,能正确交互。

内容的提问来源于stack exchange,提问作者Ethan Baxter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:29:23