This repository has been archived on 2024-11-20. You can view files and clone it, but cannot push or open issues or pull requests.
colourmeok/ColourMeOKGame/Assets/Scripts/LeaderboardUI.cs

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");
}
}