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
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.