feat: complete scoreboard

Add log in tip, loading screen & player record highlighting
This commit is contained in:
cuqmbr 2022-07-28 21:22:15 +03:00
parent 6a5831ef55
commit 662b65a605
7 changed files with 635 additions and 1056 deletions

View File

@ -73,6 +73,34 @@ AnimationClip:
path: Restart Button path: Restart Button
classID: 1 classID: 1
script: {fileID: 0} script: {fileID: 0}
- curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0.5019608
inSlope: 0
outSlope: 0
tangentMode: 136
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 136
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
attribute: m_EffectColor.a
path: Scoreboard Window Image
classID: 114
script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3}
m_PPtrCurves: [] m_PPtrCurves: []
m_SampleRate: 60 m_SampleRate: 60
m_WrapMode: 0 m_WrapMode: 0
@ -95,6 +123,13 @@ AnimationClip:
typeID: 1 typeID: 1
customType: 0 customType: 0
isPPtrCurve: 0 isPPtrCurve: 0
- serializedVersion: 2
path: 72073735
attribute: 1760254681
script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3}
typeID: 114
customType: 0
isPPtrCurve: 0
pptrCurveMapping: [] pptrCurveMapping: []
m_AnimationClipSettings: m_AnimationClipSettings:
serializedVersion: 2 serializedVersion: 2
@ -173,6 +208,34 @@ AnimationClip:
path: Restart Button path: Restart Button
classID: 1 classID: 1
script: {fileID: 0} script: {fileID: 0}
- curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0.5019608
inSlope: 0
outSlope: 0
tangentMode: 136
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 136
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
attribute: m_EffectColor.a
path: Scoreboard Window Image
classID: 114
script: {fileID: 11500000, guid: e19747de3f5aca642ab2be37e372fb86, type: 3}
m_EulerEditorCurves: [] m_EulerEditorCurves: []
m_HasGenericRootTransform: 0 m_HasGenericRootTransform: 0
m_HasMotionFloatCurves: 0 m_HasMotionFloatCurves: 0

File diff suppressed because it is too large Load Diff

View File

@ -107,11 +107,16 @@ public class ScoreManager : MonoBehaviour
_highScore = _currentScore; _highScore = _currentScore;
SessionStore.HighScore = _highScore; SessionStore.HighScore = _highScore;
if (SessionStore.UserData == null)
{
return;
}
await HttpClient.Post<ScoreboardRecordDto>( await HttpClient.Post<ScoreboardRecordDto>(
$"{SessionStore.ApiUrl}/scoreboard", $"{SessionStore.ApiUrl}/scoreboard",
new ScoreboardRecordDto new ScoreboardRecordDto
{ {
PostTime = DateTime.UtcNow, Score = SessionStore.HighScore, PostTime = DateTime.UtcNow, Score = _highScore,
User = SessionStore.UserData.ToDto() User = SessionStore.UserData.ToDto()
}); });
} }
@ -136,7 +141,7 @@ public class ScoreManager : MonoBehaviour
break; break;
case GameState.GameOver: case GameState.GameOver:
await SaveHighScore(); await SaveHighScore();
_scoreboardManager.SpawnScoreboardRecords(); await _scoreboardManager.SpawnScoreboardRecords();
break; break;
default: default:
throw new ArgumentOutOfRangeException(nameof(newGameState), newGameState, null); throw new ArgumentOutOfRangeException(nameof(newGameState), newGameState, null);

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DatabaseModels.DataTransferObjets; using DatabaseModels.DataTransferObjets;
using TMPro; using TMPro;
@ -21,9 +22,12 @@ public class UIManager : MonoBehaviour
[Header("Scoreboard")] [Header("Scoreboard")]
[SerializeField] private RectTransform _scoreboardScrollViewContent; [SerializeField] private RectTransform _scoreboardScrollViewContent;
[SerializeField] public GameObject _scoreboardLoadingScreen;
[SerializeField] public GameObject _scoreboardLoginTip;
[SerializeField] private GameObject _scoreboardRecordPrefab; [SerializeField] private GameObject _scoreboardRecordPrefab;
[SerializeField] private Color _scoreboardRecordColor1; [SerializeField] private Color _scoreboardRecordColor1;
[SerializeField] private Color _scoreboardRecordColor2; [SerializeField] private Color _scoreboardRecordColor2;
[SerializeField] private Color _scoreboardPlayerRecordColor;
private void Awake() private void Awake()
@ -63,17 +67,38 @@ public class UIManager : MonoBehaviour
isFull = Math.Abs(_experienceSlider.value - _experienceSlider.maxValue) < 0.1f; isFull = Math.Abs(_experienceSlider.value - _experienceSlider.maxValue) < 0.1f;
} }
public void InstantiateScoreboardRecords(ScoreboardRecordDto[] records) public void InstantiateScoreboardRecords(ScoreboardRecordDto[] records, int firstRecordIndex)
{ {
_scoreboardScrollViewContent.sizeDelta = new Vector2(0, records.Length * 100); _scoreboardScrollViewContent.sizeDelta = new Vector2(0, records.Length * 100);
_scoreboardScrollViewContent.localPosition = new Vector3(0, _scoreboardScrollViewContent.sizeDelta.y / records.Length * 2f);
float yPos = 0;
if (records.Last().User.Username == SessionStore.UserData.Username)
{
yPos = _scoreboardScrollViewContent.sizeDelta.y / records.Length / 100;
}
else
{
yPos = _scoreboardScrollViewContent.sizeDelta.y / records.Length * 2f;
}
_scoreboardScrollViewContent.localPosition = new Vector3(0, yPos);
for (int i = 0; i < records.Length; i++) for (int i = 0; i < records.Length; i++)
{ {
var record = Instantiate(_scoreboardRecordPrefab, Vector3.zero, Quaternion.identity, _scoreboardScrollViewContent.transform); var record = Instantiate(_scoreboardRecordPrefab, Vector3.zero, Quaternion.identity, _scoreboardScrollViewContent.transform);
record.GetComponent<RectTransform>().localPosition = new Vector2(218, -50 - 100 * i); record.GetComponent<RectTransform>().localPosition = new Vector2(218, -50 - 100 * i);
record.GetComponent<Image>().color = i % 2 == 0 ? _scoreboardRecordColor1 : _scoreboardRecordColor2;
record.GetComponentInChildren<TextMeshProUGUI>().text = $"{i + 1}. {records[i].User.Username}: {records[i].Score}"; if (records[i].User.Username == SessionStore.UserData.Username)
{
record.GetComponent<Image>().color = _scoreboardPlayerRecordColor;
}
else
{
record.GetComponent<Image>().color = i % 2 == 0 ? _scoreboardRecordColor1 : _scoreboardRecordColor2;
}
record.GetComponentInChildren<TextMeshProUGUI>().text = $"{firstRecordIndex + i + 1}. {records[i].User.Username}: {records[i].Score}";
} }
} }
@ -81,7 +106,7 @@ public class UIManager : MonoBehaviour
{ {
for (int i = 0; i < _scoreboardScrollViewContent.transform.childCount; i++) for (int i = 0; i < _scoreboardScrollViewContent.transform.childCount; i++)
{ {
Destroy(_scoreboardScrollViewContent.transform.GetChild(i).gameObject); DestroyImmediate(_scoreboardScrollViewContent.transform.GetChild(i).gameObject);
} }
} }

