Basic Scoreboard

The whole answer can be found below.

Please note: The Photon forum is closed permanently. After many dedicated years of service we have made the decision to retire our forum and switch to read-only: we've saved the best to last! And we offer you support through these channels:

Try Our
Documentation

Please check if you can find an answer in our extensive documentation on PUN.

Join Us
on Discord

Meet and talk to our staff and the entire Photon-Community via Discord.

Read More on
Stack Overflow

Find more information on Stack Overflow (for Circle members only).

Write Us
an E-Mail

Feel free to send your question directly to our developers.

Basic Scoreboard

jgonzosan
2018-09-13 02:00:35

Ok so I had this posted before but I don't know what happened to my post after I edited it so I'm posting it again. I'm trying to create a very basic scoreboard for just a few players in my game. Since I know I will only have 2 players in my game I have a pre-created scoreboard (UI based) in the multiplayer level. It has text for a generic name and points. Right now I'm checking for when a player enters the room using their player ID.

I then change the name and give them starting points of 0. This works fine locally on both computers I've tested this on, but it never syncs up. So player one will have their name properly displayed, but the second player on the scoreboard doesn't show a change. Same is true for the second player. Their name shows up under "player 2" but the first slot for player 1 doesn't show a change.

This is the script I'm using. I applied a PhotonView to the scoreboard in the game and I'm using an RPC to initialize the scoreboard in the start method:

using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
using UnityEngine.UI;

public class ScoreManager : Photon.PunBehaviour  
{


    public static ScoreManager Instance;  
    public int player;  
    public Text[] nameText;  
    public Text[] scoreText;  
    public int[] currentPoints;  
    public int totalPoints = 10;


    void Start()  
    {  
            //Check if instance already exists  
        if (Instance == null)  
            Instance = this;    
        else if (Instance != this)  
               Destroy(gameObject);

        player = PhotonNetwork.player.ID -1;  
		photonView.RPC("InitScoreboard", PhotonTargets.AllBuffered);  
    }



    [PunRPC]  
    void InitScoreboard()  
    {          
       
        nameText[player].text = "Player " + (player + 1);  
        currentPoints[player] = 0;  
        totalPoints = 10;  
    }


    public void UpdateScore()  
    {  
        Debug.Log("You got hit");  
        currentPoints[player] += 1;  
        scoreText[player].text = currentPoints[player].ToString();  
        CheckScore();  
    }

    void CheckScore()  
    {  
        if (currentPoints[player] >= totalPoints)  
        {  
            currentPoints[player] = totalPoints;  
            Debug.Log("Game Over " + "player " + player + " has Won");  
        }  
    }  
}

I've done something somewhat similar with a game object in the level that has health as I wanted to test out RPCs and it works fine. Everything syncs up and change in health on one computer syncs across the network to the other. I just don't understand why it won't work for this. What am I missing here?

Comments

[Deleted User]
2018-09-13 10:45:25

Hi @jgonzosan,

I guess the problem is, that you don't initialize the scoreboard properly. As far as I see you are just doing this with information about the local client, but not the remote one. Personally I would also avoid using a PhotonView component on a Scoreboard.

The better way would be to use the PunPlayerScores extension in combination with certain callbacks. I would recommend you taking a look at this discussion about the PunPlayerScores extension and this discussion about which callbacks you can use for the Scoreboard.

jgonzosan
2018-09-13 14:53:10

Thanks. I think I got the generic scoreboard setup for displaying the players. I have yet to test this but I wanted to ensure I properly understood this. So if I used the PunPlayerScores script with the UI text I could do something like this?:

  
public Text playerPoints;

public void AddPoints ()  
{  
PhotonNetwork.player.AddScore(10);

}

public void UpdateText ()  
{  
playerPoints.text = PhotonNetwork.player.GetScore().ToString();  
}

Is this correct?

jgonzosan
2018-09-13 15:28:23

I tried this out with a very simple points system that increments by 10 every time I press left click on my mouse. I was able to use the Set/Get/Add from the PunPlayerScores script but wasn't sure if I was using it correctly. I assumed that I could add points and get them and it would sync across the network but it didn't seem like it did. When I added points it only worked locally. I use an RPC for the add points method I created and it worked. Here is my script:

  
using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
using UnityEngine.UI;

