Learn to Create a Leaderboard Display
Hello everyone, I will now teach you how to use PlayFab in Unity 2019 to create a visual display for your leaderboard data. In the last lesson, I showed you how to get all the data and players of a chosen leaderboard in your game. We were not able to create any visual displays for this leaderboard and so that is what we will do in this next video. We will create a new UI panel to be the display of our leaderboard.
Working in Unity
To get started we will open our PlayFab Unity project. Once opened we will first create a new UI panel. We will then add a UI text object to be the title of this panel. We then want to create a container for all the different player listing of our leaderboard. To do this we will create a new UI image and size it to be like a window on our panel. We can also change the color so it stands out. Next, we need to add a mask and ScrollRect component to this GameObject. We will create a second UI image and make it a child to our ScrollRect object. For this image will make it the same width as the parent object by we will make taller so that it hangs down below the ScrollRect. We can then make this object transparent and Finally, we will attach a vertical layout group component to this object.
Create a Player Listing Prefab
Now we need to create the object that will be the listing of each player in the leaderboard. This will be made up of a UI image and two text object as children to the image. You want the image to be almost as wide as the ScrollRect. You will then position the text objects, one at each end. We then need to create a new C# script to attach to this listing object. The script only needs two public Text variables and you can remove the Start and Update functions. Once you have this script create you will need to drag it into the inspector attaching it to our listing Game Object. Then set the two variable to the two text objects of our listing object. Now we can make a new prefab object out of this object. We will create a new folder called Prefabs. We will then drag our listing object into this folder, after which we can delete this listing object from our hierarchy. Lastly, we will create a new UI button for our close button. Make the button into a square and move it to the corner of the leaderboard panel.
C# Programming
Now we need to go to our PlayFabController C# script. We first need to add some new variables. We will need a GameObject variable for our new leaderboard panel. Next we need a GameObject variable for our prefab listing and finally, we need a Transform variable for our container. We will then go to our success callback function for getting the leaderboard data. First set our leaderboard panel variable to active then inside the foreach loop we want to instantiate a new prefab listing for each player and make this object a child to our transform variable. We will then need to get the script attached to each listing object. From here we can then set the text value to our player’s display name and our player’s stat value.
This last thing we need to do in this script is create a public function that we can pair to our close button. Inside this function, we will disable our leaderboard panel. we will the for loop through all the children of the transform variable backward. Inside the for loop, we will destroy each child object.
More Unity
We can now save our script and go back to Unity. Once back in Unity we will need to assign our close panel function to our close button and then we need to set the three new variables of our PlayFabController script. Now we should be able to play our game and test it out. With your game in play mode, you should be able to click the leaderboard button. This should activate your leaderboard panel and populate the panel with all the player listing of the leaderboard data requested. If you then click the close button it should hide the leaderboard panel and destroy all the player listings.
Testing your Unity Project
Thanks for watching this tutorial. If you found it to be helpful, then please give it a like and tell your friends. Join our discord if you have any questions and please subscribe to our channel.
Unlock Code and Member Content
LeaderboardListing.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class LearderboardListing : MonoBehaviour { public Text playerNameText; public Text playerScoreText; }
PlayFabController.cs
using PlayFab; using PlayFab.ClientModels; using UnityEngine; using PlayFab.DataModels; using PlayFab.ProfilesModels; using System.Collections.Generic; using PlayFab.Json; public class PlayFabController : MonoBehaviour { public static PlayFabController PFC; private string userEmail; private string userPassword; private string username; public GameObject loginPanel; public GameObject addLoginPanel; public GameObject recoverButton; private void OnEnable() { if(PlayFabController.PFC == null) { PlayFabController.PFC = this; } else { if(PlayFabController.PFC != this) { Destroy(this.gameObject); } } DontDestroyOnLoad(this.gameObject); } public void Start() { //Note: Setting title Id here can be skipped if you have set the value in Editor Extensions already. if (string.IsNullOrEmpty(PlayFabSettings.TitleId)) { PlayFabSettings.TitleId = "8741"; // Please change this value to your own titleId from PlayFab Game Manager } //PlayerPrefs.DeleteAll(); //var request = new LoginWithCustomIDRequest { CustomId = "GettingStartedGuide", CreateAccount = true }; //PlayFabClientAPI.LoginWithCustomID(request, OnLoginSuccess, OnLoginFailure); if (PlayerPrefs.HasKey("EMAIL")) { userEmail = PlayerPrefs.GetString("EMAIL"); userPassword = PlayerPrefs.GetString("PASSWORD"); var request = new LoginWithEmailAddressRequest { Email = userEmail, Password = userPassword }; PlayFabClientAPI.LoginWithEmailAddress(request, OnLoginSuccess, OnLoginFailure); } else { #if UNITY_ANDROID var requestAndroid = new LoginWithAndroidDeviceIDRequest { AndroidDeviceId = ReturnMobileID(), CreateAccount = true }; PlayFabClientAPI.LoginWithAndroidDeviceID(requestAndroid, OnLoginMobileSuccess, OnLoginMobileFailure); #endif #if UNITY_IOS var requestIOS = new LoginWithIOSDeviceIDRequest { DeviceId = ReturnMobileID(), CreateAccount = true }; PlayFabClientAPI.LoginWithIOSDeviceID(requestIOS, OnLoginMobileSuccess, OnLoginMobileFailure); #endif } } #region Login private void OnLoginSuccess(LoginResult result) { Debug.Log("Congratulations, you made your first successful API call!"); PlayerPrefs.SetString("EMAIL", userEmail); PlayerPrefs.SetString("PASSWORD", userPassword); loginPanel.SetActive(false); recoverButton.SetActive(false); GetStats(); //StartCloudHelloWorld(); } private void OnLoginMobileSuccess(LoginResult result) { Debug.Log("Congratulations, you made your first successful API call!"); GetStats(); loginPanel.SetActive(false); } private void OnRegisterSuccess(RegisterPlayFabUserResult result) { Debug.Log("Congratulations, you made your first successful API call!"); PlayerPrefs.SetString("EMAIL", userEmail); PlayerPrefs.SetString("PASSWORD", userPassword); PlayFabClientAPI.UpdateUserTitleDisplayName(new UpdateUserTitleDisplayNameRequest { DisplayName = username }, OnDisplayName, OnLoginMobileFailure); GetStats(); loginPanel.SetActive(false); } void OnDisplayName(UpdateUserTitleDisplayNameResult result) { Debug.Log(result.DisplayName + " is your new display name"); } private void OnLoginFailure(PlayFabError error) { var registerRequest = new RegisterPlayFabUserRequest { Email = userEmail, Password = userPassword, Username = username }; PlayFabClientAPI.RegisterPlayFabUser(registerRequest, OnRegisterSuccess, OnRegisterFailure); } private void OnLoginMobileFailure(PlayFabError error) { Debug.Log(error.GenerateErrorReport()); } private void OnRegisterFailure(PlayFabError error) { Debug.LogError(error.GenerateErrorReport()); } public void GetUserEmail(string emailIn) { userEmail = emailIn; } public void GetUserPassword(string passwordIn) { userPassword = passwordIn; } public void GetUsername(string usernameIn) { username = usernameIn; } public void OnClickLogin() { var request = new LoginWithEmailAddressRequest { Email = userEmail, Password = userPassword }; PlayFabClientAPI.LoginWithEmailAddress(request, OnLoginSuccess, OnLoginFailure); } public static string ReturnMobileID() { string deviceID = SystemInfo.deviceUniqueIdentifier; return deviceID; } public void OpenAddLogin() { addLoginPanel.SetActive(true); } public void OnClickAddLogin() { var addLoginRequest = new AddUsernamePasswordRequest { Email = userEmail, Password = userPassword, Username = username }; PlayFabClientAPI.AddUsernamePassword(addLoginRequest, OnAddLoginSuccess, OnRegisterFailure); } private void OnAddLoginSuccess(AddUsernamePasswordResult result) { Debug.Log("Congratulations, you made your first successful API call!"); GetStats(); PlayerPrefs.SetString("EMAIL", userEmail); PlayerPrefs.SetString("PASSWORD", userPassword); addLoginPanel.SetActive(false); } #endregion Login public int playerLevel; public int gameLevel; public int playerHealth; public int playerDamage; public int playerHighScore; #region PlayerStats public void SetStats() { PlayFabClientAPI.UpdatePlayerStatistics(new UpdatePlayerStatisticsRequest { // request.Statistics is a list, so multiple StatisticUpdate objects can be defined if required. Statistics = new List<StatisticUpdate> { new StatisticUpdate { StatisticName = "PlayerLevel", Value = playerLevel }, new StatisticUpdate { StatisticName = "GameLevel", Value = gameLevel }, new StatisticUpdate { StatisticName = "PlayerHealth", Value = playerHealth }, new StatisticUpdate { StatisticName = "PlayerDamage", Value = playerDamage }, new StatisticUpdate { StatisticName = "PlayerHighScore", Value = playerHighScore }, } }, result => { Debug.Log("User statistics updated"); }, error => { Debug.LogError(error.GenerateErrorReport()); }); } void GetStats() { PlayFabClientAPI.GetPlayerStatistics( new GetPlayerStatisticsRequest(), OnGetStats, error => Debug.LogError(error.GenerateErrorReport()) ); } void OnGetStats(GetPlayerStatisticsResult result) { Debug.Log("Received the following Statistics:"); foreach (var eachStat in result.Statistics) { Debug.Log("Statistic (" + eachStat.StatisticName + "): " + eachStat.Value); switch(eachStat.StatisticName) { case "PlayerLevel": playerLevel = eachStat.Value; break; case "GameLevel": gameLevel = eachStat.Value; break; case "PlayerHealth": playerHealth = eachStat.Value; break; case "PlayerDamage": playerDamage = eachStat.Value; break; case "PlayerHighScore": playerHighScore = eachStat.Value; break; } } } // Build the request object and access the API public void StartCloudUpdatePlayerStats() { PlayFabClientAPI.ExecuteCloudScript(new ExecuteCloudScriptRequest() { FunctionName = "UpdatePlayerStats", // Arbitrary function name (must exist in your uploaded cloud.js file) FunctionParameter = new { Level = playerLevel, highScore =playerHighScore, apple = 0 }, // The parameter provided to your function GeneratePlayStreamEvent = true, // Optional - Shows this event in PlayStream }, OnCloudUpdateStats, OnErrorShared); } // OnCloudHelloWorld defined in the next code block private static void OnCloudUpdateStats(ExecuteCloudScriptResult result) { // Cloud Script returns arbitrary results, so you have to evaluate them one step and one parameter at a time Debug.Log(JsonWrapper.SerializeObject(result.FunctionResult)); JsonObject jsonResult = (JsonObject)result.FunctionResult; object messageValue; jsonResult.TryGetValue("messageValue", out messageValue); // note how "messageValue" directly corresponds to the JSON values set in Cloud Script Debug.Log((string)messageValue); } private static void OnErrorShared(PlayFabError error) { Debug.Log(error.GenerateErrorReport()); } #endregion PlayerStats public GameObject leaderboardPanel; public GameObject listingPrefab; public Transform listingContainer; #region Leaderboard public void GetLeaderboarder() { var requestLeaderboard = new GetLeaderboardRequest { StartPosition = 0, StatisticName = "PlayerHighScore", MaxResultsCount = 20 }; PlayFabClientAPI.GetLeaderboard(requestLeaderboard, OnGetLeadboard, OnErrorLeaderboard); } void OnGetLeadboard(GetLeaderboardResult result) { leaderboardPanel.SetActive(true); //Debug.Log(result.Leaderboard[0].StatValue); foreach(PlayerLeaderboardEntry player in result.Leaderboard) { GameObject tempListing = Instantiate(listingPrefab, listingContainer); LearderboardListing LL = tempListing.GetComponent<LearderboardListing>(); LL.playerNameText.text = player.DisplayName; LL.playerScoreText.text = player.StatValue.ToString(); Debug.Log(player.DisplayName + ": " + player.StatValue); } } public void CloseLeaderboardPanel() { leaderboardPanel.SetActive(false); for(int i = listingContainer.childCount - 1; i >= 0; i--) { Destroy(listingContainer.GetChild(i).gameObject); } } void OnErrorLeaderboard(PlayFabError error) { Debug.LogError(error.GenerateErrorReport()); } #endregion Leaderboard }
using Photon.Chat; using Photon.Pun; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class PhotonChatManager : MonoBehaviour, IChatClientListener { #region Setup [SerializeField] GameObject joinChatButton; ChatClient chatClient; bool isConnected; [SerializeField] string username; public void UsernameOnValueChange(string valueIn) { username = valueIn; } public void ChatConnectOnClick() { isConnected = true; chatClient = new ChatClient(this); //chatClient.ChatRegion = "US"; chatClient.Connect(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdChat, PhotonNetwork.AppVersion, new AuthenticationValues(username)); Debug.Log("Connenting"); } #endregion Setup #region General [SerializeField] GameObject chatPanel; string privateReceiver = ""; string currentChat; [SerializeField] InputField chatField; [SerializeField] Text chatDisplay; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if (isConnected) { chatClient.Service(); } if (chatField.text != "" && Input.GetKey(KeyCode.Return)) { SubmitPublicChatOnClick(); SubmitPrivateChatOnClick(); } } #endregion General #region PublicChat public void SubmitPublicChatOnClick() { if (privateReceiver == "") { chatClient.PublishMessage("RegionChannel", currentChat); chatField.text = ""; currentChat = ""; } } public void TypeChatOnValueChange(string valueIn) { currentChat = valueIn; } #endregion PublicChat #region PrivateChat public void ReceiverOnValueChange(string valueIn) { privateReceiver = valueIn; } public void SubmitPrivateChatOnClick() { if (privateReceiver != "") { chatClient.SendPrivateMessage(privateReceiver, currentChat); chatField.text = ""; currentChat = ""; } } #endregion PrivateChat #region Callbacks public void DebugReturn(DebugLevel level, string message) { //throw new System.NotImplementedException(); } public void OnChatStateChange(ChatState state) { if(state == ChatState.Uninitialized) { isConnected = false; joinChatButton.SetActive(true); chatPanel.SetActive(false); } } public void OnConnected() { Debug.Log("Connected"); joinChatButton.SetActive(false); chatClient.Subscribe(new string[] { "RegionChannel" }); } public void OnDisconnected() { isConnected = false; joinChatButton.SetActive(true); chatPanel.SetActive(false); } public void OnGetMessages(string channelName, string[] senders, object[] messages) { string msgs = ""; for (int i = 0; i < senders.Length; i++) { msgs = string.Format("{0}: {1}", senders[i], messages[i]); chatDisplay.text += "\n" + msgs; Debug.Log(msgs); } } public void OnPrivateMessage(string sender, object message, string channelName) { string msgs = ""; msgs = string.Format("(Private) {0}: {1}", sender, message); chatDisplay.text += "\n " + msgs; Debug.Log(msgs); } public void OnStatusUpdate(string user, int status, bool gotMessage, object message) { throw new System.NotImplementedException(); } public void OnSubscribed(string[] channels, bool[] results) { chatPanel.SetActive(true); } public void OnUnsubscribed(string[] channels) { throw new System.NotImplementedException(); } public void OnUserSubscribed(string channel, string user) { throw new System.NotImplementedException(); } public void OnUserUnsubscribed(string channel, string user) { throw new System.NotImplementedException(); } #endregion Callbacks }