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

Photon Fusion中NetworkDictionary记录玩家距离出现重复值问题

解决Photon Fusion NetworkDictionary重复键值对问题

问题根源分析

输出出现重复键值对,本质是以下几个问题叠加导致:

  1. NetworkDictionary归属错误:如果字典挂载在每个玩家的NetworkObject上,每个玩家都会维护自己的字典副本,打印时遍历所有玩家的字典就会出现重复输出。
  2. 无权限修改网络集合:Fusion的Networked集合仅允许**拥有状态权限的端(通常是服务器)**修改,客户端直接修改会导致本地状态与网络同步状态冲突,产生异常条目。
  3. PlayerPrefs本地性问题:PlayerPrefs是本地存储,用它作为字典键无法保证网络上的一致性,多客户端同时修改同一个字典时,会引发重复添加或覆盖异常。

修复步骤

1. 重构NetworkDictionary存储位置

把playerDistances移到**全局共享的权威NetworkObject(比如GameManager)**上,确保整个游戏只有一个字典实例:

using Fusion;
using UnityEngine;

public class GameManager : NetworkBehaviour
{
    [Networked]
    [Capacity(4)] // 根据实际玩家数量调整容量
    public NetworkDictionary<NetworkString<_32>, float> PlayerDistances { get; } = new();
}

2. 改用Networked字段存储玩家名称

替换本地的PlayerPrefs,用Fusion的NetworkString<_32>同步玩家名称,确保网络上所有端看到的名称一致:

public class PlayerMovement : NetworkBehaviour
{
    [Networked]
    public NetworkString<_32> PlayerName { get; set; }
    public GameObject finishObject;

    public override void OnSpawned()
    {
        // 仅拥有输入权限的本地玩家,从本地Prefs读取名称并同步
        if (HasInputAuthority)
        {
            PlayerName = PlayerPrefs.GetString("PlayerName");
        }
    }

    // 保留原距离计算逻辑
    private float CalculateDistancePercentage()
    {
        return Vector3.Distance(transform.position, finishObject.transform.position);
    }
}

3. 仅让服务器权威端更新字典

有两种可靠的更新方式,选择其一即可:

方式一:服务器主动计算所有玩家距离(推荐,逻辑更集中)

在GameManager的FixedUpdateNetwork中,服务器遍历所有玩家并更新距离:

public override void FixedUpdateNetwork()
{
    // 仅服务器/权威端执行
    if (!HasStateAuthority) return;

    foreach (var playerRef in Runner.ActivePlayers)
    {
        // 获取玩家对应的NetworkBehaviour
        if (playerRef.TryGetBehaviour<PlayerMovement>(out var playerBehaviour))
        {
            var playerName = playerBehaviour.PlayerName;
            var distance = playerBehaviour.CalculateDistancePercentage();

            // 更新字典:存在则覆盖,不存在则添加
            if (PlayerDistances.ContainsKey(playerName))
            {
                PlayerDistances.Set(playerName, distance);
            }
            else
            {
                PlayerDistances.Add(playerName, distance);
            }
        }
    }
}

方式二:客户端通过RPC请求服务器更新

在玩家脚本中,客户端向服务器发送距离更新请求,由服务器修改字典:

public class PlayerMovement : NetworkBehaviour
{
    public GameObject finishObject;

    public override void FixedUpdateNetwork()
    {
        // 仅本地玩家(拥有输入权限)发送更新请求
        if (HasInputAuthority)
        {
            float distance = CalculateDistancePercentage();
            Runner.SendRpc(RpcUpdatePlayerDistance, RpcTargets.StateAuthority, PlayerName, distance);
        }
    }

    // 仅服务器/权威端接收并处理RPC
    [Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
    private void RpcUpdatePlayerDistance(NetworkString<_32> playerName, float distance)
    {
        var gameManager = Runner.FindObjectOfType<GameManager>();
        if (gameManager.PlayerDistances.ContainsKey(playerName))
        {
            gameManager.PlayerDistances.Set(playerName, distance);
        }
        else
        {
            gameManager.PlayerDistances.Add(playerName, distance);
        }
    }

    private float CalculateDistancePercentage()
    {
        return Vector3.Distance(transform.position, finishObject.transform.position);
    }
}

4. 修改打印逻辑,仅打印全局字典内容

确保打印的是GameManager中的全局字典,而非每个玩家的本地字典:

public class UIManager : MonoBehaviour
{
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.U))
        {
            var gameManager = FindObjectOfType<GameManager>();
            foreach (var kvp in gameManager.PlayerDistances)
            {
                Debug.Log($"{gameManager.PlayerDistances.Count}/{gameManager.PlayerDistances.Capacity} Key:{kvp.Key} Value:{kvp.Value:F3}");
            }
        }
    }
}

额外注意事项

  • 原代码中playerDistances用=> default定义,会导致每次访问都返回空实例,必须改为自动属性并实例化(如步骤1中的写法)。
  • 确保GameManager的NetworkObject在场景中被正确设置为权威对象(Fusion默认服务器拥有状态权限)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 22:53:12