public class ScoreboardUpdate : Photon.PunBehaviour  
{

    public Text playerPoints;

	void Start ()  
	{  
		PhotonNetwork.player.SetScore(0);  
		UpdateText();  
	}

	  // Use this for initialization  
    void Update()  
    {

        if (Input.GetButtonDown("Fire1"))  
        {  
			photonView.RPC("AddPoints", PhotonTargets.AllBuffered);  
		  
        }

    }

	[PunRPC]  
    public void AddPoints()  
    {  
        PhotonNetwork.player.AddScore(10);  
		UpdateText();  
    }

    public void UpdateText()  
    {  
        playerPoints.text = PhotonNetwork.player.GetScore().ToString();  
    }  
}

Since I used an RPC I added a Photon View to the script. This would typically be called by an object or player so I assume this is the proper way to go about it. Should this be written differently?

[Deleted User]
2018-09-13 15:35:13

When you use AddScore (or SetScore) this is automatically synchronized across all clients in the room. The related client's Custom Properties are updated.

What you have to do manually is to 'repaint' the scoreboard itself to show up-to-date values. Right now you can basically do this from inside the Update function. However this is not very good in terms of performance. To optimize this, you can use the OnPhotonPlayerPropertiesChanged to repaint the scoreboard from inside that callback.

S_Oliver
2018-09-14 09:36:44

I created a pretty simple scoreboard in my Example Project.
https://github.com/SradnickDev/Photon-Example/blob/master/Assets/Prototype/Scoreboard.cs
http://forum.photonengine.com/discussion/12285/photon-pun-unity-example-project#latest

jgonzosan
2018-09-14 13:53:21

@S_Oliver wrote:

I created a pretty simple scoreboard in my Example Project.
https://github.com/SradnickDev/Photon-Example/blob/master/Assets/Prototype/Scoreboard.cs
http://forum.photonengine.com/discussion/12285/photon-pun-unity-example-project#latest

Hah, yesterday I was browsing Youtube and found your video shortly after you uploaded it: https://www.youtube.com/watch?v=8Lrb6-SHiv8 and was looking through the Github page. Thanks for posting this, will definitely take a look at it!

S_Oliver
2018-09-14 14:03:25

Yeah i uploaded the Video yesterday and the git repo today :D

jgonzosan
2018-09-14 14:14:09

So I have the scoreboard up for the most part, but did have a question on how you determine who shot you last. Looking through some of the health scripts I see this:

 public void OnReceiveDamage(float value, PhotonPlayer lastShoot)  
    {  
        PhotonView.RPC("OnAddDamage", PhotonTargets.All, value, lastShoot);  
    }

 [PunRPC]  
    void OnAddDamage(float value, PhotonPlayer lastShoot)  
    {  
        AddDamage(value);  
        m_lastHit = lastShoot;  
    }

So is this all that is needed to know who shot you last? You just store a reference to a Photon player when calling the RPC method? I assume whoever called that method would be the player that shot you last correct?

By the way thanks for having clearly readible code, your project is one of few on the subject that isn't overly complex. It's quite useful.

S_Oliver
2018-09-14 14:44:23

Yeah thats basically is all you need. Sure there are other ways and acctually you dont have to send the PhotonPlayer. Each RPC contains PhotonMessageInfo wich contains the Sender,TimeStamp and PhotonView.

http://doc-api.photonengine.com/en/PUN/current/struct_photon_message_info.html#a8497209fff12bb41b79d010905e1cb39
as an Example..

  
public void OnReceiveDamage(float value)  
    {  
        PhotonView.RPC("OnAddDamage", PhotonTargets.All, value);  
    }

 [PunRPC]  
    void OnAddDamage(float value, PhotonMessageInfo info)  
    {  
        AddDamage(value);  
        m_lastHit = info.sender;  
    }

Thank you @jgonzosan That was my intention for the Project.

jgonzosan
2018-09-14 22:01:17

