game: gameplay + database interim 2
This commit is contained in:
parent
cc79a5b1a0
commit
a338a62f27
|
@ -515,8 +515,44 @@ public void ForgotPassword(string email, Action<bool> callback)
|
|||
/// </param>
|
||||
public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerData.Score>> callback)
|
||||
{
|
||||
// TODO: implement this
|
||||
callback(DatabaseTransactionResult.Error, new List<LocalPlayerData.Score>(0));
|
||||
if (!Status.Equals(FirebaseConnectionStatus.Connected)) return;
|
||||
|
||||
if (_user == null)
|
||||
{
|
||||
callback(DatabaseTransactionResult.Unauthenticated, new List<LocalPlayerData.Score>(0));
|
||||
return;
|
||||
}
|
||||
|
||||
_db.Child("scores")
|
||||
.OrderByChild("timestamp")
|
||||
.LimitToLast(LocalPlayerData.MaxBestOnlineScores)
|
||||
.GetValueAsync()
|
||||
.ContinueWithOnMainThread(task =>
|
||||
{
|
||||
if (!task.IsCompletedSuccessfully)
|
||||
{
|
||||
Debug.LogError(task.Exception);
|
||||
callback(DatabaseTransactionResult.Error, new List<LocalPlayerData.Score>(0));
|
||||
return;
|
||||
}
|
||||
|
||||
var scores = new List<LocalPlayerData.Score>();
|
||||
foreach (var child in task.Result.Children)
|
||||
{
|
||||
try
|
||||
{
|
||||
var score = new LocalPlayerData.Score(child.Value as Dictionary<string, object>);
|
||||
scores.Add(score);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
|
||||
callback(DatabaseTransactionResult.Ok, scores);
|
||||
GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -528,8 +564,44 @@ public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerDa
|
|||
/// </param>
|
||||
public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData.Score>> callback)
|
||||
{
|
||||
// TODO: implement this
|
||||
callback(DatabaseTransactionResult.Error, new List<LocalPlayerData.Score>(0));
|
||||
if (!Status.Equals(FirebaseConnectionStatus.Connected)) return;
|
||||
|
||||
if (_user == null)
|
||||
{
|
||||
callback(DatabaseTransactionResult.Unauthenticated, new List<LocalPlayerData.Score>(0));
|
||||
return;
|
||||
}
|
||||
|
||||
_db.Child("scores")
|
||||
.OrderByChild("avgPerceivedAccuracy")
|
||||
.LimitToLast(LocalPlayerData.MaxBestOnlineScores)
|
||||
.GetValueAsync()
|
||||
.ContinueWithOnMainThread(task =>
|
||||
{
|
||||
if (!task.IsCompletedSuccessfully)
|
||||
{
|
||||
Debug.LogError(task.Exception);
|
||||
callback(DatabaseTransactionResult.Error, new List<LocalPlayerData.Score>(0));
|
||||
return;
|
||||
}
|
||||
|
||||
var scores = new List<LocalPlayerData.Score>();
|
||||
foreach (var child in task.Result.Children)
|
||||
{
|
||||
try
|
||||
{
|
||||
var score = new LocalPlayerData.Score(child.Value as Dictionary<string, object>);
|
||||
scores.Add(score);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
|
||||
callback(DatabaseTransactionResult.Ok, scores);
|
||||
GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -588,7 +660,7 @@ public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData
|
|||
|
||||
var recentScoreQueue = GameManager.Instance.Data.RecentOnlineScores;
|
||||
foreach (var score in recentScores) recentScoreQueue.Enqueue(score);
|
||||
while (recentScoreQueue.Count > LocalPlayerData.MaxBestOnlineScores) recentScoreQueue.Dequeue();
|
||||
while (recentScoreQueue.Count > LocalPlayerData.MaxRecentLocalScores) recentScoreQueue.Dequeue();
|
||||
|
||||
GetBestScores((bestRes, bestScores) =>
|
||||
{
|
||||
|
@ -600,7 +672,7 @@ public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData
|
|||
}
|
||||
|
||||
var bestScoreQueue = GameManager.Instance.Data.BestOnlineScores;
|
||||
foreach (var score in recentScores) bestScoreQueue.Enqueue(score);
|
||||
foreach (var score in bestScores) bestScoreQueue.Enqueue(score);
|
||||
while (bestScoreQueue.Count > LocalPlayerData.MaxBestOnlineScores) bestScoreQueue.Dequeue();
|
||||
|
||||
callback(DatabaseTransactionResult.Ok, GameManager.Instance.Data.CalculateUserRating());
|
||||
|
|
|
@ -34,6 +34,11 @@ public class GameManager : MonoBehaviour
|
|||
/// </summary>
|
||||
public Backend Backend;
|
||||
|
||||
/// <summary>
|
||||
/// gameplay object for handling game loop
|
||||
/// </summary>
|
||||
public Gameplay Gameplay;
|
||||
|
||||
/// <summary>
|
||||
/// read-only property for accessing the local player data outside this class
|
||||
/// </summary>
|
||||
|
@ -121,6 +126,8 @@ private void OnEnable()
|
|||
? DisplayStyle.Flex
|
||||
: DisplayStyle.None;
|
||||
});
|
||||
|
||||
Gameplay = new Gameplay(ui.UI);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -146,7 +153,7 @@ public void RegisterOnLocalPlayerDataChangeCallback(Action<LocalPlayerData> call
|
|||
/// <summary>
|
||||
/// function to fire all local player data change callbacks
|
||||
/// </summary>
|
||||
private void FireLocalPlayerDataChangeCallbacks(LocalPlayerData data)
|
||||
public void FireLocalPlayerDataChangeCallbacks(LocalPlayerData data)
|
||||
{
|
||||
Debug.Log($"firing LocalPlayerDataChangeCallbacks ({_onLocalPlayerDataChangeCallbacks.Count})");
|
||||
foreach (var callback in _onLocalPlayerDataChangeCallbacks)
|
||||
|
@ -194,6 +201,7 @@ public void SignalGameEnd(List<Gameplay.RoundInfo> playedRounds)
|
|||
var roundLightnessAcc = 0d;
|
||||
var roundChromaAcc = 0d;
|
||||
var roundHueAcc = 0d;
|
||||
var roundPerceivedAcc = 0d;
|
||||
|
||||
var maxDistance = Colorimetry.CalculateDistance(Color.black, Color.white);
|
||||
|
||||
|
@ -203,11 +211,13 @@ public void SignalGameEnd(List<Gameplay.RoundInfo> playedRounds)
|
|||
roundLightnessAcc += distance.dL / maxDistance.dL;
|
||||
roundChromaAcc += distance.dC / maxDistance.dC;
|
||||
roundHueAcc += distance.dH / maxDistance.dH;
|
||||
roundPerceivedAcc += distance.dE / maxDistance.dE;
|
||||
}
|
||||
|
||||
roundLightnessAcc /= playedRounds.Count;
|
||||
roundChromaAcc /= playedRounds.Count;
|
||||
roundHueAcc /= playedRounds.Count;
|
||||
roundPerceivedAcc /= playedRounds.Count;
|
||||
|
||||
var roundAcc = (roundLightnessAcc + roundChromaAcc + roundHueAcc) / 3;
|
||||
|
||||
|
@ -223,9 +233,12 @@ public void SignalGameEnd(List<Gameplay.RoundInfo> playedRounds)
|
|||
playedRounds.Count,
|
||||
(float)roundLightnessAcc,
|
||||
(float)roundChromaAcc,
|
||||
(float)roundHueAcc);
|
||||
(float)roundHueAcc,
|
||||
(float)roundPerceivedAcc);
|
||||
|
||||
_data.RegisterLocalScore(score);
|
||||
FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
||||
|
||||
Backend.SubmitScore(score,
|
||||
submitRes =>
|
||||
{
|
||||
|
@ -242,6 +255,7 @@ public void SignalGameEnd(List<Gameplay.RoundInfo> playedRounds)
|
|||
{
|
||||
Debug.Log("couldn't calculate user rating");
|
||||
TransitionToResultsView(_data.CalculateUserRating());
|
||||
FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,16 +42,23 @@ public class Gameplay
|
|||
/// <summary>
|
||||
/// game round counter
|
||||
/// </summary>
|
||||
private int _round = -1;
|
||||
public int Round = -1;
|
||||
|
||||
public int RoundsPerGame = 5;
|
||||
public double SecondsPerRound = 15d;
|
||||
/// <summary>
|
||||
/// singleton instance of the gameplay class
|
||||
/// </summary>
|
||||
private const int RoundsPerGame = 5;
|
||||
|
||||
/// <summary>
|
||||
/// seconds per round
|
||||
/// </summary>
|
||||
private const double SecondsPerRound = 15d;
|
||||
|
||||
/// <summary>
|
||||
/// constructor for the gameplay class
|
||||
/// </summary>
|
||||
/// <param name="ui">the visual element object for the ui document with the GameView</param>
|
||||
private Gameplay(VisualElement ui)
|
||||
public Gameplay(VisualElement ui)
|
||||
{
|
||||
_roundText = ui.Q<Label>("RoundText");
|
||||
_countdownText = ui.Q<Label>("TimeText");
|
||||
|
@ -62,18 +69,18 @@ private Gameplay(VisualElement ui)
|
|||
/// <summary>
|
||||
/// function for starting the game
|
||||
/// </summary>
|
||||
private void StartGame()
|
||||
public void StartGame()
|
||||
{
|
||||
_round = 0;
|
||||
Round = 0;
|
||||
AdvanceToNextRound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// called once per frame
|
||||
/// (to be) called once per frame
|
||||
/// </summary>
|
||||
private void Update()
|
||||
public void Update()
|
||||
{
|
||||
if (_round < 1) return;
|
||||
if (Round < 1) return;
|
||||
if (_countdownDatetime < DateTime.Now) AdvanceToNextRound();
|
||||
Render();
|
||||
}
|
||||
|
@ -83,18 +90,19 @@ private void Update()
|
|||
/// </summary>
|
||||
private void AdvanceToNextRound()
|
||||
{
|
||||
if (_round > 0) StoreRoundInfo();
|
||||
if (Round > 0) StoreRoundInfo();
|
||||
GenerateNewTemplateColour();
|
||||
|
||||
if (_round < RoundsPerGame)
|
||||
if (Round < RoundsPerGame)
|
||||
{
|
||||
_round++;
|
||||
Debug.Log("round advance");
|
||||
Round++;
|
||||
_countdownDatetime = DateTime.Now.AddSeconds(SecondsPerRound);
|
||||
}
|
||||
else
|
||||
{
|
||||
// end game
|
||||
_round = -1;
|
||||
Round = -1;
|
||||
GameManager.Instance.SignalGameEnd(_playedRounds);
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +117,7 @@ private void StoreRoundInfo()
|
|||
TemplateColour = _templateColour.style.backgroundColor.value,
|
||||
ResponseColour = _responseColour.style.backgroundColor.value
|
||||
});
|
||||
Debug.Log("stored round info");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -130,6 +139,7 @@ private void GenerateNewTemplateColour()
|
|||
);
|
||||
|
||||
_templateColour.style.backgroundColor = new StyleColor(colour);
|
||||
Debug.Log($"generated new template colour {colour}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -138,7 +148,7 @@ private void GenerateNewTemplateColour()
|
|||
private void Render()
|
||||
{
|
||||
var remaining = (_countdownDatetime - DateTime.Now).TotalSeconds;
|
||||
_roundText.text = $"{_round}/{RoundsPerGame}";
|
||||
_roundText.text = $"{Round}/{RoundsPerGame}";
|
||||
_countdownText.text = $"{remaining:F}";
|
||||
}
|
||||
|
||||
|
|
|
@ -83,9 +83,10 @@ public void LoadFromTheWorld(Action<LocalPlayerData> callback)
|
|||
var l = PlayerPrefs.GetFloat($"RecentLocalScores_{idx}_AvgLightnessAccuracy", -1f);
|
||||
var c = PlayerPrefs.GetFloat($"RecentLocalScores_{idx}_AvgChromaAccuracy", -1f);
|
||||
var h = PlayerPrefs.GetFloat($"RecentLocalScores_{idx}_AvgHueAccuracy", -1f);
|
||||
var e = PlayerPrefs.GetFloat($"RecentLocalScores_{idx}_AvgPerceivedAccuracy", -1f);
|
||||
|
||||
// if any of the values are invalid, don't add the score
|
||||
if (noOfRounds < 0 || l < 0 || c < 0 || h < 0) continue;
|
||||
if (noOfRounds < 0 || l < 0 || c < 0 || h < 0 || e < 0) continue;
|
||||
|
||||
RegisterLocalScore(new Score(timestamp, Math.Max(1, noOfRounds), Math.Clamp(l, 0f, 100f),
|
||||
Math.Clamp(c, 0f, 100f), Math.Clamp(h, 0f, 100f)));
|
||||
|
@ -125,6 +126,7 @@ public void SaveToTheWorld()
|
|||
PlayerPrefs.SetFloat($"RecentLocalScores_{idx}_AvgLightnessAccuracy", score.AvgLightnessAccuracy);
|
||||
PlayerPrefs.SetFloat($"RecentLocalScores_{idx}_AvgChromaAccuracy", score.AvgChromaAccuracy);
|
||||
PlayerPrefs.SetFloat($"RecentLocalScores_{idx}_AvgHueAccuracy", score.AvgHueAccuracy);
|
||||
PlayerPrefs.SetFloat($"RecentLocalScores_{idx}_AvgPerceivedAccuracy", score.AvgPerceivedAccuracy);
|
||||
idx++;
|
||||
}
|
||||
|
||||
|
@ -216,6 +218,11 @@ public struct Score
|
|||
/// average hue accuracy across all rounds (0-100)
|
||||
/// </summary>
|
||||
public readonly float AvgHueAccuracy;
|
||||
|
||||
/// <summary>
|
||||
/// average perceived accuracy across all rounds (0-100)
|
||||
/// </summary>
|
||||
public readonly float AvgPerceivedAccuracy;
|
||||
|
||||
/// <summary>
|
||||
/// constructor for the score struct
|
||||
|
@ -225,35 +232,47 @@ public struct Score
|
|||
/// <param name="l">average lightness accuracy across all rounds (0-100)</param>
|
||||
/// <param name="c">average chroma accuracy across all rounds (0-100)</param>
|
||||
/// <param name="h">average hue accuracy across all rounds (0-100)</param>
|
||||
/// /// <param name="e">average perceived accuracy across all rounds (0-100)</param>
|
||||
public Score(DateTime timestamp = new(), int noOfRounds = 1, float l = 100.0f, float c = 100.0f,
|
||||
float h = 100.0f)
|
||||
float h = 100.0f, float e = 100.0f)
|
||||
{
|
||||
Timestamp = timestamp;
|
||||
NoOfRounds = noOfRounds;
|
||||
AvgLightnessAccuracy = l;
|
||||
AvgChromaAccuracy = c;
|
||||
AvgHueAccuracy = h;
|
||||
AvgPerceivedAccuracy = e;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// dict-based constructor for the score struct
|
||||
/// </summary>
|
||||
/// <param name="data">dictionary of the score data</param>
|
||||
/// <exception cref="ArgumentException">thrown if the dictionary is malformed or missing data</exception>
|
||||
public Score(Dictionary<string, object> data)
|
||||
{
|
||||
// try to safely construct the score from a backend-provided dictionary
|
||||
// for each value, if it's not found, or not a valid value, set it to a default value
|
||||
Timestamp = data.ContainsKey("timestamp") && data["timestamp"] is long t
|
||||
? DateTimeOffset.FromUnixTimeSeconds(t).DateTime
|
||||
: DateTime.MinValue;
|
||||
NoOfRounds = data.ContainsKey("noOfRounds") && data["noOfRounds"] is int n ? n : 1;
|
||||
AvgLightnessAccuracy = data.ContainsKey("avgLightnessAccuracy") && data["avgLightnessAccuracy"] is float l
|
||||
? l
|
||||
: 100.0f;
|
||||
AvgChromaAccuracy = data.ContainsKey("avgChromaAccuracy") && data["avgChromaAccuracy"] is float c
|
||||
? c
|
||||
: 100.0f;
|
||||
AvgHueAccuracy = data.ContainsKey("avgHueAccuracy") && data["avgHueAccuracy"] is float h ? h : 100.0f;
|
||||
// for each value, if it's not found, or not a valid value, throw an exception
|
||||
|
||||
if (!data.ContainsKey("timestamp") || !(data["timestamp"] is long timestamp))
|
||||
throw new ArgumentException("timestamp not found or invalid");
|
||||
if (!data.ContainsKey("noOfRounds") || !(data["noOfRounds"] is int noOfRounds))
|
||||
throw new ArgumentException("noOfRounds not found or invalid");
|
||||
if (!data.ContainsKey("avgLightnessAccuracy") || !(data["avgLightnessAccuracy"] is float avgLightnessAccuracy))
|
||||
throw new ArgumentException("avgLightnessAccuracy not found or invalid");
|
||||
if (!data.ContainsKey("avgChromaAccuracy") || !(data["avgChromaAccuracy"] is float avgChromaAccuracy))
|
||||
throw new ArgumentException("avgChromaAccuracy not found or invalid");
|
||||
if (!data.ContainsKey("avgHueAccuracy") || !(data["avgHueAccuracy"] is float avgHueAccuracy))
|
||||
throw new ArgumentException("avgHueAccuracy not found or invalid");
|
||||
if (!data.ContainsKey("avgPerceivedAccuracy") || !(data["avgPerceivedAccuracy"] is float avgPerceivedAccuracy))
|
||||
throw new ArgumentException("avgPerceivedAccuracy not found or invalid");
|
||||
|
||||
Timestamp = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime;
|
||||
NoOfRounds = noOfRounds;
|
||||
AvgLightnessAccuracy = avgLightnessAccuracy;
|
||||
AvgChromaAccuracy = avgChromaAccuracy;
|
||||
AvgHueAccuracy = avgHueAccuracy;
|
||||
AvgPerceivedAccuracy = avgPerceivedAccuracy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -268,7 +287,8 @@ public Score(Dictionary<string, object> data)
|
|||
{ "noOfRounds", NoOfRounds },
|
||||
{ "avgLightnessAccuracy", AvgLightnessAccuracy },
|
||||
{ "avgChromaAccuracy", AvgChromaAccuracy },
|
||||
{ "avgHueAccuracy", AvgHueAccuracy }
|
||||
{ "avgHueAccuracy", AvgHueAccuracy },
|
||||
{ "avgPerceivedAccuracy", AvgPerceivedAccuracy }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,6 @@ public class SideViewUI : MonoBehaviour
|
|||
/// </summary>
|
||||
private Label _chromaAccuracyText;
|
||||
|
||||
/// <summary>
|
||||
/// connection status label for showing the connection status
|
||||
/// </summary>
|
||||
private Label _connectionStatusLabel;
|
||||
|
||||
/// <summary>
|
||||
/// text label for showing the player's stable-ish hue accuracy
|
||||
/// </summary>
|
||||
|
@ -67,8 +62,6 @@ private void OnEnable()
|
|||
_accountButton = ui.Q<Button>("AccountButton");
|
||||
_accountButton.clicked += OnAccountButtonClicked;
|
||||
|
||||
_connectionStatusLabel = ui.Q<Label>("ConnectionStatusLabel");
|
||||
|
||||
_playerText = ui.Q<Label>("PlayerText");
|
||||
_ratingText = ui.Q<Label>("RatingText");
|
||||
|
||||
|
|
|
@ -74,6 +74,13 @@ private void OnEnable()
|
|||
UI = GetComponent<UIDocument>().rootVisualElement;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var gameplay = GameManager.Instance.Gameplay;
|
||||
if (gameplay == null) return;
|
||||
if (gameplay.Round >= 1) gameplay.Update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// function to show a menu based on the enum passed,
|
||||
/// and any other necessary actions
|
||||
|
@ -134,6 +141,9 @@ public void SetDisplayState(DisplayState newDisplayState)
|
|||
UI.Q<Button>("AccountButton").style.display = DisplayStyle.None;
|
||||
UI.Q<VisualElement>("AccountSection").style.display = DisplayStyle.Flex;
|
||||
UI.Q<VisualElement>("ConnectionStatus").style.display = DisplayStyle.None;
|
||||
|
||||
// start the game
|
||||
GameManager.Instance.Gameplay.StartGame();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Reference in a new issue