基于NetworkLobbyManager按平台生成预制体:客户端与主机同步异常
问题诊断与解决方案
你遇到的问题核心很明确:你在服务器端判断的是服务器自身的运行平台,而不是客户端的平台!
当AddPlayer方法在服务器上执行时,Application.platform获取的是服务器所在机器的平台(比如你用Windows编辑器当主机,那不管客户端是安卓还是Windows,服务器都会把所有玩家的索引设为0),这就导致所有生成的玩家预制体都和主机一致了。
要解决这个问题,你需要让客户端把自己的平台信息主动发送给服务器,服务器再根据客户端传来的信息来分配对应的预制体索引。下面是具体的实现步骤:
步骤1:创建自定义LobbyPlayer脚本传递平台信息
我们可以借助NetworkLobby的玩家对象,让客户端在初始化时把自身平台信息同步给服务器:
using UnityEngine; using UnityEngine.Networking; public class CustomLobbyPlayer : NetworkLobbyPlayer { [SyncVar(hook = nameof(OnPlatformIndexChanged))] private int m_platformIndex; public override void OnStartClient() { base.OnStartClient(); // 只有本地客户端才需要向服务器发送自己的平台信息 if (isLocalPlayer) { CmdSendPlatformIndex(GetPlatformIndex()); } } // 客户端向服务器发送平台索引的Command方法 [Command] private void CmdSendPlatformIndex(int index) { m_platformIndex = index; // 通知LobbyManager记录该玩家的平台索引 var lobbyManager = FindObjectOfType<LobbyManager>(); lobbyManager.RecordPlayerPlatform(connectionToServer.connectionId, index); } // 转换当前平台为对应的预制体索引 private int GetPlatformIndex() { if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer) { return 0; } else if (Application.platform == RuntimePlatform.Android) { return 1; } // 其他平台默认使用索引0的预制体 return 0; } // 平台索引同步后的钩子方法(可选,用于客户端UI更新等) private void OnPlatformIndexChanged(int oldValue, int newValue) { // 这里可以添加客户端的逻辑,比如显示平台标识 } }
步骤2:修改LobbyManager逻辑
去掉原来在服务器端判断平台的代码,改为接收客户端传来的平台信息:
using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; public class LobbyManager : NetworkLobbyManager { private Dictionary<int, int> m_currentPlayers = new Dictionary<int, int>(); // 新增方法:记录客户端上报的平台索引 public void RecordPlayerPlatform(int connectionId, int platformIndex) { if (m_currentPlayers.ContainsKey(connectionId)) { m_currentPlayers[connectionId] = platformIndex; } else { m_currentPlayers.Add(connectionId, platformIndex); } } public override GameObject OnLobbyServerCreateLobbyPlayer(NetworkConnection conn, short playerControllerId) { // 平台信息由CustomLobbyPlayer负责传递,这里不需要额外处理 return base.OnLobbyServerCreateLobbyPlayer(conn, playerControllerId); } public override GameObject OnLobbyServerCreateGamePlayer(NetworkConnection conn, short playerControllerId) { // 根据客户端上报的平台索引实例化对应预制体 if (m_currentPlayers.TryGetValue(conn.connectionId, out int platformIndex)) { GameObject go = Instantiate(spawnPrefabs[platformIndex]); NetworkServer.AddPlayerForConnection(conn, go, playerControllerId); return go; } // 如果未获取到平台信息,默认使用第一个预制体 return base.OnLobbyServerCreateGamePlayer(conn, playerControllerId); } }
步骤3:配置预制体
在你的Lobby场景中,找到LobbyPlayer预制体,添加CustomLobbyPlayer脚本(替换或叠加原有的NetworkLobbyPlayer均可),并确保LobbyManager的lobbyPlayerPrefab字段指向这个自定义预制体。
为什么原来的方法无效?
Unity Networking的服务器端代码运行在服务器进程中,Application.platform获取的是服务器的运行环境,而非客户端的。你之前的逻辑相当于“所有玩家都使用服务器平台对应的预制体”,自然会出现客户端与主机生成相同对象的问题。
通过让客户端主动上报自身平台信息,服务器才能精准地为每个客户端分配对应的预制体,实现不同平台生成不同玩家对象的需求。
内容的提问来源于stack exchange,提问作者Kalixio
相关产品推荐
相关产品推荐

