game: gameplay loop works but inaccurate result calcs
This commit is contained in:
parent
5e1defa793
commit
e1aee5d946
|
@ -67,6 +67,12 @@ public class AccountUI : MonoBehaviour
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Button _secondaryActionButton;
|
private Button _secondaryActionButton;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// either 'delete local data' or 'delete account'
|
||||||
|
/// (in order of 'initial', and 'post' states, is hidden in 'after' state)
|
||||||
|
/// </summary>
|
||||||
|
private Button _tertiaryActionButton;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// username text field
|
/// username text field
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -108,6 +114,9 @@ private void OnEnable()
|
||||||
_secondaryActionButton = ui.Q<Button>("SecondaryActionButton");
|
_secondaryActionButton = ui.Q<Button>("SecondaryActionButton");
|
||||||
_secondaryActionButton.clicked += OnSecondaryActionButtonClick;
|
_secondaryActionButton.clicked += OnSecondaryActionButtonClick;
|
||||||
|
|
||||||
|
_tertiaryActionButton = ui.Q<Button>("TertiaryActionButton");
|
||||||
|
_tertiaryActionButton.clicked += OnTertiaryActionButtonClick;
|
||||||
|
|
||||||
TransitionStateTo(State.NotSignedIn);
|
TransitionStateTo(State.NotSignedIn);
|
||||||
|
|
||||||
if (state == State.UnassociatedState) throw new Exception("unreachable state");
|
if (state == State.UnassociatedState) throw new Exception("unreachable state");
|
||||||
|
@ -120,7 +129,7 @@ private void OnEnable()
|
||||||
_usernameField.value = username;
|
_usernameField.value = username;
|
||||||
_emailField.value = GameManager.Instance.Backend.GetUser().Email;
|
_emailField.value = GameManager.Instance.Backend.GetUser().Email;
|
||||||
});
|
});
|
||||||
GameManager.Instance.RegisterOnLocalPlayerDataChangeCallback(PopulateFields);
|
GameManager.Instance.RegisterOnLocalPlayerDataChangeCallback(OnLocalPlayerDataChangeCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TransitionStateTo(State newState, bool keepAccompanyingText = false)
|
private void TransitionStateTo(State newState, bool keepAccompanyingText = false)
|
||||||
|
@ -157,9 +166,11 @@ private void TransitionStateTo(State newState, bool keepAccompanyingText = false
|
||||||
|
|
||||||
_primaryActionButton.style.display = DisplayStyle.Flex;
|
_primaryActionButton.style.display = DisplayStyle.Flex;
|
||||||
_secondaryActionButton.style.display = DisplayStyle.Flex;
|
_secondaryActionButton.style.display = DisplayStyle.Flex;
|
||||||
|
_tertiaryActionButton.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
_primaryActionButton.text = "Continue \u2192";
|
_primaryActionButton.text = "Continue \u2192";
|
||||||
_secondaryActionButton.text = "Forgot Password \u2192";
|
_secondaryActionButton.text = "Forgot Password \u2192";
|
||||||
|
_tertiaryActionButton.text = "Delete Local Data \u2192";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State.AfterContinue:
|
case State.AfterContinue:
|
||||||
|
@ -171,6 +182,7 @@ private void TransitionStateTo(State newState, bool keepAccompanyingText = false
|
||||||
|
|
||||||
_primaryActionButton.style.display = DisplayStyle.Flex;
|
_primaryActionButton.style.display = DisplayStyle.Flex;
|
||||||
_secondaryActionButton.style.display = DisplayStyle.Flex;
|
_secondaryActionButton.style.display = DisplayStyle.Flex;
|
||||||
|
_tertiaryActionButton.style.display = DisplayStyle.None;
|
||||||
|
|
||||||
_primaryActionButton.text = "Retry Log In \u2192";
|
_primaryActionButton.text = "Retry Log In \u2192";
|
||||||
_secondaryActionButton.text = "Create an Account \u2192";
|
_secondaryActionButton.text = "Create an Account \u2192";
|
||||||
|
@ -188,8 +200,10 @@ private void TransitionStateTo(State newState, bool keepAccompanyingText = false
|
||||||
|
|
||||||
_primaryActionButton.style.display = DisplayStyle.Flex;
|
_primaryActionButton.style.display = DisplayStyle.Flex;
|
||||||
_secondaryActionButton.style.display = DisplayStyle.None;
|
_secondaryActionButton.style.display = DisplayStyle.None;
|
||||||
|
_tertiaryActionButton.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
_primaryActionButton.text = "Sign Out \u2192";
|
_primaryActionButton.text = "Sign Out \u2192";
|
||||||
|
_tertiaryActionButton.text = "Delete Account \u2192";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State.UnassociatedState:
|
case State.UnassociatedState:
|
||||||
|
@ -375,8 +389,6 @@ private void OnUsernameUpdateButtonClick()
|
||||||
_ => "An error occurred updating the username. Please try again."
|
_ => "An error occurred updating the username. Please try again."
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: update lpdata
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -409,8 +421,6 @@ private void OnEmailUpdateButtonClick()
|
||||||
_ => "An error occurred updating the email. Please try again."
|
_ => "An error occurred updating the email. Please try again."
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: update lpdata
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -513,7 +523,8 @@ private void OnPrimaryActionButtonClick()
|
||||||
|
|
||||||
case State.UnassociatedState:
|
case State.UnassociatedState:
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
Debug.LogError($"tertiary button clicked in illogical state {state} (unreachable?)");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,7 +548,7 @@ private void OnSecondaryActionButtonClick()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameManager.Instance.Backend.ForgotPassword(_emailField.value, result =>
|
GameManager.Instance.Backend.ResetUserPassword(_emailField.value, result =>
|
||||||
{
|
{
|
||||||
_accompanyingText.style.display = DisplayStyle.Flex;
|
_accompanyingText.style.display = DisplayStyle.Flex;
|
||||||
_accompanyingText.text =
|
_accompanyingText.text =
|
||||||
|
@ -591,6 +602,11 @@ private void OnSecondaryActionButtonClick()
|
||||||
_accompanyingText.text = "Invalid credentials. Please try again.";
|
_accompanyingText.text = "Invalid credentials. Please try again.";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Backend.AuthenticationResult.UsernameAlreadyTaken:
|
||||||
|
_accompanyingText.style.display = DisplayStyle.Flex;
|
||||||
|
_accompanyingText.text = "Username already taken. Please try another.";
|
||||||
|
break;
|
||||||
|
|
||||||
case Backend.AuthenticationResult.NonExistentUser:
|
case Backend.AuthenticationResult.NonExistentUser:
|
||||||
case Backend.AuthenticationResult.AlreadyExistingUser:
|
case Backend.AuthenticationResult.AlreadyExistingUser:
|
||||||
case Backend.AuthenticationResult.GenericError:
|
case Backend.AuthenticationResult.GenericError:
|
||||||
|
@ -605,10 +621,33 @@ private void OnSecondaryActionButtonClick()
|
||||||
_usernameField.value);
|
_usernameField.value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case State.SignedIn:
|
|
||||||
case State.UnassociatedState:
|
case State.UnassociatedState:
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
Debug.LogError($"tertiary button clicked in illogical state {state} (unreachable?)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTertiaryActionButtonClick()
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
// delete local data
|
||||||
|
case State.NotSignedIn:
|
||||||
|
PlayerPrefs.DeleteAll();
|
||||||
|
GameManager.Instance.Data.LoadFromTheWorld(GameManager.Instance.FireLocalPlayerDataChangeCallbacks);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// delete user account
|
||||||
|
case State.SignedIn:
|
||||||
|
GameManager.Instance.Backend.DeleteUser();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State.AfterContinue:
|
||||||
|
case State.UnassociatedState:
|
||||||
|
default:
|
||||||
|
Debug.LogError($"tertiary button clicked in illogical state {state} (unreachable?)");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,12 +655,13 @@ private void OnSecondaryActionButtonClick()
|
||||||
/// populate the fields with the given username and email,
|
/// populate the fields with the given username and email,
|
||||||
/// used as a callback to when local player data is changed
|
/// used as a callback to when local player data is changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void PopulateFields(LocalPlayerData data)
|
private void OnLocalPlayerDataChangeCallback(LocalPlayerData data)
|
||||||
{
|
{
|
||||||
Debug.Log(
|
Debug.Log(
|
||||||
$"populating AccountView fields with lkUsername={data.LastKnownUsername} and lkEmail={data.LastKnownEmail}");
|
$"updating AccountView ui with lkUsername={data.LastKnownUsername} and lkEmail={data.LastKnownEmail}");
|
||||||
_usernameField.value = data.LastKnownUsername;
|
_usernameField.value = data.LastKnownUsername;
|
||||||
_emailField.value = data.LastKnownEmail;
|
_emailField.value = data.LastKnownEmail;
|
||||||
|
_header.text = $"Signed in as {data.LastKnownUsername}";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -22,6 +22,7 @@ public enum AuthenticationResult
|
||||||
AlreadyAuthenticated,
|
AlreadyAuthenticated,
|
||||||
NonExistentUser,
|
NonExistentUser,
|
||||||
AlreadyExistingUser,
|
AlreadyExistingUser,
|
||||||
|
UsernameAlreadyTaken,
|
||||||
InvalidEmail,
|
InvalidEmail,
|
||||||
InvalidCredentials,
|
InvalidCredentials,
|
||||||
GenericError
|
GenericError
|
||||||
|
@ -308,6 +309,26 @@ private void AuthStateChanged(object sender, EventArgs eventArgs)
|
||||||
|
|
||||||
if (registerUser)
|
if (registerUser)
|
||||||
{
|
{
|
||||||
|
// check if the username is already taken
|
||||||
|
_db.Child("users")
|
||||||
|
.OrderByChild("username")
|
||||||
|
.EqualTo(registeringUsername)
|
||||||
|
.GetValueAsync()
|
||||||
|
.ContinueWithOnMainThread(task =>
|
||||||
|
{
|
||||||
|
if (task.Exception != null)
|
||||||
|
{
|
||||||
|
Debug.LogError(task.Exception);
|
||||||
|
callback(AuthenticationResult.GenericError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!task.IsCompletedSuccessfully || task.Result.ChildrenCount > 0)
|
||||||
|
{
|
||||||
|
callback(AuthenticationResult.UsernameAlreadyTaken);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// register user
|
// register user
|
||||||
_auth.CreateUserWithEmailAndPasswordAsync(email, password)
|
_auth.CreateUserWithEmailAndPasswordAsync(email, password)
|
||||||
.ContinueWithOnMainThread(createTask =>
|
.ContinueWithOnMainThread(createTask =>
|
||||||
|
@ -372,6 +393,7 @@ private void AuthStateChanged(object sender, EventArgs eventArgs)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,7 +465,11 @@ private void RetrieveUsernameWithCallback(Action<DatabaseTransactionResult, stri
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_db.Child("users").Child(_user.UserId).Child("username").GetValueAsync().ContinueWithOnMainThread(task =>
|
_db.Child("users")
|
||||||
|
.Child(_user.UserId)
|
||||||
|
.Child("username")
|
||||||
|
.GetValueAsync()
|
||||||
|
.ContinueWithOnMainThread(task =>
|
||||||
{
|
{
|
||||||
DatabaseTransactionResult result;
|
DatabaseTransactionResult result;
|
||||||
if (task.IsCompletedSuccessfully)
|
if (task.IsCompletedSuccessfully)
|
||||||
|
@ -485,14 +511,34 @@ public void SignOutUser()
|
||||||
_auth.SignOut();
|
_auth.SignOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// abstraction function to delete the user
|
||||||
|
/// </summary>
|
||||||
|
public void DeleteUser()
|
||||||
|
{
|
||||||
|
_user.DeleteAsync().ContinueWithOnMainThread(task =>
|
||||||
|
{
|
||||||
|
if (task.IsCompletedSuccessfully)
|
||||||
|
{
|
||||||
|
Debug.Log("user deleted");
|
||||||
|
SignOutUser();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError(task.Exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// abstraction function for the user to reset their password
|
/// abstraction function for the user to reset their password
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="email">the forgetful user's email lol</param>
|
/// <param name="email">the forgetful user's email lol</param>
|
||||||
/// <param name="callback">callback function to be invoked after the password reset email is sent</param>
|
/// <param name="callback">callback function to be invoked after the password reset email is sent</param>
|
||||||
public void ForgotPassword(string email, Action<bool> callback)
|
public void ResetUserPassword(string email, Action<bool> callback)
|
||||||
{
|
{
|
||||||
_auth.SendPasswordResetEmailAsync(email).ContinueWithOnMainThread(resetTask =>
|
_auth.SendPasswordResetEmailAsync(email)
|
||||||
|
.ContinueWithOnMainThread(resetTask =>
|
||||||
{
|
{
|
||||||
if (resetTask.IsCompletedSuccessfully)
|
if (resetTask.IsCompletedSuccessfully)
|
||||||
{
|
{
|
||||||
|
@ -538,7 +584,6 @@ public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerDa
|
||||||
|
|
||||||
var scores = new List<LocalPlayerData.Score>();
|
var scores = new List<LocalPlayerData.Score>();
|
||||||
foreach (var child in task.Result.Children)
|
foreach (var child in task.Result.Children)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var score = new LocalPlayerData.Score(child.Value as Dictionary<string, object>);
|
var score = new LocalPlayerData.Score(child.Value as Dictionary<string, object>);
|
||||||
|
@ -548,7 +593,6 @@ public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerDa
|
||||||
{
|
{
|
||||||
Debug.LogError($"{e}\n{child.GetRawJsonValue()}");
|
Debug.LogError($"{e}\n{child.GetRawJsonValue()}");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
callback(DatabaseTransactionResult.Ok, scores);
|
callback(DatabaseTransactionResult.Ok, scores);
|
||||||
GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
||||||
|
@ -562,7 +606,7 @@ public void GetRecentScores(Action<DatabaseTransactionResult, List<LocalPlayerDa
|
||||||
/// callback function that takes in a <c>DatabaseTransactionResult</c> enum and a
|
/// callback function that takes in a <c>DatabaseTransactionResult</c> enum and a
|
||||||
/// <c>List<LocalPlayerData.Score></c>
|
/// <c>List<LocalPlayerData.Score></c>
|
||||||
/// </param>
|
/// </param>
|
||||||
public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData.Score>> callback)
|
private void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData.Score>> callback)
|
||||||
{
|
{
|
||||||
if (!Status.Equals(FirebaseConnectionStatus.Connected)) return;
|
if (!Status.Equals(FirebaseConnectionStatus.Connected)) return;
|
||||||
|
|
||||||
|
@ -587,7 +631,6 @@ public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData
|
||||||
|
|
||||||
var scores = new List<LocalPlayerData.Score>();
|
var scores = new List<LocalPlayerData.Score>();
|
||||||
foreach (var child in task.Result.Children)
|
foreach (var child in task.Result.Children)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var score = new LocalPlayerData.Score(child.Value as Dictionary<string, object>);
|
var score = new LocalPlayerData.Score(child.Value as Dictionary<string, object>);
|
||||||
|
@ -597,7 +640,6 @@ public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData
|
||||||
{
|
{
|
||||||
Debug.LogError(e);
|
Debug.LogError(e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
callback(DatabaseTransactionResult.Ok, scores);
|
callback(DatabaseTransactionResult.Ok, scores);
|
||||||
GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
||||||
|
@ -651,10 +693,10 @@ public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData
|
||||||
{
|
{
|
||||||
GetRecentScores((recentRes, recentScores) =>
|
GetRecentScores((recentRes, recentScores) =>
|
||||||
{
|
{
|
||||||
if (recentRes == DatabaseTransactionResult.Error)
|
if (recentRes != DatabaseTransactionResult.Ok)
|
||||||
{
|
{
|
||||||
Debug.Log("failed to get recent scores");
|
Debug.Log("failed to get recent scores");
|
||||||
callback(DatabaseTransactionResult.Error, 0f);
|
callback(recentRes, 0f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,10 +706,10 @@ public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData
|
||||||
|
|
||||||
GetBestScores((bestRes, bestScores) =>
|
GetBestScores((bestRes, bestScores) =>
|
||||||
{
|
{
|
||||||
if (bestRes == DatabaseTransactionResult.Error)
|
if (bestRes != DatabaseTransactionResult.Ok)
|
||||||
{
|
{
|
||||||
Debug.Log("failed to get recent scores");
|
Debug.Log("failed to get recent scores");
|
||||||
callback(DatabaseTransactionResult.Error, 0f);
|
callback(recentRes, 0f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,7 +729,30 @@ public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData
|
||||||
public void UpdateUserRating(
|
public void UpdateUserRating(
|
||||||
Action<DatabaseTransactionResult> callback)
|
Action<DatabaseTransactionResult> callback)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (!Status.Equals(FirebaseConnectionStatus.Connected)) return;
|
||||||
|
|
||||||
|
if (_user == null)
|
||||||
|
{
|
||||||
|
callback(DatabaseTransactionResult.Unauthenticated);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_db.Child("users")
|
||||||
|
.Child(_user.UserId)
|
||||||
|
.Child("rating")
|
||||||
|
.SetValueAsync(GameManager.Instance.Data.CalculateUserRating())
|
||||||
|
.ContinueWithOnMainThread(task =>
|
||||||
|
{
|
||||||
|
if (task.IsCompletedSuccessfully)
|
||||||
|
{
|
||||||
|
callback(DatabaseTransactionResult.Ok);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError(task.Exception);
|
||||||
|
callback(DatabaseTransactionResult.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -729,6 +794,8 @@ public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData
|
||||||
{
|
{
|
||||||
if (task.IsCompletedSuccessfully)
|
if (task.IsCompletedSuccessfully)
|
||||||
{
|
{
|
||||||
|
GameManager.Instance.Data.LastKnownEmail = newValue;
|
||||||
|
GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
||||||
callback(DatabaseTransactionResult.Ok);
|
callback(DatabaseTransactionResult.Ok);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -740,12 +807,17 @@ public void GetBestScores(Action<DatabaseTransactionResult, List<LocalPlayerData
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UserAccountDetailTargetEnum.Username:
|
case UserAccountDetailTargetEnum.Username:
|
||||||
_db.Child("users").Child(_user.UserId).Child("username").SetValueAsync(newValue)
|
_db.Child("users")
|
||||||
|
.Child(_user.UserId)
|
||||||
|
.Child("username")
|
||||||
|
.SetValueAsync(newValue)
|
||||||
.ContinueWithOnMainThread(task =>
|
.ContinueWithOnMainThread(task =>
|
||||||
{
|
{
|
||||||
if (task.IsCompletedSuccessfully)
|
if (task.IsCompletedSuccessfully)
|
||||||
{
|
{
|
||||||
_username = newValue;
|
_username = newValue;
|
||||||
|
GameManager.Instance.Data.LastKnownUsername = newValue;
|
||||||
|
GameManager.Instance.FireLocalPlayerDataChangeCallbacks(GameManager.Instance.Data);
|
||||||
callback(DatabaseTransactionResult.Ok);
|
callback(DatabaseTransactionResult.Ok);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -62,13 +62,13 @@ public static DeltaLabChE CalculateDistance(Color template, Color response)
|
||||||
// ... distance between the (L, a, b) coordinates."
|
// ... distance between the (L, a, b) coordinates."
|
||||||
// https://github.com/svgeesus/svgeesus.github.io/blob/master/Color/OKLab-notes.md#color-difference-metric
|
// https://github.com/svgeesus/svgeesus.github.io/blob/master/Color/OKLab-notes.md#color-difference-metric
|
||||||
// ... ΔL = L1 - L2
|
// ... ΔL = L1 - L2
|
||||||
// ... C1 = √(a1² + b1²) -> chroma values
|
// ... C1 = √(a1² + b1²)
|
||||||
// ... C2 = √(a2² + b2²) -> chroma values
|
// ... C2 = √(a2² + b2²)
|
||||||
// ... ΔC = C1 - C2 -> chroma difference
|
// ... ΔC = C1 - C2
|
||||||
// ... Δa = a1 - a2
|
// ... Δa = a1 - a2
|
||||||
// ... Δb = b1 - b2
|
// ... Δb = b1 - b2
|
||||||
// ... ΔH = √(Δa² + Δb² - ΔC²) -> hue difference
|
// ... ΔH = √(Δa² + Δb² - ΔC²)
|
||||||
// ... ΔE = √(ΔL² + ΔC² + ΔH²) -> final difference
|
// ... ΔE = √(ΔL² + ΔC² + ΔH²)
|
||||||
|
|
||||||
float l1, a1, b1, l2, a2, b2;
|
float l1, a1, b1, l2, a2, b2;
|
||||||
(l1, a1, b1) = (templateOklab.L, templateOklab.a, templateOklab.b);
|
(l1, a1, b1) = (templateOklab.L, templateOklab.a, templateOklab.b);
|
||||||
|
@ -117,6 +117,7 @@ public static Color RawLchToColor(double lightness, double chroma, double hue)
|
||||||
/// <param name="x">the linear srgb value to transform</param>
|
/// <param name="x">the linear srgb value to transform</param>
|
||||||
/// <returns>the non-linear srgb value</returns>
|
/// <returns>the non-linear srgb value</returns>
|
||||||
// https://bottosson.github.io/posts/colorwrong/#what-can-we-do%3F (no licence specified)
|
// https://bottosson.github.io/posts/colorwrong/#what-can-we-do%3F (no licence specified)
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
public static double srgb_nonlinear_transform_f(double x)
|
public static double srgb_nonlinear_transform_f(double x)
|
||||||
{
|
{
|
||||||
if (x >= 0.0031308d)
|
if (x >= 0.0031308d)
|
||||||
|
@ -130,6 +131,7 @@ public static double srgb_nonlinear_transform_f(double x)
|
||||||
/// <param name="x">the non-linear srgb value to transform</param>
|
/// <param name="x">the non-linear srgb value to transform</param>
|
||||||
/// <returns>the linear srgb value</returns>
|
/// <returns>the linear srgb value</returns>
|
||||||
// https://bottosson.github.io/posts/colorwrong/#what-can-we-do%3F (no licence specified)
|
// https://bottosson.github.io/posts/colorwrong/#what-can-we-do%3F (no licence specified)
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
public static double srgb_nonlinear_transform_f_inv(double x)
|
public static double srgb_nonlinear_transform_f_inv(double x)
|
||||||
{
|
{
|
||||||
if (x >= 0.04045d)
|
if (x >= 0.04045d)
|
||||||
|
@ -141,6 +143,7 @@ public static double srgb_nonlinear_transform_f_inv(double x)
|
||||||
/// clips a colour to the sRGB gamut while preserving chroma
|
/// clips a colour to the sRGB gamut while preserving chroma
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// https://bottosson.github.io/posts/gamutclipping/ (MIT)
|
// https://bottosson.github.io/posts/gamutclipping/ (MIT)
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
public static RGB gamut_clip_preserve_chroma(RGB rgb)
|
public static RGB gamut_clip_preserve_chroma(RGB rgb)
|
||||||
{
|
{
|
||||||
if (rgb is { r: < 1 and > 0, g: < 1 and > 0, b: < 1 and > 0 })
|
if (rgb is { r: < 1 and > 0, g: < 1 and > 0, b: < 1 and > 0 })
|
||||||
|
@ -171,6 +174,7 @@ public static RGB gamut_clip_preserve_chroma(RGB rgb)
|
||||||
/// a and b must be normalized so a^2 + b^2 == 1
|
/// a and b must be normalized so a^2 + b^2 == 1
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// https://bottosson.github.io/posts/gamutclipping/ (MIT)
|
// https://bottosson.github.io/posts/gamutclipping/ (MIT)
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
public static float find_gamut_intersection(
|
public static float find_gamut_intersection(
|
||||||
float a,
|
float a,
|
||||||
float b,
|
float b,
|
||||||
|
@ -307,6 +311,7 @@ public static Lab linear_srgb_to_oklab(RGB c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab (public domain)
|
// https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab (public domain)
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
public static RGB oklab_to_linear_srgb(Lab c)
|
public static RGB oklab_to_linear_srgb(Lab c)
|
||||||
{
|
{
|
||||||
var interimL = c.L + 0.3963377774f * c.a + 0.2158037573f * c.b;
|
var interimL = c.L + 0.3963377774f * c.a + 0.2158037573f * c.b;
|
||||||
|
|
|
@ -175,12 +175,14 @@ public void SignalGameEnd(List<Gameplay.RoundInfo> playedRounds)
|
||||||
var historicalLightnessAcc = 0f;
|
var historicalLightnessAcc = 0f;
|
||||||
var historicalChromaAcc = 0f;
|
var historicalChromaAcc = 0f;
|
||||||
var historicalHueAcc = 0f;
|
var historicalHueAcc = 0f;
|
||||||
|
var historicalRounds = 0;
|
||||||
|
|
||||||
foreach (var localScore in _data.RecentLocalScores)
|
foreach (var localScore in _data.RecentLocalScores)
|
||||||
{
|
{
|
||||||
historicalLightnessAcc += localScore.AvgLightnessAccuracy;
|
historicalLightnessAcc += localScore.AvgLightnessAccuracy;
|
||||||
historicalChromaAcc += localScore.AvgChromaAccuracy;
|
historicalChromaAcc += localScore.AvgChromaAccuracy;
|
||||||
historicalHueAcc += localScore.AvgHueAccuracy;
|
historicalHueAcc += localScore.AvgHueAccuracy;
|
||||||
|
historicalRounds += localScore.NoOfRounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var onlineScore in _data.RecentOnlineScores)
|
foreach (var onlineScore in _data.RecentOnlineScores)
|
||||||
|
@ -188,6 +190,7 @@ public void SignalGameEnd(List<Gameplay.RoundInfo> playedRounds)
|
||||||
historicalLightnessAcc += onlineScore.AvgLightnessAccuracy;
|
historicalLightnessAcc += onlineScore.AvgLightnessAccuracy;
|
||||||
historicalChromaAcc += onlineScore.AvgChromaAccuracy;
|
historicalChromaAcc += onlineScore.AvgChromaAccuracy;
|
||||||
historicalHueAcc += onlineScore.AvgHueAccuracy;
|
historicalHueAcc += onlineScore.AvgHueAccuracy;
|
||||||
|
historicalRounds += onlineScore.NoOfRounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var onlineScore in _data.BestOnlineScores)
|
foreach (var onlineScore in _data.BestOnlineScores)
|
||||||
|
@ -195,13 +198,18 @@ public void SignalGameEnd(List<Gameplay.RoundInfo> playedRounds)
|
||||||
historicalLightnessAcc += onlineScore.AvgLightnessAccuracy;
|
historicalLightnessAcc += onlineScore.AvgLightnessAccuracy;
|
||||||
historicalChromaAcc += onlineScore.AvgChromaAccuracy;
|
historicalChromaAcc += onlineScore.AvgChromaAccuracy;
|
||||||
historicalHueAcc += onlineScore.AvgHueAccuracy;
|
historicalHueAcc += onlineScore.AvgHueAccuracy;
|
||||||
|
historicalRounds += onlineScore.NoOfRounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
historicalLightnessAcc /= historicalRounds;
|
||||||
|
historicalChromaAcc /= historicalRounds;
|
||||||
|
historicalHueAcc /= historicalRounds;
|
||||||
|
|
||||||
// calculate round averages
|
// calculate round averages
|
||||||
var roundLightnessAcc = 0d;
|
var gameLightnessAcc = 0d;
|
||||||
var roundChromaAcc = 0d;
|
var gameChromaAcc = 0d;
|
||||||
var roundHueAcc = 0d;
|
var gameHueAcc = 0d;
|
||||||
var roundPerceivedAcc = 0d;
|
var gamePerceivedAcc = 0d;
|
||||||
|
|
||||||
var templateColour = Color.clear;
|
var templateColour = Color.clear;
|
||||||
var responseColour = Color.clear;
|
var responseColour = Color.clear;
|
||||||
|
@ -215,54 +223,65 @@ public void SignalGameEnd(List<Gameplay.RoundInfo> playedRounds)
|
||||||
Debug.Log(
|
Debug.Log(
|
||||||
$"processing round: template={templateColour}, response={responseColour} (dL%={dLCh.L}, dC%={dLCh.C}, dh%={dLCh.h}, dEok={distance.dE:F})");
|
$"processing round: template={templateColour}, response={responseColour} (dL%={dLCh.L}, dC%={dLCh.C}, dh%={dLCh.h}, dEok={distance.dE:F})");
|
||||||
|
|
||||||
roundLightnessAcc += Math.Clamp(dLCh.L * 100d, 0d, 100d);
|
var roundLightnessAcc = Math.Clamp(dLCh.L * 100d, 0d, 100d);
|
||||||
roundChromaAcc += Math.Clamp(dLCh.C * 100d, 0d, 100d);
|
var roundChromaAcc = Math.Clamp(dLCh.C * 100d, 0d, 100d);
|
||||||
roundHueAcc += Math.Clamp(dLCh.h * 100d, 0d, 100d);
|
var roundHueAcc = Math.Clamp(dLCh.h * 100d, 0d, 100d);
|
||||||
roundPerceivedAcc += Math.Clamp((100d - distance.dE) * 100d, 0d, 100d);
|
var roundPerceivedAcc = Math.Clamp((100d - distance.dE) * 100d, 0d, 100d);
|
||||||
|
|
||||||
|
gameLightnessAcc += roundLightnessAcc;
|
||||||
|
gameChromaAcc += roundChromaAcc;
|
||||||
|
gameHueAcc += roundHueAcc;
|
||||||
|
gamePerceivedAcc += roundPerceivedAcc;
|
||||||
|
|
||||||
var showcaseTemplate = ui.UI.Q<VisualElement>($"ShowcasePair{roundNumber}TemplateColour");
|
var showcaseTemplate = ui.UI.Q<VisualElement>($"ShowcasePair{roundNumber}TemplateColour");
|
||||||
var showcaseResponse = ui.UI.Q<VisualElement>($"ShowcasePair{roundNumber}ResponseColour");
|
var showcaseResponse = ui.UI.Q<VisualElement>($"ShowcasePair{roundNumber}ResponseColour");
|
||||||
var showcaseInfo = ui.UI.Q<Label>($"ShowcasePair{roundNumber}Info");
|
var showcaseInfo = ui.UI.Q<Label>($"ShowcasePair{roundNumber}Info");
|
||||||
|
|
||||||
if (showcaseTemplate == null || showcaseResponse == null || showcaseInfo == null)
|
if (!(showcaseTemplate == null || showcaseResponse == null || showcaseInfo == null))
|
||||||
{
|
{
|
||||||
Debug.LogError($"showcase pair {roundNumber} not found");
|
|
||||||
roundNumber++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
showcaseTemplate.style.backgroundColor = templateColour;
|
showcaseTemplate.style.backgroundColor = templateColour;
|
||||||
showcaseResponse.style.backgroundColor = responseColour;
|
showcaseResponse.style.backgroundColor = responseColour;
|
||||||
showcaseInfo.text = $"{roundLightnessAcc:F}% {roundChromaAcc:F}% {roundHueAcc:F}% ({roundPerceivedAcc:F}%)";
|
showcaseInfo.text =
|
||||||
|
$"{roundLightnessAcc:N0}% {roundChromaAcc:N0}% {roundHueAcc:N0}% ({roundPerceivedAcc:N0}%)";
|
||||||
roundNumber++;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError($"showcase pair {roundNumber} not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
roundLightnessAcc /= Gameplay.RoundsPerGame;
|
roundNumber++; // used for ui querying
|
||||||
roundChromaAcc /= Gameplay.RoundsPerGame;
|
}
|
||||||
roundHueAcc /= Gameplay.RoundsPerGame;
|
|
||||||
roundPerceivedAcc /= Gameplay.RoundsPerGame;
|
|
||||||
|
|
||||||
var roundAcc = (roundLightnessAcc + roundChromaAcc + roundHueAcc + roundPerceivedAcc + roundPerceivedAcc) / 5;
|
gameLightnessAcc /= Gameplay.RoundsPerGame;
|
||||||
|
gameChromaAcc /= Gameplay.RoundsPerGame;
|
||||||
|
gameHueAcc /= Gameplay.RoundsPerGame;
|
||||||
|
gamePerceivedAcc /= Gameplay.RoundsPerGame;
|
||||||
|
|
||||||
|
// NOTE: this is NOT equiv to user rating, this is just a per-game accuracy score
|
||||||
|
// all that math is done in LocalPlayerData.CalculateUserRating
|
||||||
|
var gameAccuracy = (gameLightnessAcc + gameChromaAcc + gameHueAcc + gamePerceivedAcc) / 4;
|
||||||
|
|
||||||
// make comparison texts
|
// make comparison texts
|
||||||
var lAccDeltaText = (roundLightnessAcc > historicalLightnessAcc ? "+" : "-") +
|
var lAccDeltaText = (gameLightnessAcc > historicalLightnessAcc ? "+" : "-") +
|
||||||
Math.Abs(roundLightnessAcc - historicalLightnessAcc).ToString("P");
|
Math.Abs(gameLightnessAcc - historicalLightnessAcc).ToString("F2");
|
||||||
var cAccDeltaText = (roundChromaAcc > historicalChromaAcc ? "+" : "-") +
|
var cAccDeltaText = (gameChromaAcc > historicalChromaAcc ? "+" : "-") +
|
||||||
Math.Abs(roundChromaAcc - historicalChromaAcc).ToString("P");
|
Math.Abs(gameChromaAcc - historicalChromaAcc).ToString("F2");
|
||||||
var hAccDeltaText = (roundHueAcc > historicalHueAcc ? "+" : "-") +
|
var hAccDeltaText = (gameHueAcc > historicalHueAcc ? "+" : "-") +
|
||||||
Math.Abs(roundHueAcc - historicalHueAcc).ToString("P");
|
Math.Abs(gameHueAcc - historicalHueAcc).ToString("F2");
|
||||||
|
|
||||||
var score = new LocalPlayerData.Score(DateTime.Now,
|
var score = new LocalPlayerData.Score(DateTime.Now,
|
||||||
playedRounds.Count,
|
playedRounds.Count,
|
||||||
(float)roundLightnessAcc,
|
(float)gameLightnessAcc,
|
||||||
(float)roundChromaAcc,
|
(float)gameChromaAcc,
|
||||||
(float)roundHueAcc,
|
(float)gameHueAcc,
|
||||||
(float)roundPerceivedAcc);
|
(float)gamePerceivedAcc);
|
||||||
|
|
||||||
|
var oldRating = _data.CalculateUserRating();
|
||||||
_data.RegisterLocalScore(score);
|
_data.RegisterLocalScore(score);
|
||||||
FireLocalPlayerDataChangeCallbacks(Instance.Data);
|
FireLocalPlayerDataChangeCallbacks(Instance.Data);
|
||||||
|
|
||||||
|
Debug.Log("submitting score to backend");
|
||||||
|
|
||||||
Backend.SubmitScore(score,
|
Backend.SubmitScore(score,
|
||||||
submitRes =>
|
submitRes =>
|
||||||
{
|
{
|
||||||
|
@ -301,14 +320,19 @@ public void SignalGameEnd(List<Gameplay.RoundInfo> playedRounds)
|
||||||
|
|
||||||
void TransitionToResultsView(float rating)
|
void TransitionToResultsView(float rating)
|
||||||
{
|
{
|
||||||
var ratingText = rating >= 0 ? $"\nYour rating is {rating:F}" : "\nYour rating could not be calculated.";
|
Debug.Log("signal GameManager-UIManager transition to results view");
|
||||||
|
|
||||||
|
var ratingDifferenceDescriptor = oldRating > rating ? "decreased" : "increased";
|
||||||
|
var ratingText = rating >= 0
|
||||||
|
? $"\nYour rating has {ratingDifferenceDescriptor} by {Math.Abs(rating - oldRating):F2}."
|
||||||
|
: "\nYour rating could not be calculated.";
|
||||||
|
|
||||||
// build the result text and show the results view
|
// build the result text and show the results view
|
||||||
ui.UI.Q<Label>("ResultsText").text = string.Join(Environment.NewLine, $"Over {playedRounds.Count} rounds,",
|
ui.UI.Q<Label>("ResultsText").text = string.Join(Environment.NewLine, $"Over {playedRounds.Count} rounds,",
|
||||||
$"you were {roundAcc} accurate.", "",
|
$"you were {gameAccuracy} accurate.", "",
|
||||||
$"Lightness was {roundLightnessAcc:P}% accurate. ({lAccDeltaText} from your average)",
|
$"Lightness was {gameLightnessAcc:F2}% accurate. ({lAccDeltaText} from your average)",
|
||||||
$"Chroma was {roundChromaAcc:P}% accurate. ({cAccDeltaText} from your average)",
|
$"Chroma was {gameChromaAcc:F2}% accurate. ({cAccDeltaText} from your average)",
|
||||||
$"Hue was {roundHueAcc:P}% accurate. ({hAccDeltaText} from your average)") + ratingText;
|
$"Hue was {gameHueAcc:F2}% accurate. ({hAccDeltaText} from your average)") + ratingText;
|
||||||
|
|
||||||
ui.SetDisplayState(UIManager.DisplayState.ResultsView);
|
ui.SetDisplayState(UIManager.DisplayState.ResultsView);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public class LocalPlayerData
|
public class LocalPlayerData
|
||||||
|
@ -16,10 +17,9 @@ public class LocalPlayerData
|
||||||
public const int MaxRecentLocalScores = 10;
|
public const int MaxRecentLocalScores = 10;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// queue of the best online scores,
|
/// the gamma value used in the exponential user rating calculation
|
||||||
/// used in user rating calculation and accuracy display stats
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Queue<Score> BestOnlineScores = new(20);
|
private const float ExponentialUserRatingGamma = 1.75f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// last known email used
|
/// last known email used
|
||||||
|
@ -34,13 +34,19 @@ public class LocalPlayerData
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// queue of the 10 most recent local scores
|
/// queue of the 10 most recent local scores
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Queue<Score> RecentLocalScores = new(10);
|
public readonly Queue<Score> RecentLocalScores = new(10);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// queue of the 10 most recent online scores,
|
/// queue of the 10 most recent online scores,
|
||||||
/// used in user rating calculation and accuracy display stats
|
/// used in user rating calculation and accuracy display stats
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Queue<Score> RecentOnlineScores = new(10);
|
public readonly Queue<Score> RecentOnlineScores = new(10);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// queue of the best online scores,
|
||||||
|
/// used in user rating calculation and accuracy display stats
|
||||||
|
/// </summary>
|
||||||
|
public readonly Queue<Score> BestOnlineScores = new(20);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// loads player data from player prefs and database
|
/// loads player data from player prefs and database
|
||||||
|
@ -92,6 +98,10 @@ public void LoadFromTheWorld(Action<LocalPlayerData> callback)
|
||||||
Math.Clamp(c, 0f, 100f), Math.Clamp(h, 0f, 100f)));
|
Math.Clamp(c, 0f, 100f), Math.Clamp(h, 0f, 100f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug.Log(
|
||||||
|
$"loaded lpdata from the local world ({LastKnownUsername} <{LastKnownEmail}> with RLS.Count={RecentLocalScores.Count}, ROS.Count={RecentOnlineScores.Count}");
|
||||||
|
callback(this);
|
||||||
|
|
||||||
// load online scores
|
// load online scores
|
||||||
RecentOnlineScores.Clear();
|
RecentOnlineScores.Clear();
|
||||||
GameManager.Instance.Backend.GetRecentScores((_, recentOnlineScores) =>
|
GameManager.Instance.Backend.GetRecentScores((_, recentOnlineScores) =>
|
||||||
|
@ -103,7 +113,7 @@ public void LoadFromTheWorld(Action<LocalPlayerData> callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Log(
|
Debug.Log(
|
||||||
$"loaded lpdata from the world ({LastKnownUsername} <{LastKnownEmail}> with RLS.Count={RecentLocalScores.Count}, ROS.Count={RecentOnlineScores.Count}");
|
$"loaded lpdata from the online world (now {LastKnownUsername} <{LastKnownEmail}> with RLS.Count={RecentLocalScores.Count}, ROS.Count={RecentOnlineScores.Count}");
|
||||||
|
|
||||||
callback(this);
|
callback(this);
|
||||||
});
|
});
|
||||||
|
@ -149,7 +159,7 @@ public void RegisterLocalScore(Score score)
|
||||||
/// <returns>the user rating (0-100f)</returns>
|
/// <returns>the user rating (0-100f)</returns>
|
||||||
public float CalculateUserRating()
|
public float CalculateUserRating()
|
||||||
{
|
{
|
||||||
// user rating is like CHUNITHM's rating system
|
// user rating is like CHUNITHMs rating system
|
||||||
// where best + recent scores are averaged out
|
// where best + recent scores are averaged out
|
||||||
// in this case 20 best scores, and 10 recent scores are used
|
// in this case 20 best scores, and 10 recent scores are used
|
||||||
|
|
||||||
|
@ -165,7 +175,7 @@ public float CalculateUserRating()
|
||||||
var scores = 0;
|
var scores = 0;
|
||||||
var totalRating = 0d;
|
var totalRating = 0d;
|
||||||
|
|
||||||
foreach (var score in recentScores)
|
foreach (var score in recentScores.Take(10))
|
||||||
{
|
{
|
||||||
scores++;
|
scores++;
|
||||||
var dL = score.AvgLightnessAccuracy;
|
var dL = score.AvgLightnessAccuracy;
|
||||||
|
@ -174,10 +184,10 @@ public float CalculateUserRating()
|
||||||
var dE = Math.Sqrt(score.AvgLightnessAccuracy * score.AvgLightnessAccuracy
|
var dE = Math.Sqrt(score.AvgLightnessAccuracy * score.AvgLightnessAccuracy
|
||||||
+ score.AvgChromaAccuracy * score.AvgChromaAccuracy
|
+ score.AvgChromaAccuracy * score.AvgChromaAccuracy
|
||||||
+ score.AvgHueAccuracy * score.AvgHueAccuracy);
|
+ score.AvgHueAccuracy * score.AvgHueAccuracy);
|
||||||
totalRating = (dL + dC + dH + dE) / 4d;
|
totalRating += (dL + dC + dH + dE + dE) / 5d;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var score in bestScores)
|
foreach (var score in bestScores.Take(20))
|
||||||
{
|
{
|
||||||
scores++;
|
scores++;
|
||||||
var dL = score.AvgLightnessAccuracy;
|
var dL = score.AvgLightnessAccuracy;
|
||||||
|
@ -186,10 +196,18 @@ public float CalculateUserRating()
|
||||||
var dE = Math.Sqrt(score.AvgLightnessAccuracy * score.AvgLightnessAccuracy
|
var dE = Math.Sqrt(score.AvgLightnessAccuracy * score.AvgLightnessAccuracy
|
||||||
+ score.AvgChromaAccuracy * score.AvgChromaAccuracy
|
+ score.AvgChromaAccuracy * score.AvgChromaAccuracy
|
||||||
+ score.AvgHueAccuracy * score.AvgHueAccuracy);
|
+ score.AvgHueAccuracy * score.AvgHueAccuracy);
|
||||||
totalRating = (dL + dC + dH + dE) / 4d;
|
totalRating += (dL + dC + dH + dE + dE) / 5d;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Math.Clamp((float)(totalRating / scores), 0f, 100f);
|
scores = Math.Max(1, scores);
|
||||||
|
totalRating /= scores;
|
||||||
|
|
||||||
|
var rawUserRating = Math.Clamp(totalRating, 0d, 100d);
|
||||||
|
var exponentialRating = 100d * Math.Pow(rawUserRating / 100d, ExponentialUserRatingGamma);
|
||||||
|
|
||||||
|
Debug.Log($"locally calculated user rating: lin: {rawUserRating} -> exp: {exponentialRating}");
|
||||||
|
|
||||||
|
return (float)exponentialRating;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Score
|
public struct Score
|
||||||
|
@ -232,7 +250,8 @@ public struct Score
|
||||||
/// <param name="l">average lightness accuracy across all rounds (0-100)</param>
|
/// <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="c">average chroma accuracy across all rounds (0-100)</param>
|
||||||
/// <param name="h">average hue 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>
|
/// ///
|
||||||
|
/// <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,
|
public Score(DateTime timestamp = new(), int noOfRounds = 1, float l = 100.0f, float c = 100.0f,
|
||||||
float h = 100.0f, float e = 100.0f)
|
float h = 100.0f, float e = 100.0f)
|
||||||
{
|
{
|
||||||
|
@ -256,23 +275,33 @@ public Score(Dictionary<string, object> data)
|
||||||
|
|
||||||
if (!data.ContainsKey("timestamp") || data["timestamp"] is not long timestamp)
|
if (!data.ContainsKey("timestamp") || data["timestamp"] is not long timestamp)
|
||||||
throw new ArgumentException("timestamp not found or invalid");
|
throw new ArgumentException("timestamp not found or invalid");
|
||||||
if (!data.ContainsKey("noOfRounds") || data["noOfRounds"] is not int noOfRounds)
|
if (!data.ContainsKey("noOfRounds") || data["noOfRounds"] is not long noOfRounds)
|
||||||
throw new ArgumentException("noOfRounds not found or invalid");
|
throw new ArgumentException("noOfRounds not found or invalid");
|
||||||
if (!data.ContainsKey("avgLightnessAccuracy") || data["avgLightnessAccuracy"] is not float avgLightnessAccuracy)
|
|
||||||
throw new ArgumentException("avgLightnessAccuracy not found or invalid");
|
var avgLightnessAccuracy = GetFloatyKey("avgLightnessAccuracy");
|
||||||
if (!data.ContainsKey("avgChromaAccuracy") || data["avgChromaAccuracy"] is not float avgChromaAccuracy)
|
var avgChromaAccuracy = GetFloatyKey("avgChromaAccuracy");
|
||||||
throw new ArgumentException("avgChromaAccuracy not found or invalid");
|
var avgHueAccuracy = GetFloatyKey("avgHueAccuracy");
|
||||||
if (!data.ContainsKey("avgHueAccuracy") || data["avgHueAccuracy"] is not float avgHueAccuracy)
|
var avgPerceivedAccuracy = GetFloatyKey("avgPerceivedAccuracy");
|
||||||
throw new ArgumentException("avgHueAccuracy not found or invalid");
|
|
||||||
if (!data.ContainsKey("avgPerceivedAccuracy") || data["avgPerceivedAccuracy"] is not float avgPerceivedAccuracy)
|
|
||||||
throw new ArgumentException("avgPerceivedAccuracy not found or invalid");
|
|
||||||
|
|
||||||
Timestamp = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime;
|
Timestamp = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime;
|
||||||
NoOfRounds = noOfRounds;
|
NoOfRounds = (int)noOfRounds;
|
||||||
AvgLightnessAccuracy = avgLightnessAccuracy;
|
AvgLightnessAccuracy = avgLightnessAccuracy;
|
||||||
AvgChromaAccuracy = avgChromaAccuracy;
|
AvgChromaAccuracy = avgChromaAccuracy;
|
||||||
AvgHueAccuracy = avgHueAccuracy;
|
AvgHueAccuracy = avgHueAccuracy;
|
||||||
AvgPerceivedAccuracy = avgPerceivedAccuracy;
|
AvgPerceivedAccuracy = avgPerceivedAccuracy;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
float GetFloatyKey(string key)
|
||||||
|
{
|
||||||
|
if (!data.TryGetValue(key, out var possibleFloat)) throw new ArgumentException($"{key} not found");
|
||||||
|
return possibleFloat switch
|
||||||
|
{
|
||||||
|
double f => (float)f,
|
||||||
|
long l => l,
|
||||||
|
_ => throw new ArgumentException($"{key} not a valid float")
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -105,6 +105,8 @@ private static void OnAccountButtonClicked()
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void RenderFromPlayerData(LocalPlayerData data)
|
private void RenderFromPlayerData(LocalPlayerData data)
|
||||||
{
|
{
|
||||||
|
Debug.Log("updating SideView > AccountSection with new player data");
|
||||||
|
|
||||||
// calculate averages from both recent local scores and online scores
|
// calculate averages from both recent local scores and online scores
|
||||||
var totalLightnessAcc = 0f;
|
var totalLightnessAcc = 0f;
|
||||||
var totalChromaAcc = 0f;
|
var totalChromaAcc = 0f;
|
||||||
|
@ -149,9 +151,9 @@ private void RenderFromPlayerData(LocalPlayerData data)
|
||||||
|
|
||||||
// finally, set the labels
|
// finally, set the labels
|
||||||
_playerText.text = playerText;
|
_playerText.text = playerText;
|
||||||
|
_ratingText.text = $"{rating:F}";
|
||||||
_lightnessAccuracyText.text = $"{lightnessAcc:F}";
|
_lightnessAccuracyText.text = $"{lightnessAcc:F}";
|
||||||
_chromaAccuracyText.text = $"{chromaAcc:F}";
|
_chromaAccuracyText.text = $"{chromaAcc:F}";
|
||||||
_hueAccuracyText.text = $"{hueAcc:F}";
|
_hueAccuracyText.text = $"{hueAcc:F}";
|
||||||
_ratingText.text = $"{rating:F}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -132,7 +132,7 @@
|
||||||
</ui:VisualElement>
|
</ui:VisualElement>
|
||||||
</ui:VisualElement>
|
</ui:VisualElement>
|
||||||
<ui:VisualElement name="ResultsView"
|
<ui:VisualElement name="ResultsView"
|
||||||
style="flex-grow: 1; display: flex; margin-top: 3.25%; margin-right: 3.25%; margin-bottom: 3.25%; margin-left: 3.25%; justify-content: space-between;">
|
style="flex-grow: 1; display: none; margin-top: 3.25%; margin-right: 3.25%; margin-bottom: 3.25%; margin-left: 3.25%; justify-content: space-between;">
|
||||||
<ui:VisualElement name="ColourShowcase"
|
<ui:VisualElement name="ColourShowcase"
|
||||||
style="flex-grow: 1; background-color: rgb(255, 255, 255); border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-right-radius: 8px; border-bottom-left-radius: 8px; padding-top: 2%; padding-right: 2%; padding-bottom: 2%; padding-left: 2%; margin-bottom: 2%; margin-top: 0; margin-right: 0; margin-left: 0;">
|
style="flex-grow: 1; background-color: rgb(255, 255, 255); border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-right-radius: 8px; border-bottom-left-radius: 8px; padding-top: 2%; padding-right: 2%; padding-bottom: 2%; padding-left: 2%; margin-bottom: 2%; margin-top: 0; margin-right: 0; margin-left: 0;">
|
||||||
<ui:Label tabindex="-1" text="Templates" parse-escape-sequences="true"
|
<ui:Label tabindex="-1" text="Templates" parse-escape-sequences="true"
|
||||||
|
@ -202,7 +202,7 @@
|
||||||
<ui:ListView name="LeaderboardListView"/>
|
<ui:ListView name="LeaderboardListView"/>
|
||||||
</ui:VisualElement>
|
</ui:VisualElement>
|
||||||
<ui:VisualElement name="AccountView"
|
<ui:VisualElement name="AccountView"
|
||||||
style="flex-grow: 1; display: none; padding-top: 0; padding-right: 0; padding-bottom: 0; padding-left: 0; margin-top: 3.25%; margin-right: 3.25%; margin-bottom: 3.25%; margin-left: 3.25%; flex-direction: column; justify-content: space-between;">
|
style="flex-grow: 1; display: flex; padding-top: 0; padding-right: 0; padding-bottom: 0; padding-left: 0; margin-top: 3.25%; margin-right: 3.25%; margin-bottom: 3.25%; margin-left: 3.25%; flex-direction: column; justify-content: space-between;">
|
||||||
<ui:Label tabindex="-1" text="You are not signed in." parse-escape-sequences="true"
|
<ui:Label tabindex="-1" text="You are not signed in." parse-escape-sequences="true"
|
||||||
display-tooltip-when-elided="true" name="AccountHeader"
|
display-tooltip-when-elided="true" name="AccountHeader"
|
||||||
style="font-size: 58px; -unity-font-style: normal;"/>
|
style="font-size: 58px; -unity-font-style: normal;"/>
|
||||||
|
@ -236,6 +236,9 @@
|
||||||
style="-unity-text-align: middle-center; margin-bottom: 1%; margin-right: 1%; margin-top: 1%; -unity-font-style: bold;"/>
|
style="-unity-text-align: middle-center; margin-bottom: 1%; margin-right: 1%; margin-top: 1%; -unity-font-style: bold;"/>
|
||||||
<ui:Button text="Secondary Action Button →" parse-escape-sequences="true"
|
<ui:Button text="Secondary Action Button →" parse-escape-sequences="true"
|
||||||
display-tooltip-when-elided="true" name="SecondaryActionButton"
|
display-tooltip-when-elided="true" name="SecondaryActionButton"
|
||||||
|
style="-unity-text-align: middle-center; margin-bottom: 1%; margin-right: 1%; margin-top: 1%; -unity-font-style: bold;"/>
|
||||||
|
<ui:Button text="Tertiary Action Button →" parse-escape-sequences="true"
|
||||||
|
display-tooltip-when-elided="true" name="TertiaryActionButton"
|
||||||
style="margin-top: 1%; margin-right: 1%; -unity-font-style: bold;"/>
|
style="margin-top: 1%; margin-right: 1%; -unity-font-style: bold;"/>
|
||||||
</ui:VisualElement>
|
</ui:VisualElement>
|
||||||
</ui:VisualElement>
|
</ui:VisualElement>
|
||||||
|
|
Reference in a new issue