144 lines
No EOL
5.1 KiB
C#
144 lines
No EOL
5.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
/// <summary>
|
|
/// class that loads leaderboard data and displays it in the UI
|
|
/// </summary>
|
|
public class LeaderboardUI : MonoBehaviour
|
|
{
|
|
/// <summary>
|
|
/// maximum number of entries to display in the leaderboard
|
|
/// </summary>
|
|
public const int MaxEntries = 50;
|
|
|
|
/// <summary>
|
|
/// uxml template for a leaderboard entry
|
|
/// </summary>
|
|
[SerializeField] private VisualTreeAsset leaderboardEntryTemplate;
|
|
|
|
/// <summary>
|
|
/// leaderboard data
|
|
/// </summary>
|
|
private List<LeaderboardEntry> _leaderboardData = new(MaxEntries);
|
|
|
|
/// <summary>
|
|
/// reference to the leaderboard scroll view
|
|
/// </summary>
|
|
private ScrollView _leaderboardScrollView;
|
|
|
|
/// <summary>
|
|
/// register callbacks
|
|
/// </summary>
|
|
private void OnEnable()
|
|
{
|
|
UIManager.Instance.RegisterOnDisplayStateChangeCallback((_, newState) =>
|
|
{
|
|
if (newState == UIManager.DisplayState.LeaderboardView) LoadLeaderboardData();
|
|
});
|
|
|
|
_leaderboardScrollView = UIManager.Instance.UI.Q<ScrollView>("LeaderboardListContent");
|
|
if (_leaderboardScrollView == null)
|
|
throw new NullReferenceException("leaderboard scroll view not found in the UI");
|
|
}
|
|
|
|
/// <summary>
|
|
/// load leaderboard data from the backend
|
|
/// </summary>
|
|
private void LoadLeaderboardData()
|
|
{
|
|
if (GameManager.Instance.Backend.Status != Backend.FirebaseConnectionStatus.Connected)
|
|
{
|
|
Debug.LogError("attempted to load leaderboard data without a connection to the backend");
|
|
RenderLeaderboardData("Not connected to the backend, can't load leaderboard data.");
|
|
return;
|
|
}
|
|
|
|
GameManager.Instance.Backend.GetLeaderboard((result, entries) =>
|
|
{
|
|
if (result == Backend.TransactionResult.Ok)
|
|
{
|
|
_leaderboardData = entries;
|
|
RenderLeaderboardData();
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("failed to load leaderboard data");
|
|
RenderLeaderboardData("An error occured, couldn't load leaderboard data.");
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// render leaderboard data in the UI
|
|
/// </summary>
|
|
/// <param name="message">message to display in the leaderboard in lieu of actual data</param>
|
|
/// <exception cref="NullReferenceException">
|
|
/// thrown when the leaderboard scroll view is missing or when the leaderboard
|
|
/// entry
|
|
/// </exception>
|
|
private void RenderLeaderboardData(string message = "")
|
|
{
|
|
_leaderboardScrollView.Clear();
|
|
|
|
if (!string.IsNullOrEmpty(message))
|
|
{
|
|
Debug.Log($"rendering leaderboard with message entry-in-lieu: '{message}'");
|
|
_leaderboardScrollView.Add(BuildEntryElement("", message, ""));
|
|
return;
|
|
}
|
|
|
|
Debug.Log($"rendering {_leaderboardData.Count} leaderboard entries");
|
|
foreach (var (entry, index) in _leaderboardData.Take(MaxEntries).Reverse()
|
|
.Select((entry, index) => (entry, index)))
|
|
_leaderboardScrollView.Add(BuildEntryElement((index + 1).ToString(), entry.Username, $"{entry.Rating:F3}"));
|
|
}
|
|
|
|
/// <summary>
|
|
/// build a leaderboard entry element
|
|
/// </summary>
|
|
/// <param name="position">leaderboard position as a string</param>
|
|
/// <param name="username">score holder's username</param>
|
|
/// <param name="rating">score holder's rating</param>
|
|
/// <returns>a visual element representing a leaderboard entry</returns>
|
|
/// <exception cref="NullReferenceException">thrown when the leaderboard entry template is missing required elements</exception>
|
|
private TemplateContainer BuildEntryElement(string position, string username, string rating)
|
|
{
|
|
var template = leaderboardEntryTemplate.Instantiate();
|
|
var templatePositionText = template.Q<Label>("EntryRankPosition");
|
|
var templateUsernameText = template.Q<Label>("EntryNameText");
|
|
var templateRatingText = template.Q<Label>("EntryRatingText");
|
|
|
|
if (templatePositionText == null || templateUsernameText == null || templateRatingText == null)
|
|
throw new NullReferenceException("leaderboard entry template is missing required elements");
|
|
|
|
templatePositionText.text = position;
|
|
templateUsernameText.text = username;
|
|
templateRatingText.text = rating;
|
|
|
|
return template;
|
|
}
|
|
}
|
|
|
|
public readonly struct LeaderboardEntry
|
|
{
|
|
public readonly string Username;
|
|
public readonly float Rating;
|
|
|
|
// public LeaderboardEntry(string username, float rating)
|
|
// {
|
|
// Username = username;
|
|
// Rating = rating;
|
|
// }
|
|
//
|
|
public LeaderboardEntry(Dictionary<string, object> data)
|
|
{
|
|
if (!data.ContainsKey("username") || data["username"] is not string username)
|
|
throw new ArgumentException("data['username'] not found or invalid");
|
|
|
|
Username = username;
|
|
Rating = LocalPlayerData.GetFloatyKey(data, "rating");
|
|
}
|
|
} |