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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:14:46