View File

@ -3,39 +3,41 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DatabaseModels.DataTransferObjets; using DatabaseModels.DataTransferObjets;
using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
public class ScoreboardManager : MonoBehaviour public class ScoreboardManager : MonoBehaviour
{ {
[SerializeField] private UIManager _uiManager; [SerializeField] private UIManager _uiManager;
public async void SpawnScoreboardRecords() public async Task SpawnScoreboardRecords()
{ {
if (SessionStore.UserData == null) if (SessionStore.UserData == null)
{ {
// Login to display online scoreboard _uiManager._scoreboardLoginTip.SetActive(true);
return; return;
} }
var filteredScoreboardRecords = await GetFilteredScoreboardRecords(); _uiManager._scoreboardLoadingScreen.SetActive(true);
_uiManager.DestroyAllScoreboardRecords(); _uiManager.DestroyAllScoreboardRecords();
_uiManager.InstantiateScoreboardRecords(filteredScoreboardRecords); var filteredScoreboard = await GetFilteredScoreboard();
_uiManager.InstantiateScoreboardRecords(filteredScoreboard.records, filteredScoreboard.firstRecordIndex);
_uiManager._scoreboardLoadingScreen.SetActive(false);
async Task<ScoreboardRecordDto[]> GetFilteredScoreboardRecords() async Task<(ScoreboardRecordDto[] records, int firstRecordIndex)> GetFilteredScoreboard()
{ {
int spareCount = 5;
var localRecords = await HttpClient.Get<List<ScoreboardRecordDto>>($"{SessionStore.ApiUrl}/scoreboard"); var localRecords = await HttpClient.Get<List<ScoreboardRecordDto>>($"{SessionStore.ApiUrl}/scoreboard");
var currentUserRecord = localRecords.First(r => r.User.Username == SessionStore.UserData.Username); var currentUserRecord = localRecords.First(r => r.User.Username == SessionStore.UserData.Username);
var firstRecordNum = Mathf.Clamp(localRecords.IndexOf(currentUserRecord) - spareCount, 0, localRecords.Count);
var filteredRecords = localRecords var filteredRecords = localRecords
.SkipWhile(r => Math.Abs(localRecords.IndexOf(r) - localRecords.IndexOf(currentUserRecord)) >= 5) .SkipWhile(r => Math.Abs(localRecords.IndexOf(r) - localRecords.IndexOf(currentUserRecord)) >= spareCount)
.TakeWhile(r => Math.Abs(localRecords.IndexOf(r) - localRecords.IndexOf(currentUserRecord)) <= 5) .TakeWhile(r => Math.Abs(localRecords.IndexOf(r) - localRecords.IndexOf(currentUserRecord)) <= spareCount)
.ToArray(); .ToArray();
return filteredRecords.ToArray(); return (filteredRecords, firstRecordNum);
} }
} }
} }

