英文:
Duplicate Values in Dictionary When Logging Distances in Photon Fusion
问题
我正在使用Photon Fusion在Unity中开发一个多人游戏。
我有一个名为playerDistances的字典:
[Networked]
[Capacity(4)] // 设置集合的固定容量
[UnitySerializeField] // 在检查器中显示此私有属性。
private NetworkDictionary<NetworkString<_32>, float> playerDistances => default;
在这里,我记录了每个玩家距离终点线的距离。我在FixedUpdateNetwork()方法中更新这些距离:
Dist = CalculateDistancePercentage();
if (playerDistances.ContainsKey(PlayerPrefs.GetString("PlayerName")))
{
// 如果玩家已经在字典中,我们应该更新他的距离而不是添加新的键值对
playerDistances.Set(PlayerPrefs.GetString("PlayerName"), Dist);
}
else
{
// 如果玩家不在字典中,我们应该添加一个新的键值对
playerDistances.Add(PlayerPrefs.GetString("PlayerName"), Dist);
}
并且在按下'U'键时,在Update()方法中打印它们:
if (Input.GetKeyDown(KeyCode.U))
{
foreach (var kvp in playerDistances)
{
Debug.Log($"{playerDistances.Count}/{playerDistances.Capacity} Key:{kvp.Key} Value:{kvp.Value}");
}
}
这是我的CalculateDistancePercentage方法:
private float CalculateDistancePercentage()
{
//Vector3 closestPointOnBounds = finishCollider.bounds.ClosestPoint(transform.position);
float currentDistance = Vector3.Distance(transform.position, finishObject.transform.position);
//Debug.Log($"Absolute distance to the finish edge: {currentDistance}");
return currentDistance;
}
然而,当我按'U'键时,它会打印不同键的重复值:
2/6 Key:AC Value:69.639
2/6 Key:BBB Value:69.639
2/6 Key:AC Value:22.622
2/6 Key:BBB Value:22.622
尽管每个键(代表一个唯一的玩家)应该有不同的值,就像这样:
2/6 Key:AC Value:69.639
2/6 Key:BBB Value:22.622
英文:
I'm developing a multiplayer game in Unity using Photon Fusion.
I have a dictionary, playerDistances:
[Networked]
[Capacity(4)] // Sets the fixed capacity of the collection
[UnitySerializeField] // Show this private property in the inspector.
private NetworkDictionary<NetworkString<_32>, float> playerDistances => default;
where I record the distance of each player to the finish line. I update these distances in the FixedUpdateNetwork() method
Dist=CalculateDistancePercentage();
If (playerDistances.ContainsKey(PlayerPrefs.GetString("PlayerName"))) {
// If the player is already in the dictionary, then we should update his distances instead of adding new key pairs
playerDistances.Set(PlayerPrefs.GetString("PlayerName"), Dist).
} else {
// If the player is not already in the dictionary, then we should add a new key-value pair
playerDistances.Add(PlayerPrefs.GetString("PlayerName"), Dist).
}
and print them when pressing the 'U' key in the Update() method.
If (Input.GetKeyDown(KeyCode.U))
{
foreach(var kvp in playerDistances)
{
Debug.Log($"{playerDistances.Count}/{playerDistances.Capacity} Key:{kvp.Key} Value:{kvp.Value}").
}
}
this is my CalculateDistancePercentage:
private float CalculateDistancePercentage()
{
//Vector3 closestPointOnBounds = finishCollider.boun ds.ClosestPoint(transform.position).
float currentDistance = Vector3.Distance(transform.position,finishObject.transform.position).
//Debug.Log($"Absolute distance to the finish edge: {currentDistance}").
return currentDistance.
}
However, when I press 'U', it prints duplicate values for different keys
2/6 Key:AC Value:69.639
2/6 Key:BBB Value:69.639
2/6 Key:AC Value:22.622
2/6 Key:BBB Value:22.622
even though each key (which represents a unique player) should have a different value like this:
2/6 Key:AC Value:69.639
2/6 Key:BBB Value:22.622
答案1
得分: 0
不解决你的问题,但总的来说:根据API,只使用Set
就足够了,它会为给定的键设置值。如果键不存在,它将添加一个新键。因此,你的检查是多余的;)
然后,对我来说,你似乎没有检查本地权限
=> 你同时对字典中的内容进行写入和本地打印,对两个对象都是如此。
此外,playerDistances
似乎与你的玩家对象的特定实例绑定,因此实际上有两个独立的字典,每个玩家组件上都有一个。
所以,对于每个玩家,你都执行
CalculateDistancePercentage();
它始终使用相应的变换的正确距离
但是然后你总是使用以下方式存储它
PlayerPrefs.GetString("PlayerName")
作为键,无论这个组件运行在哪个客户端,它都将始终返回相同的本地名称。
因此,我的猜测是,在一个字典中,比如在玩家"AC"上,你存储了
2/6 键:AC 值:69.639
由于你还在另一个客户端("BBB")的组件上运行了相同的代码,在该字典中,你添加了
2/6 键:AC 值:22.622
同样的事情也发生在另一个客户端,该组件在"AC"上添加
2/6 键:BBB 值:69.639
而"BBB"的组件添加了
2/6 键:BBB 值:22.622
现在它们被同步,两个字典都包含了两个不同距离的键。
而且,对于打印,你实际上打印了两个独立的字典
所以要解决这个问题(免责声明:我不是Photon专家,我只是在网上搜索这些信息),你要么根本不使用字典,而只是在相应组件本身上同步一个单一的浮点数
[Networked] [field: SerializeField] public float distance { get; private set; }
然后确保只在本地客户端上进行计算
if(HasStateAuthority)
{
distance = CalculateDistancePercentage();
}
或者你可以确保只使用一个字典来存储两个玩家的数据 - 即与会话本身关联的字典,而不是与每个玩家关联的字典 - 自定义会话属性,每个玩家都可以写入SessionInfo
.UpdateCustomProperties
,并从SessionInfo.Properties
中读取
例如,有点像这样
if(HasStateAuthority)
{
var distance = CalculateDistancePercentage();
var properties = SessionInfo.Properties;
properties[PlayerPrefs.GetString("PlayerName")] = distance;
SessionInfo.UpdateCustomProperties(properties);
}
然后对于打印
if (HasInputAuthority && Input.GetKeyDown(KeyCode.U))
{
var properties = SessionInfo.Properties;
foreach(var kvp in properties)
{
Debug.Log($"Key:{kvp.Key} Value:{kvp.Value}").
}
}
如果使用更多这些属性,你可能希望为键添加一个专用前缀,以便知道这是关于玩家距离的。
英文:
Not solving your issue but in general: It is enough to use only Set
as per API Sets the value for the given key. Will add a new key if the key does not already exist.
so your check is redundant
Then it sounds and looks to me like you are not checking for the local authority
=> You run both, the writing of things into the dictionary as well as the printing locally for both objects.
Further the playerDistances
seems to be bound to a specific instance of your player objects so you actually have two independent dictionaries, one on each player component.
So what happens is for each player you do
CalculateDistancePercentage();
which always uses the correct according transform's distance
BUT then you always store it using
PlayerPrefs.GetString("PlayerName")
as key, which no matter which component this runs on will always return the same local name.
So my guess is that in the one dictionary let's say on player "AC" you store
2/6 Key:AC Value:69.639
and since you also run the code on the other client's ("BBB") component in that dictionary you add
2/6 Key:AC Value:22.622
The same happens on the other client's side where the component on "AC" adds
2/6 Key:BBB Value:69.639
and the component of "BBB" adds
2/6 Key:BBB Value:22.622
Now they get synchronized and both dictionaries contain both keys with the different distances.
And again for printing you actually print both independent dictionaries
So to solve this (disclaimer: No Photon expert whatsoever, I'm just googling this together) you either do not want to use a dictionary at all, but just synchronize a single float on the respective component itself
[Networked] [field: SerializeField] public float distance { get; private set; }
and then make sure to only calculate this on the local client
if(HasStateAuthority)
{
distance = CalculateDistancePercentage();
}
Or you could make sure you only use one single dictionary for both players - namely one that is associated with the session itself rather than each player - Custom Session Properties each player can write to SessionInfo
.UpdateCustomProperties
and read from SessionInfo.Properties
e.g. somewhat like
if(HasStateAuthority)
{
var distance = CalculateDistancePercentage();
var properties = SessionInfo.Properties;
properties[PlayerPrefs.GetString("PlayerName")] = distance;
SessionInfo.UpdateCustomProperties(properties);
}
and then for printing
if (HasInputAuthority && Input.GetKeyDown(KeyCode.U))
{
var properties = SessionInfo.Properties;
foreach(var kvp in properties)
{
Debug.Log($"Key:{kvp.Key} Value:{kvp.Value}").
}
}
if using more of those properties you might want to add a dedicated prefix to the keys so you know this is about a player distance.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论