Unity网络地图实例化同步问题:服务器生成后无法同步至客户端
解决Unity UNet服务器生成Tile Map同步至新客户端的问题
看起来你在Unity旧版UNet的网络同步上踩了几个常见的坑,我来帮你梳理清楚问题出在哪,以及怎么解决:
核心问题分析
- 新客户端同步失效:你最初调用的
NetworkServer.Spawn(t)只会把对象同步给当前已连接的客户端,新玩家加入时不会自动补发这些对象的生成消息;后来加的OnPlayerConnected方法根本不会被触发——因为这是NetworkManager的回调,不是NetworkBehaviour的,你的脚本里这个方法等于没写。 - 客户端重复生成地图:你没有严格隔离服务器和客户端的生成逻辑,导致每个客户端都会执行
Start里的地图生成代码,自然会出现各自生成地图的情况。 - 预制件配置缺失:如果你的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
相关产品推荐
相关产品推荐

