Basic Scoreboard

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

  • 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.
  • 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?
  • 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?
  • 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 said:
    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!
  • Yeah i uploaded the Video yesterday and the git repo today :D
  • 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.
  • 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.
  • 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.
  • 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?
  • Are you using PhotonNetwork.playerList ? I think its not in the same order on different clients.
  • 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?
  • 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.
  • 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
    jgonzosan
    edited September 2018
    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
    S_Oliver ✭✭✭
    edited September 2018
    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

  • Thank you, I'll give that a try!
  • Well that seemed to do the trick @S_Oliver, thanks once again for all your help!