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

Unity中Client/Server RPC调用后字典未完整同步问题

问题分析与解决

核心原因

  1. 成员变量的独立性:testDict是PlayerPosition类的私有成员变量,每个玩家Prefab实例都会拥有独立的字典副本。在客户端/主机模式下,每个客户端仅会实例化自己的玩家对象,因此你的客户端只会处理自己触发的ClientRpc更新,自然只会有一个键值对;主机上虽然有两个玩家实例,但每个实例的字典也是独立的。
  2. 静态类的进程隔离:静态类的数据在每个独立进程中是隔离的。主机作为服务器+客户端,其进程内的静态字典会收到两次Rpc更新(来自两个客户端),所以显示正确;但每个客户端是单独的进程,各自的静态字典只会处理自己触发的Rpc更新,因此依然只有一个键值对。

解决方案

要实现所有客户端共享全局的玩家数据字典,需要将数据放在服务器端统一管理,并同步完整数据给所有客户端,或者每次更新时确保所有客户端都能收到完整的数据集。

方案1:服务器端单例管理全局字典,同步完整数据

创建服务器端单例类统一管理玩家数据,更新时同步完整字典给所有客户端:

public class PlayerDataManager : NetworkBehaviour
{
    public static PlayerDataManager Instance { get; private set; }
    private Dictionary<string, int> globalPlayerDict = new();

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
        }
        else
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
    }

    public override void OnNetworkSpawn()
    {
        if (!IsServer)
        {
            enabled = false;
            return;
        }
    }

    [ServerRpc(RequireOwnership = false)]
    public void UpdatePlayerDataServerRpc(string key, int value)
    {
        globalPlayerDict[key] = value;
        SyncPlayerDataClientRpc(globalPlayerDict);
    }

    [ClientRpc]
    private void SyncPlayerDataClientRpc(Dictionary<string, int> dict)
    {
        globalPlayerDict = new Dictionary<string, int>(dict);
        foreach (var item in globalPlayerDict)
        {
            Debug.Log($"Item in current dict: {item.Key} = {item.Value}");
        }
    }
}

修改原PlayerPosition脚本调用单例:

public class PlayerPosition : NetworkBehaviour
{
    public void SubmitData(string key, int value)
    {
        PlayerDataManager.Instance?.UpdatePlayerDataServerRpc(key, value);
    }

    // 其他调用SubmitData的逻辑代码
}

方案2:广播单条更新,客户端累积数据

适合简单场景,确保每次更新都发送给所有客户端,客户端用静态字典累积数据:

public class PlayerPosition : NetworkBehaviour
{
    private static Dictionary<string, int> testDict = new();

    [ServerRpc(RequireOwnership = false)]
    private void HitServerRpc(string key, int value)
    {
        testDict[key] = value;
        RefreshClientRpc(key, value);
    }

    [ClientRpc]
    private void RefreshClientRpc(string key, int value)
    {
        testDict[key] = value;
        foreach (var item in testDict)
        {
            Debug.Log($"Item in current dict: {item.Key} = {item.Value}");
        }
    }

    // 其他调用HitServerRpc的逻辑代码
}

注意:这种方式需要处理新客户端的初始数据同步,避免新客户端遗漏历史数据。

关键注意事项

  • Rpc作用范围:ClientRpc默认发送给所有客户端,但会在每个客户端的对应NetworkBehaviour实例上执行。如果字典是实例成员变量,每个实例的字典相互独立,无法实现全局共享。
  • 服务器权威:所有数据更新必须先经过服务器处理,再同步给客户端,避免数据不一致。
  • 新客户端同步:若有客户端中途加入,需额外同步当前完整数据,确保新客户端数据完整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 09:15:15