View File

@ -1,15 +1,16 @@
using System.Linq;
using DatabaseModels.DataTransferObjets; using DatabaseModels.DataTransferObjets;
using DatabaseModels.Requests;
using DatabaseModels.Responses;
public static class SessionStore public static class SessionStore
{ {
public static string ApiUrl { get; set; } public static string ApiUrl { get; set; }
public static string Jwt { get; set; }
public static UserData UserData { get; set; } public static UserData UserData { get; set; }
public static int HighScore { get; set; } public static int HighScore { get; set; }
public static ScoreboardRecordDto[] ScoreboardRecords;
public static async void Save() public static async void Save()
{ {
await SaveSystem.SaveToJsonAsync("userData.json", UserData); await SaveSystem.SaveToJsonAsync("userData.json", UserData);
@ -21,17 +22,18 @@ public static class SessionStore
UserData = await SaveSystem.LoadFromJsonAsync<UserData>("userData.json"); UserData = await SaveSystem.LoadFromJsonAsync<UserData>("userData.json");
HighScore = await SaveSystem.LoadFromBinaryAsync<int>("HighScore.bin"); HighScore = await SaveSystem.LoadFromBinaryAsync<int>("HighScore.bin");
ScoreboardRecords = await HttpClient.Get<ScoreboardRecordDto[]>($"{ApiUrl}/scoreboard");
if (UserData == null) if (UserData == null)
{ {
return; return;
} }
int? dbHighScore = ScoreboardRecords?.FirstOrDefault(sbr => sbr.User.Username == UserData.Username)?.Score; var authResponse = await HttpClient.Post<AuthenticationResponse>($"{ApiUrl}/auth/login", new AuthenticationRequest { Username = UserData.Username, Password = UserData.Password } );
if (dbHighScore != null && dbHighScore > HighScore) Jwt = authResponse.Token;
var dbHighScore = await HttpClient.Get<ScoreboardRecordDto>($"{ApiUrl}/scoreboard/{UserData.Username}");
if (dbHighScore?.Score != null && dbHighScore.Score > HighScore)
{ {
HighScore = (int) dbHighScore; HighScore = dbHighScore.Score;
} }
} }
} }

View File

@ -2,12 +2,11 @@ using System;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
public static class HttpClient public static class HttpClient
{ {
private static string _jwt = "";
public static async Task<T> Get<T>(string endpoint) public static async Task<T> Get<T>(string endpoint)
{ {
var getRequest = CreateRequest(endpoint, RequestType.GET); var getRequest = CreateRequest(endpoint, RequestType.GET);
@ -22,8 +21,15 @@ public static class HttpClient
{ {
return JsonConvert.DeserializeObject<T>(getRequest.downloadHandler.text); return JsonConvert.DeserializeObject<T>(getRequest.downloadHandler.text);
} }
catch (Exception) catch (Exception e)
{ {
Debug.LogWarning($"HttpClient: GET from {endpoint}" +
$"\nSent object type: {typeof(T)}" +
$"\nError: {getRequest.error}" +
$"\n\n" +
$"See details in a exception logged below." +
$"\n\n");
Debug.LogException(e);
return default(T); return default(T);
} }
} }
@ -43,12 +49,22 @@ public static class HttpClient
await Task.Delay(10); await Task.Delay(10);
} }
return JsonConvert.DeserializeObject<T>(postRequest.downloadHandler.text); try
} {
return JsonConvert.DeserializeObject<T>(postRequest.downloadHandler.text);
public static void SetJwt(string jwt) }
{ catch (Exception e)
_jwt = jwt; {
Debug.LogWarning($"HttpClient: POST to {endpoint}" +
$"\nSent object type: {payload.GetType()}" +
$"\nRetrieved object type: {typeof(T)}" +
$"\nError: {postRequest.error}" +
$"\n\n" +
$"See details in a exception logged below." +
$"\n\n");
Debug.LogException(e);
return default(T);
}
} }
private static UnityWebRequest CreateRequest(string path, RequestType type, object data = null) private static UnityWebRequest CreateRequest(string path, RequestType type, object data = null)
@ -64,9 +80,9 @@ public static class HttpClient
request.downloadHandler = new DownloadHandlerBuffer(); request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("Content-Type", "application/json");
if (_jwt != null) if (SessionStore.Jwt != null)
{ {
request.SetRequestHeader("Authorization", $"Bearer {_jwt}"); request.SetRequestHeader("Authorization", $"Bearer {SessionStore.Jwt}");
} }
request.certificateHandler = new CertificateWhore(); request.certificateHandler = new CertificateWhore();