Ok after a lot of testing and experimenting I think I finally understand it. I built a very simple script to test scoring with two players to see if it updates properly and this is what I came up with:

	// Update is called once per frame  
	void Update () {

		if(photonView.isMine && Input.GetButtonDown("Fire1"))  
		{  
			PhotonNetwork.player.AddScore(10);  
		}  
			  
		  
		  
	}

	 void OnPhotonPlayerPropertiesChanged(object[] playerAndUpdatedProps)  
	 {  
		  
		PhotonPlayer[] pList = PhotonNetwork.playerList;  
		for (int i = 0; i < pList.Length; i++)  
        {  
        	PhotonPlayer player = pList[i];  
         	 ScoreManager.Instance.playerText[i].text = player.GetScore().ToString();  
        }  
 	 }  
}

I'll probably include all of the scoring methods and such within the ScoreManager but this was a quick way of seeing how it worked. Since each player had their own UI prefab on the scoreboard I stored all the text elements in an array. I essentially modified some parts of what you had @S_Oliver in your scoreboard script so thanks once again. I was missing little bits and pieces so having a solid reference like your project helped quite a bit. Also thanks to @Christian_Simon for pointing me in the right direction. I'd probably still be trying to use RPCs for this had you not mentioned the proper way to update these.

jgonzosan
2018-09-17 14:29:05

Ok I'm having a bit of a weird issue with my scoreboard at the moment. It seems like whenever I have two players in the game, the text "switches" sides. I posted an image of what I'm seeing:

https://ibb.co/dK6JHK

It seems like no matter which I play as, client or master, the text is always swapped in that exact way. Any ideas why this is happening?

S_Oliver
2018-09-18 04:24:09

Are you using PhotonNetwork.playerList ? I think its not in the same order on different clients.

jgonzosan
2018-09-18 12:56:55

Yes I am, the code I'm using I posted above. So is there a way to order the list so that everyone has the same order?

jgonzosan
2018-09-18 13:33:09

Ok so I'm adding in this, similar to how you had it in your scoreboard script. I don't know why I didn't add this in intiially:

  PhotonPlayer[] pList = PhotonNetwork.playerList;  
        System.Array.Sort(pList, delegate (PhotonPlayer p1, PhotonPlayer p2) { return p1.GetCurrentScore().CompareTo(p2.GetCurrentScore()); });  
        System.Array.Reverse(pList);

Though in yours you used "GetCurrentScore" which gave me an error. Was that an old way of getting the score or is that a custom method? I replaced it with "GetScore". I assume this would fix my issue since the list would be ordered by the score on all systems.

S_Oliver
2018-09-18 14:50:55

Yeah it is a part of my Player Extension but it is kinda like the Photon default.
https://github.com/SradnickDev/Photon-Example/blob/master/Assets/Scripts/Extensions/PlayerExtension.cs

You can find the photon defaults in the PhotonNetwork folder under Utilities or something

jgonzosan
2018-09-19 03:05:11

Ok I can't seem to catch a break on this. Even without using properties my text still gets swapped. At first I thought it was the player list, but it doesn't seem to be that either. I'm accessing a list of text elements by the player ID. Does the player ID change based on whether you're the client or Master? This is really frustrating.

At this point I just need two text elements to represent player 1 and player 2 with their respective points. They don't need to be moved based on their order, they just need to be accurate. Can't seem to figure this out. I can update points just fine, just not sure how I'm supposed to ensure the points are accurate.

S_Oliver
2018-09-19 05:10:47

Oh i missed one Point to mention
This sorts the List after Score.

  PhotonPlayer[] pList = PhotonNetwork.playerList;  
        System.Array.Sort(pList, delegate (PhotonPlayer p1, PhotonPlayer p2) { return p1.GetCurrentScore().CompareTo(p2.GetScore()); });  
        System.Array.Reverse(pList);

and
this sorts after ID so the list is always on each client the same, hopefully :D

  
System.Array.Sort(pList, delegate (PhotonPlayer p1, PhotonPlayer p2) { return p1.ID.CompareTo(p2.ID); });  

edit:
quick test
PlayerList is sorted after ID on 4 Clients
https://imgur.com/a/XOdc37W

jgonzosan
2018-09-19 12:21:52

Thank you, I'll give that a try!

jgonzosan
2018-09-20 04:11:34

Well that seemed to do the trick @S_Oliver, thanks once again for all your help!

Back to top