diff --git a/ColourMeOKGame/Assets/Scenes/GameScene.unity b/ColourMeOKGame/Assets/Scenes/GameScene.unity
index 87fe1f5..c80fb31 100644
--- a/ColourMeOKGame/Assets/Scenes/GameScene.unity
+++ b/ColourMeOKGame/Assets/Scenes/GameScene.unity
@@ -143,7 +143,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
- m_IsActive: 1
+ m_IsActive: 0
--- !u!114 &133964671
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -258,7 +258,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
- m_IsActive: 1
+ m_IsActive: 0
--- !u!4 &447905427
Transform:
m_ObjectHideFlags: 0
@@ -476,7 +476,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
- m_IsActive: 1
+ m_IsActive: 0
--- !u!114 &1204483825
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -505,6 +505,37 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1680304394
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1680304395}
+ m_Layer: 0
+ m_Name: GameObject
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1680304395
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1680304394}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
@@ -514,3 +545,4 @@ SceneRoots:
- {fileID: 133964672}
- {fileID: 447905427}
- {fileID: 1204483826}
+ - {fileID: 1680304395}
diff --git a/ColourMeOKGame/Assets/Scripts/AccountUI.cs b/ColourMeOKGame/Assets/Scripts/AccountUI.cs
index 1b737a0..98a9e85 100644
--- a/ColourMeOKGame/Assets/Scripts/AccountUI.cs
+++ b/ColourMeOKGame/Assets/Scripts/AccountUI.cs
@@ -18,12 +18,12 @@ public class AccountUI : MonoBehaviour
///
/// default text colour
///
- private readonly Color _defaultInputFieldValueTextColour = new(5.88f, 5.1f, 10.59f);
+ private readonly StyleColor _defaultInputFieldValueTextColour = new(new Color(0.0588f, 0.051f, 0.1059f));
///
/// error text colour
///
- private readonly Color _errorInputFieldValueTextColour = new(1f, 50.59f, 50.2f);
+ private readonly StyleColor _errorInputFieldValueTextColour = new Color(1f, 0.5059f, 0.502f);
///
/// accompanying text for the account input fields, used when an error/notice is needed
diff --git a/ColourMeOKGame/Assets/Scripts/Backend.cs b/ColourMeOKGame/Assets/Scripts/Backend.cs
index 0f2431d..cc99b08 100644
--- a/ColourMeOKGame/Assets/Scripts/Backend.cs
+++ b/ColourMeOKGame/Assets/Scripts/Backend.cs
@@ -175,7 +175,7 @@ public void Cleanup()
_auth.StateChanged -= AuthStateChanged;
_auth = null;
}
-
+
///
/// function to register a callback for when the user signs in
///
@@ -195,7 +195,7 @@ public void RegisterOnSignOutCallback(Action callback)
_onSignOutCallbacks.Add(callback);
Debug.Log($"registering OnSignOutCallback ({_onSignOutCallbacks.Count})");
}
-
+
///
/// function to register a callback for when the connection status changes
///
@@ -205,7 +205,7 @@ public void RegisterOnConnectionStatusChangedCallback(Action
/// function to fire all on sign in callbacks
///
@@ -239,7 +239,7 @@ private void FireOnSignOutCallbacks()
Debug.LogError($"error invoking OnSignOutCallback: {e.Message}");
}
}
-
+
///
/// function to fire all on connection status changed callbacks
///
@@ -519,6 +519,19 @@ public void GetRecentScores(Action(0));
}
+ ///
+ /// abstraction function to get the user's best scores from the database
+ ///
+ ///
+ /// callback function that takes in a DatabaseTransactionResult enum and a
+ /// List<LocalPlayerData.Score>
+ ///
+ public void GetBestScores(Action> callback)
+ {
+ // TODO: implement this
+ callback(DatabaseTransactionResult.Error, new List(0));
+ }
+
///
/// abstraction function to submit a score to the database
///
@@ -528,7 +541,29 @@ public void GetRecentScores(Action callback)
{
- throw new NotImplementedException();
+ if (!Status.Equals(FirebaseConnectionStatus.Connected)) return;
+
+ if (_user == null)
+ {
+ callback(DatabaseTransactionResult.Unauthenticated);
+ return;
+ }
+
+ _db.Child("scores")
+ .Push()
+ .SetValueAsync(score.ToDictionary())
+ .ContinueWithOnMainThread(task =>
+ {
+ if (task.IsCompletedSuccessfully)
+ {
+ callback(DatabaseTransactionResult.Ok);
+ }
+ else
+ {
+ Debug.LogError(task.Exception);
+ callback(DatabaseTransactionResult.Error);
+ }
+ });
}
///
@@ -542,16 +577,42 @@ public void GetRecentScores(Action callback)
{
- throw new NotImplementedException();
+ GetRecentScores((recentRes, recentScores) =>
+ {
+ if (recentRes == DatabaseTransactionResult.Error)
+ {
+ Debug.Log("failed to get recent scores");
+ callback(DatabaseTransactionResult.Error, 0f);
+ return;
+ }
+
+ var recentScoreQueue = GameManager.Instance.Data.RecentOnlineScores;
+ foreach (var score in recentScores) recentScoreQueue.Enqueue(score);
+ while (recentScoreQueue.Count > LocalPlayerData.MaxBestOnlineScores) recentScoreQueue.Dequeue();
+
+ GetBestScores((bestRes, bestScores) =>
+ {
+ if (bestRes == DatabaseTransactionResult.Error)
+ {
+ Debug.Log("failed to get recent scores");
+ callback(DatabaseTransactionResult.Error, 0f);
+ return;
+ }
+
+ var bestScoreQueue = GameManager.Instance.Data.BestOnlineScores;
+ foreach (var score in recentScores) bestScoreQueue.Enqueue(score);
+ while (bestScoreQueue.Count > LocalPlayerData.MaxBestOnlineScores) bestScoreQueue.Dequeue();
+
+ callback(DatabaseTransactionResult.Ok, GameManager.Instance.Data.CalculateUserRating());
+ });
+ });
}
///
/// abstraction function to update the user's rating in the database
///
- /// new user rating value as a float
/// callback function that takes in a DatabaseTransactionResult enum
public void UpdateUserRating(
- float newRating,
Action callback)
{
throw new NotImplementedException();
diff --git a/ColourMeOKGame/Assets/Scripts/Colorimetry.cs b/ColourMeOKGame/Assets/Scripts/Colorimetry.cs
index 8b19f80..2819747 100644
--- a/ColourMeOKGame/Assets/Scripts/Colorimetry.cs
+++ b/ColourMeOKGame/Assets/Scripts/Colorimetry.cs
@@ -3,10 +3,58 @@
public static class Colorimetry
{
+ ///
+ /// calculate a 0-100% distance/accuracy between two unity rgba colour objects
+ ///
+ /// the template colour to compare against
+ /// the response colour to compare
+ /// a DeltaLabCHE struct
+ public static DeltaLabCHE CalculateDistance(Color template, Color response)
+ {
+ // rgb to oklab
+ var templateOklab = linear_srgb_to_oklab(new RGB(
+ (float)srgb_nonlinear_transform_f_inv(template.r),
+ (float)srgb_nonlinear_transform_f_inv(template.g),
+ (float)srgb_nonlinear_transform_f_inv(template.b)));
+
+ var responseOklab = linear_srgb_to_oklab(new RGB(
+ (float)srgb_nonlinear_transform_f_inv(response.r),
+ (float)srgb_nonlinear_transform_f_inv(response.g),
+ (float)srgb_nonlinear_transform_f_inv(response.b)));
+
+ // https://en.wikipedia.org/wiki/Oklab_color_space#Color_differences
+ // ... "The perceptual color difference in Oklab is calculated as the Euclidean
+ // ... distance between the (L, a, b) coordinates."
+ // https://github.com/svgeesus/svgeesus.github.io/blob/master/Color/OKLab-notes.md#color-difference-metric
+ // ... ΔL = L1 - L2
+ // ... C1 = √(a1² + b1²) -> chroma values
+ // ... C2 = √(a2² + b2²) -> chroma values
+ // ... ΔC = C1 - C2 -> chroma difference
+ // ... Δa = a1 - a2
+ // ... Δb = b1 - b2
+ // ... ΔH = √(Δa² + Δb² - ΔC²) -> hue difference
+ // ... ΔE = √(ΔL² + ΔC² + ΔH²) -> final difference
+
+ float l1, a1, b1, l2, a2, b2;
+ (l1, a1, b1) = (templateOklab.L, templateOklab.a, templateOklab.b);
+ (l2, a2, b2) = (responseOklab.L, responseOklab.a, responseOklab.b);
+
+ var deltaL = l1 - l2;
+ var c1 = Math.Sqrt(a1 * a1 + b1 * b1);
+ var c2 = Math.Sqrt(a2 * a2 + b2 * b2);
+ var deltaC = c1 - c2;
+ var deltaA = a1 - a2;
+ var deltaB = b1 - b2;
+ var deltaH = Math.Max(0d, Math.Sqrt(deltaA * deltaA + deltaB * deltaB - deltaC * deltaC));
+ var deltaE = Math.Sqrt(deltaL * deltaL + deltaC * deltaC + deltaH * deltaH);
+
+ return new DeltaLabCHE(deltaL, deltaA, deltaB, deltaC, deltaH, deltaE);
+ }
+
///
/// convert the oklch colour to a unity rgba colour object
///
- /// a unity rgba color object
+ /// a unity rgba Color object
public static Color RawLchToColor(double lightness, double chroma, double hue)
{
// clamp values
@@ -55,7 +103,7 @@ public static double srgb_nonlinear_transform_f_inv(double x)
}
///
- /// clips a color to the sRGB gamut while preserving chroma
+ /// clips a colour to the sRGB gamut while preserving chroma
///
// https://bottosson.github.io/posts/gamutclipping/ (MIT)
public static RGB gamut_clip_preserve_chroma(RGB rgb)
@@ -331,6 +379,48 @@ public static float compute_max_saturation(float a, float b)
return maxSaturation;
}
+ // ReSharper disable once InconsistentNaming
+ public struct DeltaLabCHE
+ {
+ // ReSharper disable once InconsistentNaming
+ public double dL;
+
+ // ReSharper disable once InconsistentNaming
+ public double da;
+
+ // ReSharper disable once InconsistentNaming
+ public double db;
+
+ // ReSharper disable once InconsistentNaming
+ public double dC;
+
+ // ReSharper disable once InconsistentNaming
+ public double dH;
+
+ // ReSharper disable once InconsistentNaming
+ public double dE;
+
+ public DeltaLabCHE(
+ // ReSharper disable once InconsistentNaming
+ double L,
+ double a,
+ double b,
+ // ReSharper disable once InconsistentNaming
+ double C,
+ // ReSharper disable once InconsistentNaming
+ double H,
+ // ReSharper disable once InconsistentNaming
+ double E)
+ {
+ dL = L;
+ da = a;
+ db = b;
+ dC = C;
+ dH = H;
+ dE = E;
+ }
+ }
+
public readonly struct Lab
{
public readonly float L;
diff --git a/ColourMeOKGame/Assets/Scripts/GameManager.cs b/ColourMeOKGame/Assets/Scripts/GameManager.cs
index 5518b5c..d702e8a 100644
--- a/ColourMeOKGame/Assets/Scripts/GameManager.cs
+++ b/ColourMeOKGame/Assets/Scripts/GameManager.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
@@ -34,7 +35,7 @@ public class GameManager : MonoBehaviour
public Backend Backend;
///
- /// read-only property for accessing the local player data outside of this class
+ /// read-only property for accessing the local player data outside this class
///
public LocalPlayerData Data => _data;
@@ -107,16 +108,16 @@ private void OnEnable()
Debug.Log("sign in callback, refreshing GameManager-controlled SideView UI");
_data.LoadFromTheWorld(FireLocalPlayerDataChangeCallbacks);
});
-
+
Backend.RegisterOnConnectionStatusChangedCallback(status =>
{
Debug.Log($"post-fcStatus change, deciding to show/hide buttons based on new status: {status}");
ui.UI.Q