101 Commits

Author SHA1 Message Date
d70725750a New macos build 2023-04-21 12:28:10 +02:00
Dries Van Schuylenbergh
76b4bdd596 Change A hand gesture sprite 2023-04-20 12:31:09 +02:00
Dries Van Schuylenbergh
3246e1022c Fix fonts 2023-04-20 11:54:29 +02:00
Dries Van Schuylenbergh
52ebd0e67a Change to NL 2023-04-20 11:41:44 +02:00
Dries Van Schuylenbergh
7cac7aa9ca Prepare for D-Day 2023-04-19 23:06:59 +02:00
Dries Van Schuylenbergh
92c3101db8 Fix missing TextAsset + other panel use + double use of 'Slecht' sign 2023-04-19 22:22:38 +02:00
CoudronJerome
5ee7add6b9 Added basicsigns videos and added "Slecht" to the basicSigns 2023-04-19 19:51:08 +02:00
ec60f7d8e3 Changed tresholds 2023-04-19 18:32:44 +02:00
Dries Van Schuylenbergh
9422df7083 Implement basic signs course 2023-04-19 17:09:11 +02:00
Dries Van Schuylenbergh
db1a72fadd Merge remote-tracking branch 'origin/SpoterEmbedding' into demo-day 2023-04-19 16:05:22 +02:00
db96a700e8 Implemented Embedding Signpredictor 2023-04-19 13:14:06 +02:00
Dries Van Schuylenbergh
ab6c817667 Fix broken font (again) 2023-04-18 21:54:48 +02:00
Dries Van Schuylenbergh
e8de984ba1 Implement scene transitions 2023-04-18 20:16:53 +02:00
Dries Van Schuylenbergh
2ba62e316f Implement demo day changes - DO NOT MERGE 2023-04-17 22:15:26 +02:00
5b4a3ec4e7 New MacOS build 2023-04-11 19:15:53 +02:00
Louis Adriaens
77850bfbe6 Merge branch 'UI-resolution-fix' into 'development'
Fixed screen scaling

See merge request wesign/unity-application!82
2023-04-11 16:04:01 +00:00
Supaaah1
f9fff14662 Fixed screen scaling 2023-04-11 16:57:21 +02:00
aed09649e3 Added MacOS build 2023-04-10 19:47:35 +02:00
Jelle De Geest
c8dfa96c8f Merge branch 'WES-133-multiple-choice' into 'development'
Resolve WES-133 "Multiple choice"

See merge request wesign/unity-application!77
2023-04-10 15:05:13 +00:00
Tibe Habils
4e9d801e61 Resolve WES-133 "Multiple choice" 2023-04-10 15:05:11 +00:00
Helena Van Breugel
04d9a4bf2b Merge branch 'WES-144-feedback-JustSign' into 'development'
Resolve WES-144 "Feedback justsign"

See merge request wesign/unity-application!79
2023-04-08 20:28:46 +00:00
Helena Van Breugel
2b71bde592 Resolve WES-144 "Feedback justsign" 2023-04-08 20:28:42 +00:00
Dries Van Schuylenbergh
73c4756f19 Merge branch 'WES-XX-persistent-data-controller-backwards-compatible' into 'development'
Fix bug

See merge request wesign/unity-application!80
2023-04-08 19:24:46 +00:00
Dries Van Schuylenbergh
f5615bbef3 Fix bug 2023-04-08 11:31:24 +02:00
Helena Van Breugel
8670fcc4ce Merge branch 'WES-146-UI-Take2' into 'development'
WES-146-UI

See merge request wesign/unity-application!78
2023-04-06 07:20:42 +00:00
Jelle De Geest
e6d10db5ca WES-146-UI 2023-04-06 07:20:41 +00:00
Jelle De Geest
8db7c80dad Merge branch 'WES-128-Hangman-Clear-Feedback' into 'development'
Resolve WES-128-Hangman-Clear_feedback

See merge request wesign/unity-application!74
2023-04-05 20:54:20 +00:00
Jerome Coudron
197a6200c1 Resolve WES-128-Hangman-Clear_feedback 2023-04-05 20:54:20 +00:00
Jelle De Geest
68372d859b Merge branch 'WES-130-feedback-spellingbee' into 'development'
Feedback SpellingBee

See merge request wesign/unity-application!76
2023-04-05 13:40:55 +00:00
Helena Van Breugel
2a7dd16f5f Feedback SpellingBee 2023-04-05 13:40:54 +00:00
Jerome Coudron
e6b0cbd596 Merge branch 'WES-143-feedback-courses' into 'development'
Resolve WES-143 "Feedback courses"

See merge request wesign/unity-application!73
2023-04-04 18:24:20 +00:00
Helena Van Breugel
c358ac59e4 Resolve WES-143 "Feedback courses" 2023-04-04 18:24:17 +00:00
Dries Van Schuylenbergh
c20cd89c3a Merge branch 'WES-117-persistent-data-handling' into 'development'
Resolve WES-117 "Persistent data handling"

See merge request wesign/unity-application!72
2023-04-04 17:00:50 +00:00
Dries Van Schuylenbergh
5f4408063f Resolve WES-117 "Persistent data handling" 2023-04-04 17:00:47 +00:00
3499e61bb0 Added CoreML model (for Mac) 2023-04-03 20:52:01 +02:00
Jelle De Geest
aefc75f3c4 Merge branch 'WES-100-NatML-Integration' into 'development'
Resolve WES-100 "Natml integration"

See merge request wesign/unity-application!70
2023-04-03 14:14:50 +00:00
Jelle De Geest
f95e34c6fe Resolve WES-100 "Natml integration" 2023-04-03 14:14:49 +00:00
Dries Van Schuylenbergh
edf1805a92 Merge branch 'WES-131-Feedback-Refactor-Manual-Merge' into 'development'
Resolve WES-131-Feedback-REfactor

See merge request wesign/unity-application!71
2023-04-02 12:28:00 +00:00
Jerome Coudron
a808e73a29 Resolve WES-131-Feedback-REfactor 2023-04-02 12:27:59 +00:00
Jelle De Geest
b955d2164c Merge branch 'WES-145-Consistency-controllers' into 'development'
Resolve WES-145 "Consistency controllers"

See merge request wesign/unity-application!68
2023-03-30 21:28:50 +00:00
Helena Van Breugel
e96f1ff7ca Resolve WES-145 "Consistency controllers" 2023-03-30 21:28:48 +00:00
Jelle De Geest
b8cdcd128b Merge branch 'WES-127-Refactor-ThemeList' into 'development'
Resolve WES-127-Refactor_Themelist

See merge request wesign/unity-application!69
2023-03-30 21:10:39 +00:00
Jerome Coudron
ea13788199 Resolve WES-127-Refactor_Themelist 2023-03-30 21:10:35 +00:00
Tibe Habils
564ec209c7 Add windows installer 2023-03-26 20:42:59 +02:00
a3735af649 Added macos build 2023-03-26 22:27:44 +02:00
Victor Mylle
692b4318bf Update .drone.yml 2023-03-26 19:36:48 +00:00
Jelle De Geest
0015c1453c Merge branch 'WES-88-Scoreboard' into 'development'
Resolve WES-88 "Scoreboard"

See merge request wesign/unity-application!66
2023-03-26 18:01:52 +00:00
Helena Van Breugel
c78ef0e773 Resolve WES-88 "Scoreboard" 2023-03-26 18:01:51 +00:00
Dries Van Schuylenbergh
45f545b13f Merge branch 'WES-112-windows-installer' into 'development'
Added basic installer

See merge request wesign/unity-application!65
2023-03-26 14:22:45 +00:00
Tibe Habils
d3af75f676 Added basic installer 2023-03-26 14:22:42 +00:00
Tibe Habils
5f7216f24c Merge branch 'hot-fix-common-tests' into 'development'
common tests fix

See merge request wesign/unity-application!61
2023-03-26 11:47:52 +00:00
Tibe Habils
b012e40df8 common tests fix 2023-03-26 11:47:48 +00:00
Jerome Coudron
f69e2385fc Merge branch 'WES-110-spelling-bee-tests' into 'development'
Spelling bee tests

See merge request wesign/unity-application!58
2023-03-25 22:47:27 +00:00
Tibe Habils
37905a904c Spelling bee tests 2023-03-25 22:47:25 +00:00
Dries Van Schuylenbergh
0019a4d07f Merge branch 'WES-XX-ScriptableList-Index-Fix' into 'development'
Resolve WES-XX-ScriptableList-Index-Fix

See merge request wesign/unity-application!62
2023-03-25 17:16:42 +00:00
Jerome Coudron
4402e80d0c Resolve WES-XX-ScriptableList-Index-Fix 2023-03-25 17:16:41 +00:00
Jelle De Geest
d95a6590d5 Merge branch 'test-accounts' into 'development'
Test accounts

See merge request wesign/unity-application!60
2023-03-25 15:35:27 +00:00
Dries Van Schuylenbergh
7b6eb4db69 Test accounts 2023-03-25 15:35:26 +00:00
Jerome Coudron
8696aff135 Merge branch 'Common-Interfaces-Tests' into 'development'
Resolve Common-Interfaces-Tests

See merge request wesign/unity-application!63
2023-03-25 13:12:56 +00:00
Jerome Coudron
001cf6dedb Resolve Common-Interfaces-Tests 2023-03-25 13:12:55 +00:00
Lukas Van Rossem
669bcb3793 Merge branch 'WES-89-justsign-music' into 'development'
Resolve WES-89 "Justsign music"

See merge request wesign/unity-application!64
2023-03-25 11:34:09 +00:00
Lukas Van Rossem
668b769094 Resolve WES-89 "Justsign music" 2023-03-25 11:34:08 +00:00
Victor Mylle
fee989006c Update .drone.yml 2023-03-24 09:12:39 +00:00
Supaaah1
b3913c281a Review fix 2023-03-23 13:21:43 +01:00
Louis Adriaens
b0c0cdc095 Merge branch 'progress-fix' into 'development'
Fix CourseProgressCard prefab missing script reference

See merge request wesign/unity-application!59
2023-03-23 12:09:44 +00:00
Dries Van Schuylenbergh
db37a16155 Fix CourseProgressCard prefab missing script reference 2023-03-23 12:09:43 +00:00
Tibe Habils
9c7366e49c Merge branch 'WES-113-commons-tests' into 'development'
Resolve WES-113 "Commons tests"

See merge request wesign/unity-application!56
2023-03-23 11:35:15 +00:00
Tibe Habils
5a5a1f1542 Resolve WES-113 "Commons tests" 2023-03-23 11:35:14 +00:00
Louis Adriaens
0e997666e2 Merge branch 'New_Model' into 'development'
Added model from A - Z

See merge request wesign/unity-application!54
2023-03-23 11:10:58 +00:00
Victor Mylle
6ef35cabd4 Added model from A - Z 2023-03-23 11:10:55 +00:00
Tibe Habils
2581cd6137 Merge branch 'Code-Coverage' into 'development'
Code coverage

See merge request wesign/unity-application!57
2023-03-22 22:03:45 +00:00
Victor Mylle
800f0ae77f Code coverage 2023-03-22 22:03:44 +00:00
Dries Van Schuylenbergh
bb11f4d743 Merge branch 'WES-75-hangman-integration' into 'development'
Resolve WES-75 "Hangman integration"

See merge request wesign/unity-application!55
2023-03-22 16:46:11 +00:00
Jelle De Geest
f835adaa23 Resolve WES-75 "Hangman integration" 2023-03-22 16:46:07 +00:00
f9298a055a Add code analysis package 2023-03-21 11:57:39 +01:00
Dries Van Schuylenbergh
2e3dd2e26d Fix bug with inconsistent project build settings 2023-03-20 19:22:26 +01:00
Tibe Habils
f2743408a3 Merge branch 'WES-75_Hangman' into 'development'
Introduced Hangman in DEV-compatible branch

See merge request wesign/unity-application!53
2023-03-19 23:02:50 +00:00
Jerome Coudron
e23de9a2d3 Introduced Hangman in DEV-compatible branch 2023-03-19 23:02:50 +00:00
Lukas Van Rossem
b5328d0b9d Merge branch 'WES-81-justsign-new' into 'development'
Decent first prototype

See merge request wesign/unity-application!52
2023-03-19 19:39:32 +00:00
Lukas Van Rossem
44d3398e03 Decent first prototype 2023-03-19 19:39:31 +00:00
Lukas Van Rossem
c9ee031df3 Merge branch 'WES-97-Integrate-SignPredictor-in-SpellingBee' into 'development'
Resolve WES-97 "Integrate signpredictor in spellingbee"

See merge request wesign/unity-application!48
2023-03-19 17:37:52 +00:00
Dries Van Schuylenbergh
3abc24a39c Resolve WES-97 "Integrate signpredictor in spellingbee" 2023-03-19 17:37:50 +00:00
Tibe Habils
f827c29d3a Merge branch 'tests-setup' into 'development'
Tests setup

See merge request wesign/unity-application!45
2023-03-19 16:22:42 +00:00
Tibe Habils
0a4cb9e8c6 Tests setup 2023-03-19 16:22:40 +00:00
Helena Van Breugel
ac000132a9 Merge branch 'WES-80-Data' into 'development'
Resolve WES-80 "Data"

See merge request wesign/unity-application!49
2023-03-18 22:32:38 +00:00
Helena Van Breugel
a19d89db03 Resolve WES-80 "Data" 2023-03-18 22:32:36 +00:00
Jerome Coudron
8ff5c6c4c8 Merge branch 'WES-90-Integrate-SignPredictor-in-courses' into 'development'
Resolve WES-90 "Integrate signpredictor in courses"

See merge request wesign/unity-application!47
2023-03-18 19:53:19 +00:00
Louis Adriaens
746906294b Resolve WES-90 "Integrate signpredictor in courses" 2023-03-18 19:53:17 +00:00
Dries Van Schuylenbergh
1a75791d62 Merge branch 'WES-95-user-progress' into 'development'
Resolve WES-95 "User progress"

See merge request wesign/unity-application!43
2023-03-18 10:25:50 +00:00
Dries Van Schuylenbergh
9dfadece44 Resolve WES-95 "User progress" 2023-03-18 10:25:49 +00:00
Dries Van Schuylenbergh
5e26970bad Merge branch 'WES-113-write-tests' into 'development'
Resolve WES-113 "Write tests"

See merge request wesign/unity-application!44
2023-03-16 12:36:47 +00:00
Louis Adriaens
f6e6afe340 Resolve WES-113 "Write tests" 2023-03-16 12:36:46 +00:00
Dries Van Schuylenbergh
96fb3c89c3 Merge branch 'WES-99-cc-refactor' into 'development'
Resolve WES-99 "Cc refactor"

See merge request wesign/unity-application!42
2023-03-14 10:56:43 +00:00
Dries Van Schuylenbergh
dfc69ddd76 Resolve WES-99 "Cc refactor" 2023-03-14 10:56:42 +00:00
Lukas Van Rossem
59d69f7412 Merge branch 'WES-76-87-themeselection' into 'development'
Resolve WES-76 "87 themeselection"

See merge request wesign/unity-application!41
2023-03-13 21:49:42 +00:00
Lukas Van Rossem
a267301ab6 Resolve WES-76 "87 themeselection" 2023-03-13 21:49:40 +00:00
Dries Van Schuylenbergh
94ec7e0359 Change directories 2023-03-12 23:15:36 +01:00
Dries Van Schuylenbergh
0c795b09bc Merge branch 'development' of gitlab.ilabt.imec.be:wesign/unity-application into development 2023-03-12 23:10:51 +01:00
Dries Van Schuylenbergh
8501edb03a Add builds 2023-03-12 23:10:30 +01:00
Victor Mylle
cef72508cc Update .drone.yml 2023-03-12 22:03:42 +00:00
Victor Mylle
17abca46ea Update .drone.yml 2023-03-12 21:51:51 +00:00
47 changed files with 1089 additions and 1092 deletions

View File

@@ -1,42 +0,0 @@
## Description
_Please provide a brief summary of the changes in this merge request._
_If possible, add a short screengrab or some screenshots of the changes._
## Testing Instructions
_Please provide instructions on how the code reviewers can test your changes:_
1. [Step 1]
2. [Step 2]
3. [Step 3]
4. ...
_Please include any specific information on test data, configurations, or other requirements that are necessary to properly test the changes._
Once you've tested the changes, please confirm that they work as expected and that there are no regressions or unexpected side effects. If any issues are discovered during testing, please include detailed steps to reproduce the issue in the merge request comments. Thank you!
## Related Issues
_Please list any related issues or pull requests that are relevant to this merge request._
_E.g. WES-XXX-..._
## Known bugs or issues
_Please list any known bugs or issues related to the changes in this merge request._
## Checklist
- [ ] I have filled in this template.
- [ ] I have tested my changes thoroughly (both in the editor + **build and run (ctrl+B)**!).
- [ ] I have added appropriate unit tests.
- [ ] I have added appropriate playmode tests.
- [ ] I have updated the user documentation as necessary.
- [ ] Code reviewed by 2 people.
## Additional Notes
_Please add any additional notes or comments that may be helpful for reviewers to understand your changes._
/assign_reviewer @wesign/unityappreviewers

View File

@@ -6,7 +6,7 @@
"UnityEditor.TestRunner",
"AccountsScripts",
"InterfacesScripts",
"ArchitectureScripts"
"SystemArchitecture"
],
"includePlatforms": [
"Editor"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -10,7 +10,7 @@
"Unity.TextMeshPro",
"AccountsScripts",
"SignPredictor",
"ArchitectureScripts"
"SystemArchitecture"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -513,10 +513,10 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 1
m_UiScaleMode: 0
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 1920, y: 1080}
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3

View File

@@ -39,7 +39,6 @@ public class CourseActivityScreen : MonoBehaviour
/// </summary>
public Image courseImage;
/// <summary>
/// Progress bar Display
/// </summary>

View File

@@ -7,8 +7,8 @@
"CommonScripts",
"InterfacesScripts",
"SignPredictor",
"NatML.ML",
"ArchitectureScripts"
"SystemArchitecture",
"NatML.ML"
],
"includePlatforms": [
"Editor"

View File

@@ -4,7 +4,7 @@
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"ArchitectureScripts"
"SystemArchitecture"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -7,8 +7,8 @@
"InterfacesScripts",
"SignPredictor",
"NatML.ML",
"Tween",
"ArchitectureScripts"
"SystemArchitecture",
"Tween"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -1,7 +1,6 @@
using DigitalRuby.Tween;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
@@ -171,26 +170,10 @@ public class CoursesController : AbstractFeedback
void Start()
{
StartCourseController();
signPredictor.SetSignsList(GetSignsList());
signPredictor.SetModel(course.theme.modelIndex);
AddSelfAsListener();
}
/// <summary>
/// Fetches all the strings of the signs of the course
/// </summary>
/// <returns>The signsList that needs to be passed to the signPredictor</returns>
private List<string> GetSignsList()
{
List<string> signsList = new List<string>();
foreach (Learnable learnable in course.theme.learnables)
{
signsList.Add(learnable.name);
}
return signsList;
}
/// <summary>
/// Holds the course-specific logic to start the controller, it is seperated to allow the course to be reset (if that would become needed)
/// </summary>
@@ -427,7 +410,6 @@ public class CoursesController : AbstractFeedback
float accPredictSign = signPredictor.learnableProbabilities[predictedSign];
Learnable predSign = course.theme.learnables.Find(l => l.name.ToUpper().Replace(" ", "-") == predictedSign);
// If there is a feedback-object, we wil change its appearance
if (feedbackText != null && feedbackProgressImage != null)
{
Color col;
@@ -457,7 +439,6 @@ public class CoursesController : AbstractFeedback
feedbackText.color = col;
feedbackProgressImage.color = col;
// Tween the feedback-bar
float oldValue = feedbackProgress.value;
// use an exponential scale
float newValue = Mathf.Exp(4 * (Mathf.Clamp(accCurrentSign / sign.thresholdPercentage, 0.0f, 1.0f) - 1.0f));
@@ -470,7 +451,6 @@ public class CoursesController : AbstractFeedback
});
}
// The internal logic for the courses
if (accPredictSign > sign.thresholdPercentage)
{
// Correct sign

View File

@@ -38,7 +38,7 @@ RenderSettings:
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.37311918, g: 0.3807398, b: 0.35872716, a: 1}
m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
@@ -2241,9 +2241,7 @@ MonoBehaviour:
m_text: 'Speler 1 geeft een woord in aan de hand van vingerspelling.
Het woord
moet tussen 3 en 17 letters zijn.
Je kan letters verwijderen met backspace.'
moet tussen 3 en 17 letters zijn.'
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_sharedMaterial: {fileID: -2577534979213189211, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
@@ -3278,7 +3276,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: AZERTY
m_text:
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_sharedMaterial: {fileID: -2577534979213189211, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
@@ -4542,8 +4540,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 3576f66af2a0eea42ae06ac73d9779e6, type: 3}
m_Name:
m_EditorClassIdentifier:
scoreboardEntriesContainer: {fileID: 2082181368}
scoreboardEntry: {fileID: 9154151134820372555, guid: d4a3a228b08d61847acc6da35b44e52c, type: 3}
endText: {fileID: 1205429732}
lettersRightText: {fileID: 815411823}
lettersWrongText: {fileID: 1606437998}
@@ -4551,7 +4547,19 @@ MonoBehaviour:
accuracyText: {fileID: 1341392955}
wordText: {fileID: 328407984}
scoreText: {fileID: 941310846}
scoreboardEntriesContainer: {fileID: 2082181368}
scoreboardEntry: {fileID: 9154151134820372555, guid: d4a3a228b08d61847acc6da35b44e52c, type: 3}
image: {fileID: 1503788513}
sprites:
- {fileID: 21300000, guid: 46b9ec93c5782294f93be12a7c7bc3f0, type: 3}
- {fileID: 21300000, guid: 0a2c6671fa08a1249ba97ddd3432ac60, type: 3}
- {fileID: 21300000, guid: e5b621bb42e52654d88ad633777004a8, type: 3}
- {fileID: 21300000, guid: 42b25d40421cfce4684bad733747c2e3, type: 3}
- {fileID: 21300000, guid: 76120f6b7a0251949a56f73d49985d3a, type: 3}
- {fileID: 21300000, guid: c9b599bbbe306584a926c415d7e96983, type: 3}
- {fileID: 21300000, guid: 3fa072f7f8a26cf45b84781ce2dfdc51, type: 3}
- {fileID: 21300000, guid: 3f6826496e2a1334ab74ec68262b2c11, type: 3}
- {fileID: 21300000, guid: 98ec939c075818c4e869916da0b33aad, type: 3}
--- !u!1 &987091747
GameObject:
m_ObjectHideFlags: 0
@@ -7667,10 +7675,6 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
signPredictor: {fileID: 1991376311}
minigamelist: {fileID: 11400000, guid: 51453f9b41bc72f468ba3e67ab622f8f, type: 2}
feedbackProgress: {fileID: 211555568}
webcamScreen: {fileID: 1966441454}
gameEndedPanel: {fileID: 974474713}
themeList: {fileID: 11400000, guid: a247e2ce790f0f746a3bc521e6ab7d58, type: 2}
fingerSpelling: {fileID: 11400000, guid: e02921b294fdad940b6e4d57e716d3bf, type: 2}
letterPrefab: {fileID: 4639383499500021565, guid: c3e66e8957864914cb022af914df6a28, type: 3}
@@ -7692,10 +7696,15 @@ MonoBehaviour:
inputPanel: {fileID: 640467091}
scoreDisplay: {fileID: 2026578772}
scoreBonus: {fileID: 1537143412}
gameEndedPanel: {fileID: 974474713}
minigame: {fileID: 11400000, guid: 165c1d9867275924d9720d409e935f95, type: 2}
minigamelist: {fileID: 11400000, guid: 51453f9b41bc72f468ba3e67ab622f8f, type: 2}
gotoGameButton: {fileID: 482979078}
inputTextField: {fileID: 752351940}
feedbackText: {fileID: 887237819}
feedbackProgress: {fileID: 211555568}
feedbackProgressImage: {fileID: 1107240765}
webcamScreen: {fileID: 1966441454}
timerCircle: {fileID: 683997248}
confirmPanel: {fileID: 1002919202}
confirmText: {fileID: 1672600188}

View File

@@ -1,14 +1,15 @@
using DigitalRuby.Tween;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;
public class HangmanController : AbstractMinigameController
public class HangmanController : AbstractFeedback
{
[Header("ConcreteVariables")]
/// <summary>
/// The scriptable with all the themes, will be used to select a random word for hangman.
/// The spellingthemeList will be used for the words.
@@ -16,7 +17,7 @@ public class HangmanController : AbstractMinigameController
public ThemeList themeList;
/// <summary>
/// reference to the fingerspelling-theme to reach the letter-thresholds and to pass to the signPredictor
/// reference to the fingerspelling-theme to reach the letter-thresholds
/// </summary>
public Theme fingerSpelling;
@@ -95,6 +96,11 @@ public class HangmanController : AbstractMinigameController
/// </summary>
public TMP_Text scoreBonus;
///// <summary>
///// This panel holds the panels for input and playing the game, sharing webcam and feedback
///// </summary>
//public GameObject inputGamePanel;
/// <summary>
/// This int shows what mode we are in, used in update: <br></br>
/// 0 : single or multiplayer?<br></br>
@@ -105,6 +111,27 @@ public class HangmanController : AbstractMinigameController
/// </summary>
private int mode;
/// <summary>
/// The game over panel
/// </summary>
public GameObject gameEndedPanel;
/// <summary>
/// Reference to the minigame ScriptableObject
/// </summary>
public Minigame minigame;
/// <summary>
/// We keep the minigamelist as well so that the minigame-index doesn't get reset
/// DO NOT REMOVE
/// </summary>
public MinigameList minigamelist;
/// <summary>
/// Reference to the current user
/// </summary>
private User user;
/// <summary>
/// The button to go into the game
/// </summary>
@@ -125,11 +152,21 @@ public class HangmanController : AbstractMinigameController
/// </summary>
public TMP_Text feedbackText;
/// <summary>
/// Reference to the progress bar
/// </summary>
public Slider feedbackProgress;
/// <summary>
/// Reference to the progress bar image, so we can add fancy colors
/// </summary>
public Image feedbackProgressImage;
/// <summary>
/// reference to the webcam background
/// </summary>
public RawImage webcamScreen;
/// <summary>
/// Previous incorrect sign, so we can keep track whether the user is wrong or the user is still changing signs
/// </summary>
@@ -202,11 +239,48 @@ public class HangmanController : AbstractMinigameController
private int winScore = 25;
/// <summary>
/// Set the AbstractMinigameController variable to inform it of the theme for the signPredictor
/// Start is called before the first frame update
/// </summary>
protected override Theme signPredictorTheme
void Start()
{
get { return fingerSpelling; }
signPredictor.SwapScreen(webcamScreen);
signPredictor.SetModel(ModelIndex.FINGERSPELLING);
AddSelfAsListener();
StartController();
}
/// <summary>
/// Called at the start of the scene AND when the scene is replayed
/// </summary>
public void StartController()
{
// Make sure the mode starts at zero
mode = 0;
// Make sure that only the player-selection panel is the one shown
gamePanel.SetActive(false);
inputPanel.SetActive(false);
playerPanel.SetActive(true);
// Make sure that unneeded panels are inactive
gameEndedPanel.SetActive(false);
confirmPanel.SetActive(false);
// Create entry in current user for keeping track of progress
user = UserList.GetCurrentUser();
var progress = user.GetMinigameProgress(minigame.index);
if (progress == null)
{
progress = new PersistentDataController.SavedMinigameProgress();
progress.minigameIndex = minigame.index;
user.AddMinigameProgress(progress);
}
UserList.Save();
// Guesses needs to be created instantly because it is used in the FeedbackLoop
//guesses = new List<string>();
}
/// <summary>
@@ -215,7 +289,7 @@ public class HangmanController : AbstractMinigameController
public void StartGame()
{
// Change the mode
SwitchMode(2);
mode = 2;
// Activate the right panel
gamePanel.SetActive(true);
@@ -260,7 +334,11 @@ public class HangmanController : AbstractMinigameController
public void GoToInput()
{
// Change the mode
SwitchMode(1);
mode = 1;
// Initialise the word to an empty String
currentWord = "";
inputTextField.text = currentWord.ToUpper();
// Activate the right panel
gamePanel.SetActive(false);
@@ -268,11 +346,7 @@ public class HangmanController : AbstractMinigameController
inputPanel.SetActive(true);
playerPanel.SetActive(false);
// Initialise the word to an empty String
currentWord = "";
PanelMultiplayerInput script = inputPanel.GetComponent<PanelMultiplayerInput>();
script.inputTextField.text = "";
gotoGameButton = script.gotoGameButton;
inputTextField = script.inputTextField;
@@ -306,6 +380,11 @@ public class HangmanController : AbstractMinigameController
/// </summary>
private void PickRandomWord()
{
// vvv DEMO DAY STUFF vvv
currentWord = "DEMO DAG";
return;
// ^^^ DEMO DAY STUFF ^^^
// Get a random index for the themes
// Then get a random index for a word to pull
@@ -396,7 +475,7 @@ public class HangmanController : AbstractMinigameController
{
confirmPanel.SetActive(true);
confirmText.text = $"Letter '{currentSign.ToUpper()}' ?";
SwitchMode(4);
mode = 4;
}
}
break;
@@ -404,7 +483,7 @@ public class HangmanController : AbstractMinigameController
case 2: // Sign your letter
if (!guesses.Contains(currentSign))
{
SwitchMode(3);
mode = 3;
ConfirmAccept();
}
break;
@@ -443,23 +522,24 @@ public class HangmanController : AbstractMinigameController
usedLettersText.text += letter.ToString().ToUpper();
// The current sign was accepted, return to the game
SwitchMode(2);
if (corrects == currentWord.Length)
mode = 2;
if (corrects == currentWord.Replace(" ", "").Length)
{
// Victory, deactivate the model and show the scoreboard
ActivateEnd(true);
ActivateWin();
}
else if (NUMBER_OF_FAILS_BEFORE_GAMEOVER < wrongs)
{
// You lost, deactivate the model and show the scoreboard
ActivateEnd(false);
ActivateGameOver();
}
}
else if (mode == 4)
{
currentWord += letter;
inputTextField.text = currentWord.ToUpper();
SwitchMode(1);
mode = 1;
}
}
@@ -469,23 +549,9 @@ public class HangmanController : AbstractMinigameController
// The current sign was rejected, return to the game-mode
if (mode == 3)
SwitchMode(2);
mode = 2;
else if (mode == 4)
SwitchMode(1);
}
public void SwitchMode(int mode)
{
this.mode = mode;
// In mode 1 and 2, the signPredictor needs to run, otherwise it does not
if (mode == 1 || mode == 2)
{
gameIsActive = true;
}
else
{
gameIsActive = false;
}
mode = 1;
}
/// <summary>
@@ -538,7 +604,7 @@ public class HangmanController : AbstractMinigameController
/// This function returns the score that the user currently has
/// </summary>
/// <returns>The current score of the user</returns>
public override int CalculateScore()
private int CalculateScore()
{
int won = corrects == currentWord.Length ? 1 : 0;
return corrects * correctLetterScore + wrongs * incorrectLetterScore + winScore * won;
@@ -579,19 +645,115 @@ public class HangmanController : AbstractMinigameController
}
/// <summary>
/// The logic to process the signs sent by the signPredictor
/// Update and save the scores
/// </summary>
/// <param name="accuracy">The accuracy of the passed sign</param>
/// <param name="predictedSign">The name of the passed sign</param>
protected override void ProcessMostProbableSign(float accuracy, string predictedSign)
private void SaveScores()
{
// Grab the threshold for the most probable letter
Learnable letter = fingerSpelling.learnables.Find((l) => l.name == predictedSign);
float threshold = letter.thresholdPercentage;
// Calculate new score
int newScore = CalculateScore();
// Save the score as a tuple: < int score, string time ago>
Score score = new Score();
score.scoreValue = newScore;
score.time = DateTime.Now.ToString();
// If there is a feedback-object, we wil change its appearance
if (feedbackText != null && feedbackProgressImage != null)
// Save the new score
var progress = user.GetMinigameProgress(minigame.index);
// Get the current list of scores
List<Score> latestScores = progress.latestScores;
List<Score> highestScores = progress.highestScores;
// Add the new score
latestScores.Add(score);
highestScores.Add(score);
// Sort the scores
highestScores.Sort((a, b) => b.scoreValue.CompareTo(a.scoreValue));
// Only save the top 10 scores, so this list doesn't keep growing endlessly
progress.latestScores = latestScores.Take(10).ToList();
progress.highestScores = highestScores.Take(10).ToList();
UserList.Save();
}
/// <summary>
/// Display win screen
/// </summary>
private void ActivateWin()
{
// Deactivate the model
mode = 0;
// Save the scores and show the scoreboard
SaveScores();
gameEndedPanel.GetComponent<HangmanGameEndedPanel>().GenerateContent(
guessWord: currentWord.ToLower(),
correctLetters: corrects,
incorrectLetters: wrongs,
sprite: hangmanImage.sprite,
result: "GEWONNEN",
score: CalculateScore()
);
gameEndedPanel.SetActive(true);
// @lukas stuff
DeleteWord();
}
/// <summary>
/// Displays the game over panel and score values
/// </summary>
private void ActivateGameOver()
{
// Deactivate the model
mode = 0;
// Save the scores and show the scoreboard
SaveScores();
gameEndedPanel.GetComponent<HangmanGameEndedPanel>().GenerateContent(
guessWord: currentWord.ToLower(),
correctLetters: corrects,
incorrectLetters: wrongs,
sprite: hangmanImage.sprite,
result: "VERLOREN",
score: CalculateScore()
);
gameEndedPanel.SetActive(true);
DeleteWord();
}
/// <summary>
/// The updateFunction that is called when new probabilities become available
/// </summary>
/// <returns></returns>
protected override IEnumerator UpdateFeedback()
{
// Get the sign with the highest prediction
if ((mode == 1 || mode == 2) &&
signPredictor != null &&
signPredictor.learnableProbabilities != null)
{
KeyValuePair<string, float> highestPrediction = signPredictor.learnableProbabilities.Aggregate((x, y) => x.Value > y.Value ? x : y);
float accuracy = highestPrediction.Value;
string predictedSign = highestPrediction.Key;
// vvv TEMPORARY STUFF vvv
if (predictedSign == "J" && accuracy <= 0.965f)
{
highestPrediction = signPredictor.learnableProbabilities.Aggregate((x, y) => x.Value > y.Value && x.Key != "J" ? x : y);
}
accuracy = highestPrediction.Value;
predictedSign = highestPrediction.Key;
// ^^^ TEMPORARY STUFF ^^^
// Grab the threshold for the most probable letter
Learnable letter = fingerSpelling.learnables.Find((l) => l.name == predictedSign);
float threshold = letter.thresholdPercentage;
float oldValue = feedbackProgress.value;
// use an exponential scale
float newValue = Mathf.Exp(4 * (Mathf.Clamp(accuracy / threshold, 0.0f, 1.0f) - 1.0f));
@@ -624,33 +786,37 @@ public class HangmanController : AbstractMinigameController
feedbackText.color = red;
feedbackProgressImage.color = red;
}
}
// The logic for the internal workings of the game
if (accuracy > threshold)
{
// A different sign was predicted compared to the last call of this function
if (previousSign != predictedSign)
if (accuracy > threshold)
{
// Reset the timer
previousSign = predictedSign;
currentTime = 0;
// If you are entering a word the timer needs to work
// If you are playing the game and haven't guessed the letter yet, then the timer needs to work
if ((mode == 1) ||
(mode == 2 && !guesses.Contains(previousSign.ToUpper())))
if (previousSign != predictedSign)
{
runTime = true;
// Reset the timer
previousSign = predictedSign;
currentTime = 0;
if ((mode == 1) ||
(mode == 2 && !guesses.Contains(previousSign.ToUpper())))
{
runTime = true;
}
timerCircle.fillAmount = currentTime;
}
timerCircle.fillAmount = currentTime;
else if (currentTime == maxTime)
{
// Set the predictedSign as your guess and update the Hangman
currentSign = predictedSign;
UpdateSign();
// reset the timer and look for a new prediction
previousSign = null;
currentTime = 0;
runTime = false;
timerCircle.fillAmount = currentTime;
}
}
// The same sign was predicted as last time and said sign has been held for a sufficiently long time
else if (currentTime == maxTime)
else
{
// Set the predictedSign as your guess and update the Hangman
currentSign = predictedSign;
UpdateSign();
// reset the timer and look for a new prediction
// The sign was dropped, reset the timer
previousSign = null;
currentTime = 0;
runTime = false;
@@ -658,69 +824,11 @@ public class HangmanController : AbstractMinigameController
}
}
else
else if (feedbackProgress != null)
{
// The sign was dropped, reset the timer
previousSign = null;
currentTime = 0;
runTime = false;
timerCircle.fillAmount = currentTime;
feedbackProgress.value = 0.0f;
}
}
/// <summary>
/// The logic to set the scoreboard of hangman
/// </summary>
/// <param name="victory">SHows whether or not the player won</param>
protected override void SetScoreBoard(bool victory)
{
string resultTxt;
if (victory)
{
resultTxt = "GEWONNEN";
}
else
{
resultTxt = "VERLOREN";
}
gameEndedPanel.GetComponent<HangmanGameEndedPanel>().GenerateContent(
guessWord: currentWord.ToLower(),
correctLetters: corrects,
incorrectLetters: wrongs,
sprite: hangmanImage.sprite,
result: resultTxt,
score: CalculateScore()
);
}
/// <summary>
/// The hangman-specific logic that needs to be called at the start of the game
/// </summary>
protected override void StartGameLogic()
{
// Make sure the mode starts at zero
SwitchMode(0);
// Make sure that only the player-selection panel is the one shown
gamePanel.SetActive(false);
inputPanel.SetActive(false);
playerPanel.SetActive(true);
// Make sure that unneeded panels are inactive
gameEndedPanel.SetActive(false);
confirmPanel.SetActive(false);
}
/// <summary>
/// The Hangman-specific logic that needs to be called at the end of a game
/// </summary>
/// <param name="victory"></param>
protected override void EndGameLogic(bool victory)
{
// Deactivate the model
SwitchMode(0);
DeleteWord();
yield return null;
}
}

View File

@@ -7,16 +7,8 @@ using UnityEngine.UI;
/// <summary>
/// The hangman-variant of the ScoreBoard
/// </summary>
public class HangmanGameEndedPanel : AbstractGameEndedPanel
public class HangmanGameEndedPanel : MonoBehaviour
{
/// <summary>
/// Tell the scoreboard that the scoreboard is for HangMan
/// </summary>
protected override MinigameIndex minigameIndex
{
get { return MinigameIndex.HANGMAN; }
}
/// <summary>
/// "VERLOREN" or "GEWONNEN"
/// </summary>
@@ -48,11 +40,28 @@ public class HangmanGameEndedPanel : AbstractGameEndedPanel
/// </summary>
public TMP_Text scoreText;
/// <summary>
/// Reference to the scoreboard entries container
/// </summary>
public Transform scoreboardEntriesContainer;
/// <summary>
/// The GameObjects representing the letters
/// </summary>
private List<GameObject> scoreboardEntries = new List<GameObject>();
/// <summary>
/// Reference to the ScoreboardEntry prefab
/// </summary>
public GameObject scoreboardEntry;
/// <summary>
/// Reference to the end result image
/// </summary>
public Image image;
public List<Sprite> sprites;
/// <summary>
/// Generate the content of the GameEnded panel
/// </summary>
@@ -90,4 +99,105 @@ public class HangmanGameEndedPanel : AbstractGameEndedPanel
scoreText.text = $"Score: {score}";
SetScoreBoard();
}
/// <summary>
/// Sets the scoreboard
/// </summary>
private void SetScoreBoard()
{
// Clean the previous scoreboard entries
for (int i = 0; i < scoreboardEntries.Count; i++)
{
Destroy(scoreboardEntries[i]);
}
scoreboardEntries.Clear();
// Instantiate new entries
// Get all scores from all users
List<Tuple<string, Score>> allScores = new List<Tuple<string, Score>>();
foreach (User user in UserList.GetUsers())
{
// Get user's progress for this minigame
var progress = user.GetMinigameProgress(MinigameIndex.HANGMAN);
if (progress != null)
{
// Add scores to dictionary
List<Score> scores = progress.highestScores;
foreach (Score score in scores)
{
allScores.Add(new Tuple<string, Score>(user.GetUsername(), score));
}
}
}
// Sort allScores based on Score.scoreValue
allScores.Sort((a, b) => b.Item2.scoreValue.CompareTo(a.Item2.scoreValue));
// Instantiate scoreboard entries
int rank = 1;
foreach (Tuple<string, Score> tup in allScores.Take(10))
{
string username = tup.Item1;
Score score = tup.Item2;
GameObject entry = Instantiate(scoreboardEntry, scoreboardEntriesContainer);
scoreboardEntries.Add(entry);
// Set the player icon
entry.transform.Find("Image").GetComponent<Image>().sprite = UserList.GetUserByUsername(username).GetAvatar();
// Set the player name
entry.transform.Find("PlayerName").GetComponent<TMP_Text>().text = username;
// Set the score
entry.transform.Find("Score").GetComponent<TMP_Text>().text = score.scoreValue.ToString();
// Set the rank
entry.transform.Find("Rank").GetComponent<TMP_Text>().text = rank.ToString();
// Set the ago
// Convert the score.time to Datetime
DateTime time = DateTime.Parse(score.time);
DateTime currentTime = DateTime.Now;
TimeSpan diff = currentTime.Subtract(time);
string formatted;
if (diff.Days > 0)
{
formatted = $"{diff.Days}d ";
}
else if (diff.Hours > 0)
{
formatted = $"{diff.Hours}h ";
}
else if (diff.Minutes > 0)
{
formatted = $"{diff.Minutes}m ";
}
else
{
formatted = "now";
}
entry.transform.Find("Ago").GetComponent<TMP_Text>().text = formatted;
// Alternating colors looks nice
if (rank % 2 == 0)
{
Image image = entry.transform.GetComponent<Image>();
image.color = new Color(image.color.r, image.color.g, image.color.b, 0f);
}
// Make new score stand out
if (diff.TotalSeconds < 1)
{
Image image = entry.transform.GetComponent<Image>();
image.color = new Color(0, 229, 255, 233);
}
rank++;
}
}
}

View File

@@ -5,10 +5,9 @@
"GUID:e83ddf9a537a96b4a804a16bb7872ec1",
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:1631ed2680c61245b8211d943c1639a8",
"GUID:d0b6b39a21908f94fbbd9f2c196a9725",
"GUID:58e104b97fb3752438ada2902a36dcbf",
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25",
"GUID:403dd94a93598934eb522dc36df43d7b",
"GUID:d0b6b39a21908f94fbbd9f2c196a9725"
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -1557,19 +1557,20 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
signPredictor: {fileID: 1150323775}
minigamelist: {fileID: 11400000, guid: 51453f9b41bc72f468ba3e67ab622f8f, type: 2}
feedbackProgress: {fileID: 1753834854}
webcamScreen: {fileID: 496523654}
gameEndedPanel: {fileID: 2498222378566216023}
answerField: {fileID: 0}
timingFeedback: {fileID: 128049076}
scoreDisplay: {fileID: 1627575593}
minigame: {fileID: 11400000, guid: e726e0b93ea88465db7ee27605deb83f, type: 2}
minigamelist: {fileID: 11400000, guid: 51453f9b41bc72f468ba3e67ab622f8f, type: 2}
songList: {fileID: 11400000, guid: 4f0ce70309bb901feb28199a82a7d195, type: 2}
hitZonePerfect: {fileID: 2012531008}
hitZoneGood: {fileID: 369868393}
hitZoneMeh: {fileID: 141066497}
webcamScreen: {fileID: 496523654}
symbolPrefab: {fileID: 4639383499500021565, guid: f3117b0203a1342a48a95904347b03c8, type: 3}
symbolContainer: {fileID: 2093721209}
scoreboardEntriesContainer: {fileID: 2498222377153197597}
scoreboardEntry: {fileID: 9154151134820372555, guid: d4a3a228b08d61847acc6da35b44e52c, type: 3}
lpmText: {fileID: 2498222377094258231}
perfectSignsText: {fileID: 2498222378642934129}
goodSignsText: {fileID: 524776817}
@@ -1577,7 +1578,9 @@ MonoBehaviour:
terribleSignsText: {fileID: 1343527143}
notFoundSignsText: {fileID: 850357042}
scoreText: {fileID: 2498222378754007924}
gameEndedPanel: {fileID: 2498222378566216023}
feedbackText: {fileID: 1753834853}
feedbackProgressBar: {fileID: 1753834854}
feedbackProgressImage: {fileID: 1753834852}
perfectSprite: {fileID: 21300000, guid: 43b0de2d8fcec1540bb9989e45db4581, type: 3}
goodSprite: {fileID: 21300000, guid: 168acd43cf46d1c419991a2620485bf6, type: 3}
@@ -1854,7 +1857,7 @@ MonoBehaviour:
modelInfoFile: {fileID: 4900000, guid: fb8b51022bdcd654a9f29c054832a1b5, type: 3}
modelInfoFileEmbedding: {fileID: 4900000, guid: 4e303164823194bc4be87f4c9550cfd0, type: 3}
configAsset: {fileID: 4900000, guid: 6288c43cdca97374782dac1ea87aa029, type: 3}
screen: {fileID: 496523654}
screen: {fileID: 0}
--- !u!4 &1150323776
Transform:
m_ObjectHideFlags: 0
@@ -3521,7 +3524,7 @@ GameObject:
- component: {fileID: 2498222377094258230}
- component: {fileID: 2498222377094258231}
m_Layer: 5
m_Name: GPM
m_Name: LPM
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@@ -3575,7 +3578,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: XXX GPM
m_text: XXX LPM
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_sharedMaterial: {fileID: -2577534979213189211, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
@@ -4697,6 +4700,29 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2498222378566216023}
m_CullTransparentMesh: 1
--- !u!114 &2498222378566215979
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2498222378566216023}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5aa929dce1f59b340b4a0cca1bb68edc, type: 3}
m_Name:
m_EditorClassIdentifier:
endText: {fileID: 2498222377166680336}
lpmText: {fileID: 2498222377094258231}
lettersRightText: {fileID: 0}
lettersWrongText: {fileID: 0}
lettersTotalText: {fileID: 0}
accuracyText: {fileID: 2498222378642934129}
wordsText: {fileID: 0}
timeText: {fileID: 0}
scoreText: {fileID: 2498222378754007924}
scoreboardEntriesContainer: {fileID: 2498222377153197597}
scoreboardEntry: {fileID: 9154151134820372555, guid: d4a3a228b08d61847acc6da35b44e52c, type: 3}
--- !u!224 &2498222378566216020
RectTransform:
m_ObjectHideFlags: 0
@@ -4762,7 +4788,7 @@ GameObject:
- component: {fileID: 2498222378566216020}
- component: {fileID: 2498222378566215978}
- component: {fileID: 2498222378566216021}
- component: {fileID: 2498222378566216024}
- component: {fileID: 2498222378566215979}
m_Layer: 5
m_Name: GameEnded Panel
m_TagString: Untagged
@@ -4770,27 +4796,6 @@ GameObject:
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!114 &2498222378566216024
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2498222378566216023}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9a86c239be1aa1543ba8a4ace5f658b1, type: 3}
m_Name:
m_EditorClassIdentifier:
scoreboardEntriesContainer: {fileID: 2498222377153197597}
scoreboardEntry: {fileID: 9154151134820372555, guid: d4a3a228b08d61847acc6da35b44e52c, type: 3}
perfectSignsText: {fileID: 2498222378642934129}
goodSignsText: {fileID: 524776817}
mehSignsText: {fileID: 6218584}
terribleSignsText: {fileID: 1343527143}
notFoundSignsText: {fileID: 850357042}
gpmText: {fileID: 2498222377094258231}
scoreText: {fileID: 2498222378754007924}
--- !u!1 &2498222378611643141
GameObject:
m_ObjectHideFlags: 0

View File

@@ -1,6 +1,8 @@
using DigitalRuby.Tween;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -9,7 +11,7 @@ using Random = UnityEngine.Random;
/// <summary>
/// Contains all game logic for the JustSign game
/// </summary>
public class JustSignController : AbstractMinigameController
public class JustSignController : AbstractFeedback
{
/// <summary>
/// All of the words that can be used in this session
@@ -31,6 +33,17 @@ public class JustSignController : AbstractMinigameController
/// </summary>
public TMP_Text scoreDisplay;
/// <summary>
/// Reference to the minigame ScriptableObject
/// </summary>
public Minigame minigame;
/// <summary>
/// We keep the minigamelist as well so that the minigame-index doesn't get reset
/// DO NOT REMOVE
/// </summary>
public MinigameList minigamelist;
/// </summary>
/// Reference to the list of available songs
/// </summary>
@@ -56,6 +69,11 @@ public class JustSignController : AbstractMinigameController
/// </summary>
public RectTransform hitZoneMeh;
/// <summary>
/// Reference to the webcam
/// </summary>
public RawImage webcamScreen;
/// <summary>
/// Score obtained when getting a perfect hit
/// </summary>
@@ -106,6 +124,11 @@ public class JustSignController : AbstractMinigameController
/// </summary>
private List<GameObject> activeSymbols = new List<GameObject>();
/// <summary>
/// Have the symbols started spawning or not
/// </summary>
private bool gameIsActive = false;
/// <summary>
/// Controls movement speed of symbols (higher -> faster)
/// </summary>
@@ -161,6 +184,26 @@ public class JustSignController : AbstractMinigameController
/// </summary>
private int incorrectSigns;
/// <summary>
/// Reference to the scoreboard entries container
/// </summary>
public Transform scoreboardEntriesContainer;
/// <summary>
/// The GameObjects representing the letters
/// </summary>
private List<GameObject> scoreboardEntries = new List<GameObject>();
/// <summary>
/// Reference to the ScoreboardEntry prefab
/// </summary>
public GameObject scoreboardEntry;
/// <summary>
/// Reference to the current user
/// </summary>
private User user;
/// <summary>
/// LPM
/// </summary>
@@ -196,11 +239,21 @@ public class JustSignController : AbstractMinigameController
/// </summary>
public TMP_Text scoreText;
/// <summary>
/// Reference to the gameEnded panel, so we can update its display
/// </summary>
public GameObject gameEndedPanel;
/// <summary>
/// Reference to the feedback field
/// </summary>
public TMP_Text feedbackText;
/// <summary>
/// Reference to the progress bar
/// </summary>
public Slider feedbackProgressBar;
/// <summary>
/// Reference to the progress bar image, so we can add fancy colors
/// </summary>
@@ -246,9 +299,53 @@ public class JustSignController : AbstractMinigameController
/// </summary>
public GameObject userFeedback;
protected override Theme signPredictorTheme
/// <summary>
/// Start is called before the first frame update
/// </summary>
public void Start()
{
get { return currentTheme; }
currentTheme = minigame.themeList.themes[minigame.themeList.currentThemeIndex];
signPredictor.SetModel(currentTheme.modelIndex);
signPredictor.SwapScreen(webcamScreen);
AddSelfAsListener();
StartController();
}
/// <summary>
/// Holds the game-specific logic to start the controller
/// </summary>
public void StartController()
{
userFeedback.SetActive(currentTheme.modelIndex != ModelIndex.NONE);
previewMessage.SetActive(currentTheme.modelIndex == ModelIndex.NONE);
perfectSigns = 0;
goodSigns = 0;
mehSigns = 0;
terribleSigns = 0;
incorrectSigns = 0;
timingFeedback.text = "";
imageFeedback.sprite = minigame.thumbnail;
gameEndedPanel.SetActive(false);
// Create entry in current user for keeping track of progress
user = UserList.GetCurrentUser();
var progress = user.GetMinigameProgress(minigame.index);
if (progress == null)
{
progress = new PersistentDataController.SavedMinigameProgress();
progress.minigameIndex = minigame.index;
user.AddMinigameProgress(progress);
}
UserList.Save();
scoreDisplay.text = $"Score: {CalculateScore()}";
words.AddRange(currentTheme.learnables);
currentSong = songList.songs[songList.currentSongIndex];
AudioSource.PlayClipAtPoint(currentSong.song, Vector3.zero, 1.0f);
beginTime = Time.time;
lastSymbolTime = beginTime + currentSong.duration - 1920.0f / moveSpeed;
StartCoroutine(WaitThenStart(currentSong.firstSymbolTime));
}
/// <summary>
@@ -291,8 +388,7 @@ public class JustSignController : AbstractMinigameController
// Check if the song has ended and activate scorescreen if it has
if (currentTime - beginTime > currentSong.duration)
{
// The boolean that is passed is irrelevant for this game
ActivateEnd(true);
ActivateEnd();
}
// Move all active symbols to the right
@@ -310,11 +406,28 @@ public class JustSignController : AbstractMinigameController
/// Calculate the score
/// </summary>
/// <returns>The calculated score</returns>
public override int CalculateScore()
public int CalculateScore()
{
return goodSigns * goodScore + perfectSigns * perfectScore + mehScore * mehSigns + terribleScore * terribleSigns + incorrectSigns * offscreenScore;
}
/// <summary>
/// Display Scoreboard + Metrics
/// </summary>
public void ActivateEnd()
{
gameIsActive = false;
while (activeSymbols.Count > 0)
{
DestroySymbolAt(0);
}
// TODO: Scoreboard
SaveScores();
SetScoreMetrics();
SetScoreBoard();
gameEndedPanel.SetActive(true);
}
/// <summary>
/// Destroy the symbol at the given index
/// </summary>
@@ -361,160 +474,282 @@ public class JustSignController : AbstractMinigameController
}
/// <summary>
/// The logic to process the signs sent by the signPredictor
/// Update and save the scores
/// </summary>
/// <param name="accuracy">The accuracy of the passed sign</param>
/// <param name="predictedSign">The name of the passed sign</param>
protected override void ProcessMostProbableSign(float accuracy, string predictedSign)
public void SaveScores()
{
Learnable predSign = currentTheme.learnables.Find(l => l.name.ToUpper() == predictedSign);
// Calculate new score
int newScore = CalculateScore();
// If there is a feedback-object, we wil change its appearance
if (feedbackText != null && feedbackProgressImage != null)
// Save the score as a tuple: < int score, string time ago>
Score score = new Score();
score.scoreValue = newScore;
score.time = DateTime.Now.ToString();
// Save the new score
var progress = user.GetMinigameProgress(minigame.index);
// Get the current list of scores
List<Score> latestScores = progress.latestScores;
List<Score> highestScores = progress.highestScores;
// Add the new score
latestScores.Add(score);
highestScores.Add(score);
// Sort the scores
highestScores.Sort((a, b) => b.scoreValue.CompareTo(a.scoreValue));
// Only save the top 10 scores, so this list doesn't keep growing endlessly
progress.latestScores = latestScores.Take(10).ToList();
progress.highestScores = highestScores.Take(10).ToList();
PersistentDataController.GetInstance().Save();
}
/// <summary>
/// Set score metrics
/// </summary>
private void SetScoreMetrics()
{
// In de zone
perfectSignsText.text = perfectSigns.ToString();
// Aanvaardbaar
goodSignsText.text = goodSigns.ToString();
// Nipt
mehSignsText.text = mehSigns.ToString();
// Slechte timing
terribleSignsText.text = terribleSigns.ToString();
// Niet Geraden
notFoundSignsText.text = incorrectSigns.ToString();
// LPM
int duration = songList.songs[songList.currentSongIndex].duration;
int correctSigns = goodSigns + perfectSigns + mehSigns + terribleSigns;
lpmText.text = (60f * correctSigns / duration).ToString("#") + " GPM";
// Score
scoreText.text = $"Score: {CalculateScore()}";
}
/// <summary>
/// Sets the scoreboard
/// </summary>
private void SetScoreBoard()
{
// Clean the previous scoreboard entries
for (int i = 0; i < scoreboardEntries.Count; i++)
{
Color col;
if (accuracy > predSign.thresholdPercentage)
Destroy(scoreboardEntries[i]);
}
scoreboardEntries.Clear();
// Instantiate new entries
// Get all scores from all users
List<Tuple<string, Score>> allScores = new List<Tuple<string, Score>>();
foreach (User user in UserList.GetUsers())
{
// Get user's progress for this minigame
var progress = user.GetMinigameProgress(minigame.index);
if (progress != null)
{
feedbackText.text = $"Herkent '{predictedSign}'";
col = new Color(0x8b / 255.0f, 0xd4 / 255.0f, 0x5e / 255.0f);
// Add scores to dictionary
List<Score> scores = progress.highestScores;
foreach (Score score in scores)
{
allScores.Add(new Tuple<string, Score>(user.GetUsername(), score));
}
}
else if (accuracy > 0.9 * predSign.thresholdPercentage)
}
// Sort allScores based on Score.scoreValue
allScores.Sort((a, b) => b.Item2.scoreValue.CompareTo(a.Item2.scoreValue));
// Instantiate scoreboard entries
int rank = 1;
foreach (Tuple<string, Score> tup in allScores.Take(10))
{
string username = tup.Item1;
Score score = tup.Item2;
GameObject entry = Instantiate(scoreboardEntry, scoreboardEntriesContainer);
scoreboardEntries.Add(entry);
// Set the player icon
entry.transform.Find("Image").GetComponent<Image>().sprite = UserList.GetUserByUsername(username).GetAvatar();
// Set the player name
entry.transform.Find("PlayerName").GetComponent<TMP_Text>().text = username;
// Set the score
entry.transform.Find("Score").GetComponent<TMP_Text>().text = score.scoreValue.ToString();
// Set the rank
entry.transform.Find("Rank").GetComponent<TMP_Text>().text = rank.ToString();
// Set the ago
// Convert the score.time to Datetime
DateTime time = DateTime.Parse(score.time);
DateTime currentTime = DateTime.Now;
TimeSpan diff = currentTime.Subtract(time);
string formatted;
if (diff.Days > 0)
{
feedbackText.text = $"Lijkt op '{predictedSign}'";
col = new Color(0xf2 / 255.0f, 0x7f / 255.0f, 0x0c / 255.0f);
formatted = $"{diff.Days}d ";
}
else if (diff.Hours > 0)
{
formatted = $"{diff.Hours}h ";
}
else if (diff.Minutes > 0)
{
formatted = $"{diff.Minutes}m ";
}
else
{
feedbackText.text = "Detecteren...";
col = new Color(0xf5 / 255.0f, 0x49 / 255.0f, 0x3d / 255.0f);
formatted = "now";
}
feedbackText.color = col;
feedbackProgressImage.color = col;
entry.transform.Find("Ago").GetComponent<TMP_Text>().text = formatted;
float oldValue = feedbackProgress.value;
// use an exponential scale
float newValue = Mathf.Exp(4 * (Mathf.Clamp(accuracy / predSign.thresholdPercentage, 0.0f, 1.0f) - 1.0f));
feedbackProgress.gameObject.Tween("FeedbackUpdate", oldValue, newValue, 0.2f, TweenScaleFunctions.CubicEaseInOut, (t) =>
// Alternating colors looks nice
if (rank % 2 == 0)
{
if (feedbackProgress != null)
{
feedbackProgress.value = t.CurrentValue;
}
});
Image image = entry.transform.GetComponent<Image>();
image.color = new Color(image.color.r, image.color.g, image.color.b, 0f);
}
// Make new score stand out
if (diff.TotalSeconds < 1)
{
Image image = entry.transform.GetComponent<Image>();
image.color = new Color(0, 229, 255, 233);
}
rank++;
}
}
// The logic for the internal workings of the game
if (accuracy > predSign.thresholdPercentage)
/// <summary>
/// The updateFunction that is called when new probabilities become available
/// </summary>
/// <returns></returns>
protected override IEnumerator UpdateFeedback()
{
// Get the predicted sign
if (signPredictor != null && signPredictor.learnableProbabilities != null && gameIsActive)
{
int matchedSymbolIndex = activeWords.IndexOf(predictedSign.ToUpper());
// Get highest predicted sign
string predictedSign = signPredictor.learnableProbabilities.Aggregate((a, b) => a.Value > b.Value ? a : b).Key;
float accuracy = signPredictor.learnableProbabilities[predictedSign];
// Destroy the oldest symbol if the current input matches it
if (0 <= matchedSymbolIndex)
// vvv TEMPORARY STUFF vvv
if (predictedSign == "J" && accuracy <= 0.97f)
{
float x = activeSymbols[matchedSymbolIndex].transform.localPosition.x;
predictedSign = signPredictor.learnableProbabilities.Aggregate((x, y) => x.Value > y.Value && x.Key != "J" ? x : y).Key;
}
accuracy = signPredictor.learnableProbabilities[predictedSign];
// ^^^ TEMPORARY STUFF ^^^
// parameters to define the Perfect hit zone
float perfectRange = hitZonePerfect.sizeDelta.x;
float perfectCenter = hitZonePerfect.localPosition.x;
// parameters to define the Good hit zone
float goodRange = hitZoneGood.sizeDelta.x;
float goodCenter = hitZoneGood.localPosition.x;
// parameters to define the Meh hit zone
float mehRange = hitZoneMeh.sizeDelta.x;
float mehCenter = hitZoneMeh.localPosition.x;
Learnable predSign = currentTheme.learnables.Find(l => l.name.ToUpper().Replace(" ", "-") == predictedSign);
if (perfectCenter - perfectRange / 2 <= x && x <= perfectCenter + perfectRange / 2)
if (feedbackText != null && feedbackProgressImage != null)
{
Color col;
if (accuracy > predSign.thresholdPercentage)
{
timingFeedback.text = $"Perfect! \n +{perfectScore}";
imageFeedback.sprite = perfectSprite;
perfectSigns++;
timingFeedback.color = new Color(0x8b / 255.0f, 0xd4 / 255.0f, 0x5e / 255.0f);
feedbackText.text = $"Herkent '{predictedSign}'";
col = new Color(0x8b / 255.0f, 0xd4 / 255.0f, 0x5e / 255.0f);
}
else if (goodCenter - goodRange / 2 <= x && x <= goodCenter + goodRange / 2)
else if (accuracy > 0.9 * predSign.thresholdPercentage)
{
timingFeedback.text = $"Goed \n +{goodScore}";
imageFeedback.sprite = goodSprite;
goodSigns++;
timingFeedback.color = new Color(0xf7 / 255.0f, 0xad / 255.0f, 0x19 / 255.0f);
}
else if (mehCenter - mehRange / 2 <= x && x <= mehCenter + mehRange / 2)
{
timingFeedback.text = $"Bijna... \n +{mehScore}";
imageFeedback.sprite = mehSprite;
mehSigns++;
timingFeedback.color = new Color(0xf2 / 255.0f, 0x7f / 255.0f, 0x0c / 255.0f);
feedbackText.text = $"Lijkt op '{predictedSign}'";
col = new Color(0xf2 / 255.0f, 0x7f / 255.0f, 0x0c / 255.0f);
}
else
{
timingFeedback.text = $"Te vroeg! \n {terribleScore}";
imageFeedback.sprite = terribleSprite;
terribleSigns++;
timingFeedback.color = new Color(0xf5 / 255.0f, 0x49 / 255.0f, 0x3d / 255.0f);
feedbackText.text = "Detecteren...";
col = new Color(0xf5 / 255.0f, 0x49 / 255.0f, 0x3d / 255.0f);
}
DestroySymbolAt(matchedSymbolIndex);
feedbackText.color = col;
feedbackProgressImage.color = col;
float oldValue = feedbackProgressBar.value;
// use an exponential scale
float newValue = Mathf.Exp(4 * (Mathf.Clamp(accuracy / predSign.thresholdPercentage, 0.0f, 1.0f) - 1.0f));
feedbackProgressBar.gameObject.Tween("FeedbackUpdate", oldValue, newValue, 0.2f, TweenScaleFunctions.CubicEaseInOut, (t) =>
{
if (feedbackProgressBar != null)
{
feedbackProgressBar.value = t.CurrentValue;
}
});
}
if (accuracy > predSign.thresholdPercentage)
{
int matchedSymbolIndex = activeWords.IndexOf(predictedSign.ToUpper());
// Destroy the oldest symbol if the current input matches it
if (0 <= matchedSymbolIndex)
{
float x = activeSymbols[matchedSymbolIndex].transform.localPosition.x;
// parameters to define the Perfect hit zone
float perfectRange = hitZonePerfect.sizeDelta.x;
float perfectCenter = hitZonePerfect.localPosition.x;
// parameters to define the Good hit zone
float goodRange = hitZoneGood.sizeDelta.x;
float goodCenter = hitZoneGood.localPosition.x;
// parameters to define the Meh hit zone
float mehRange = hitZoneMeh.sizeDelta.x;
float mehCenter = hitZoneMeh.localPosition.x;
if (perfectCenter - perfectRange / 2 <= x && x <= perfectCenter + perfectRange / 2)
{
timingFeedback.text = $"Perfect! \n +{perfectScore}";
imageFeedback.sprite = perfectSprite;
perfectSigns++;
timingFeedback.color = new Color(0x8b / 255.0f, 0xd4 / 255.0f, 0x5e / 255.0f);
}
else if (goodCenter - goodRange / 2 <= x && x <= goodCenter + goodRange / 2)
{
timingFeedback.text = $"Goed \n +{goodScore}";
imageFeedback.sprite = goodSprite;
goodSigns++;
timingFeedback.color = new Color(0xf7 / 255.0f, 0xad / 255.0f, 0x19 / 255.0f);
}
else if (mehCenter - mehRange / 2 <= x && x <= mehCenter + mehRange / 2)
{
timingFeedback.text = $"Bijna... \n +{mehScore}";
imageFeedback.sprite = mehSprite;
mehSigns++;
timingFeedback.color = new Color(0xf2 / 255.0f, 0x7f / 255.0f, 0x0c / 255.0f);
}
else
{
timingFeedback.text = $"Te vroeg! \n {terribleScore}";
imageFeedback.sprite = terribleSprite;
terribleSigns++;
timingFeedback.color = new Color(0xf5 / 255.0f, 0x49 / 255.0f, 0x3d / 255.0f);
}
DestroySymbolAt(matchedSymbolIndex);
}
}
}
}
/// <summary>
/// The logic to set the scoreboard of justsign
/// </summary>
/// <param name="victory">Shows whether or not the player won, is not relevant for JustSIgn</param>
protected override void SetScoreBoard(bool victory)
{
gameEndedPanel.GetComponent<JustSignGameEndedPanel>().GenerateContent(
perfectSigns: perfectSigns,
goodSigns: goodSigns,
mehSigns: mehSigns,
terribleSigns: terribleSigns,
incorrectSigns: incorrectSigns,
duration: currentSong.duration,
score: CalculateScore()
);
}
/// <summary>
/// The justsign-specific logic that needs to be called at the start of the game
/// </summary>
protected override void StartGameLogic()
{
// Set the current theme so that it can be passed along
currentTheme = minigame.themeList.themes[minigame.themeList.currentThemeIndex];
userFeedback.SetActive(currentTheme.modelIndex != ModelIndex.NONE);
previewMessage.SetActive(currentTheme.modelIndex == ModelIndex.NONE);
perfectSigns = 0;
goodSigns = 0;
mehSigns = 0;
terribleSigns = 0;
incorrectSigns = 0;
timingFeedback.text = "";
imageFeedback.sprite = minigame.thumbnail;
gameEndedPanel.SetActive(false);
scoreDisplay.text = $"Score: {CalculateScore()}";
words.AddRange(currentTheme.learnables);
currentSong = songList.songs[songList.currentSongIndex];
AudioSource.PlayClipAtPoint(currentSong.song, Vector3.zero, 1.0f);
beginTime = Time.time;
lastSymbolTime = beginTime + currentSong.duration - 1920.0f / moveSpeed;
StartCoroutine(WaitThenStart(currentSong.firstSymbolTime));
}
/// <summary>
/// The justsign-specific logic that needs to be called at the end of a game
/// </summary>
/// <param name="victory"></param>
protected override void EndGameLogic(bool victory)
{
gameIsActive = false;
while (activeSymbols.Count > 0)
else if (feedbackProgressBar != null)
{
DestroySymbolAt(0);
feedbackProgressBar.value = 0.0f;
}
yield return null;
}
}

View File

@@ -1,90 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// The JustSign-variant of the ScoreBoard
/// </summary>
public class JustSignGameEndedPanel : AbstractGameEndedPanel
{
/// <summary>
/// Tell the scoreboard that the scoreboard is for JustSign
/// </summary>
protected override MinigameIndex minigameIndex
{
get { return MinigameIndex.JUST_SIGN; }
}
/// <summary>
/// The field that will display the amount of perfect signs
/// </summary>
public TMP_Text perfectSignsText;
/// <summary>
/// The field that will display the amount of good signs
/// </summary>
public TMP_Text goodSignsText;
/// <summary>
/// The field that will display the amount of meh signs
/// </summary>
public TMP_Text mehSignsText;
/// <summary>
/// The field that will display the amount of terrible signs
/// </summary>
public TMP_Text terribleSignsText;
/// <summary>
/// The field that will display the amount of not found signs
/// </summary>
public TMP_Text notFoundSignsText;
/// <summary>
/// The field that will display the signs per minute
/// </summary>
public TMP_Text gpmText;
/// <summary>
/// Score
/// </summary>
public TMP_Text scoreText;
/// <summary>
/// Generate the content of the gameEnded panel
/// </summary>
/// <param name="perfectSigns">The amount of perfect signs</param>
/// <param name="goodSigns">The amount of good signs</param>
/// <param name="mehSigns">The amount of meh signs</param>
/// <param name="terribleSigns">The emount of terrible signs</param>
/// <param name="incorrectSigns">The amount of incorrect signs</param>
/// <param name="duration">The duration of the song that was played</param>
/// <param name="score">The score obtained by the player</param>
public void GenerateContent(int perfectSigns, int goodSigns, int mehSigns, int terribleSigns, int incorrectSigns, int duration, int score)
{
// In de zone
perfectSignsText.text = perfectSigns.ToString();
// Aanvaardbaar
goodSignsText.text = goodSigns.ToString();
// Nipt
mehSignsText.text = mehSigns.ToString();
// Slechte timing
terribleSignsText.text = terribleSigns.ToString();
// Niet Geraden
notFoundSignsText.text = incorrectSigns.ToString();
// LPM
int correctSigns = goodSigns + perfectSigns + mehSigns + terribleSigns;
gpmText.text = (60f * correctSigns / duration).ToString("#") + " GPM";
// Score
scoreText.text = $"Score: {score}";
SetScoreBoard();
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 9a86c239be1aa1543ba8a4ace5f658b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -7,8 +7,7 @@
"GUID:1631ed2680c61245b8211d943c1639a8",
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25",
"GUID:58e104b97fb3752438ada2902a36dcbf",
"GUID:d0b6b39a21908f94fbbd9f2c196a9725",
"GUID:403dd94a93598934eb522dc36df43d7b"
"GUID:d0b6b39a21908f94fbbd9f2c196a9725"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -297,7 +297,6 @@ public class SignPredictor : MonoBehaviour
/// </summary>
private static bool resourceManagerIsInitialized = false;
private List<string> signs;
private EmbeddingDataList embeddingDataList;
private ModelIndex modelID;
@@ -522,6 +521,15 @@ public class SignPredictor : MonoBehaviour
{
learnableProbabilities = new Dictionary<string, float>();
// Temporary fix
List<string> signs = new List<string>()
{
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
};
for (int j = 0; j < result.Count; j++)
{
learnableProbabilities.Add(signs[j].ToUpper(), result[j]);
@@ -707,8 +715,4 @@ public class SignPredictor : MonoBehaviour
}
}
public void SetSignsList(List<string> signs)
{
this.signs = signs;
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 353d02d53aeb14341835f87efae01039
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d5e1253d871fce14ab568e5c9ad4ced2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 73d452eb6e118ec4091d6cdd82f3550c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,221 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Shared abstract class for the minigameControllers
/// </summary>
public abstract class AbstractMinigameController : AbstractFeedback
{
[Header("AbstractVariables")]
/// <summary>
/// We keep the minigamelist so that the minigame-index doesn't get reset
/// DO NOT REMOVE
/// </summary>
public MinigameList minigamelist;
/// <summary>
/// A bool to denote whether or not the game is still being played
/// </summary>
protected bool gameIsActive;
/// <summary>
/// Reference to the progress bar
/// </summary>
public Slider feedbackProgress;
/// <summary>
/// Reference to the current user
/// </summary>
private User user;
/// <summary>
/// Reference to the minigame ScriptableObject
/// </summary>
protected Minigame minigame;
/// <summary>
/// Each minigame has a webcamTexture, this will be used in children-methods
/// </summary>
public RawImage webcamScreen;
/// <summary>
/// Reference to the gameEnded panel, so we can update its display
/// </summary>
public GameObject gameEndedPanel;
/// <summary>
/// The theme that will be used by the signpredictor, this needs to be passed from the concrete class.
/// This theme CAN be different from the theme that words are fetched from (Think SpellingBee and Hangman)
/// </summary>
protected abstract Theme signPredictorTheme
{
get;
}
/// <summary>
/// Start is called before the first frame update, seal it to prevent minigames from changing it
/// </summary>
protected void Start()
{
// Get the scriptable of the current minigame
minigame = minigamelist.minigames[minigamelist.currentMinigameIndex];
// Start the game-specific start-logic
StartController();
// Prepare the signPredictor
signPredictor.SetModel(signPredictorTheme.modelIndex);
signPredictor.SwapScreen(webcamScreen);
signPredictor.SetSignsList(GetSignsList());
AddSelfAsListener();
}
/// <summary>
/// All minigames use the same principle, they grab the most probable sign and use said sign to show feedback to the user
/// Because we don't want minigames to write their own UpdateFeedbacks this function will be sealed
/// </summary>
/// <returns></returns>
/// <exception cref="System.NotImplementedException"></exception>
protected override sealed IEnumerator UpdateFeedback()
{
// Get the predicted sign
if (signPredictor != null && signPredictor.learnableProbabilities != null && gameIsActive)
{
// Get highest predicted sign
string predictedSign = signPredictor.learnableProbabilities.Aggregate((a, b) => a.Value > b.Value ? a : b).Key;
float accuracy = signPredictor.learnableProbabilities[predictedSign];
// vvv TEMPORARY STUFF vvv
if (predictedSign == "J" && accuracy <= 0.97f)
{
predictedSign = signPredictor.learnableProbabilities.Aggregate((x, y) => x.Value > y.Value && x.Key != "J" ? x : y).Key;
}
accuracy = signPredictor.learnableProbabilities[predictedSign];
// ^^^ TEMPORARY STUFF ^^^
ProcessMostProbableSign(accuracy, predictedSign);
}
// This part is the only reason that feedbackProgress is needed in the abstract
else if (feedbackProgress != null)
{
feedbackProgress.value = 0.0f;
}
yield return null;
}
/// <summary>
/// Each game keeps a score, this score needs to be saved at some point
/// </summary>
public void SaveScores()
{
// Calculate new score
int newScore = CalculateScore();
// Save the score as a tuple: < int score, string time ago>
Score score = new Score();
score.scoreValue = newScore;
score.time = DateTime.Now.ToString();
// Save the new score
var progress = user.GetMinigameProgress(minigame.index);
// Get the current list of scores
List<Score> latestScores = progress.latestScores;
List<Score> highestScores = progress.highestScores;
// Add the new score
latestScores.Add(score);
highestScores.Add(score);
// Sort the scores
highestScores.Sort((a, b) => b.scoreValue.CompareTo(a.scoreValue));
// Only save the top 10 scores, so this list doesn't keep growing endlessly
progress.latestScores = latestScores.Take(10).ToList();
progress.highestScores = highestScores.Take(10).ToList();
PersistentDataController.GetInstance().Save();
}
/// <summary>
/// The function that activates when the game ends, handles some endgame logic and displays the EndPanel
/// </summary>
/// <param name="won"></param>
public void ActivateEnd(bool victory)
{
EndGameLogic(victory);
SaveScores();
SetScoreBoard(victory);
gameEndedPanel.SetActive(true);
}
/// <summary>
/// Once the most probable sign has been fetched, they can be processed
/// </summary>
/// <param name="accuracy">The accuracy of the passed sign</param>
/// <param name="predictedSign">The name of the passed sign</param>
protected abstract void ProcessMostProbableSign(float accuracy, string predictedSign);
/// <summary>
/// Each minigame has their own way of calculating their score
/// </summary>
/// <returns>The score that the user has at that point</returns>
public abstract int CalculateScore();
/// <summary>
/// Each minigame has an AbstractGameEndedPanel at the end, but they each have their own unique concrete instance
/// </summary>
/// <param name="victory">1 if the player won, 0 if they lost. Some games need this</param>
protected abstract void SetScoreBoard(bool victory);
/// <summary>
/// Each minigame puts their GameLogic to be called at (re)start in a seperate function from Start()
/// </summary>
public void StartController()
{
StartGameLogic();
// Create entry in current user for keeping track of progress
user = UserList.GetCurrentUser();
var progress = user.GetMinigameProgress(minigame.index);
if (progress == null)
{
progress = new PersistentDataController.SavedMinigameProgress();
progress.minigameIndex = minigame.index;
user.AddMinigameProgress(progress);
}
UserList.Save();
}
/// <summary>
/// Logic to be called at the start of the game
/// </summary>
protected abstract void StartGameLogic();
/// <summary>
/// Function that contains all the logic to end the game
/// </summary>
/// <param name="victory">1 if the player won, 0 if they lost. Some games need this</param>
protected abstract void EndGameLogic(bool victory);
/// <summary>
/// All non-fingerspelling-minigames have the same logic for the GetSignsList
/// </summary>
/// <returns>The signsList that needs to be passed to the signPredictor</returns>
private List<string> GetSignsList()
{
List<string> signsList = new List<string>();
foreach (Learnable learnable in signPredictorTheme.learnables)
{
signsList.Add(learnable.name);
}
return signsList;
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a32c0ecc5507e4542a79c1b96a47b0a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,20 +0,0 @@
{
"name": "MinigameScripts",
"rootNamespace": "",
"references": [
"GUID:d0b6b39a21908f94fbbd9f2c196a9725",
"GUID:e83ddf9a537a96b4a804a16bb7872ec1",
"GUID:1631ed2680c61245b8211d943c1639a8",
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25",
"GUID:6055be8ebefd69e48b49212b09b47b2f"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 403dd94a93598934eb522dc36df43d7b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -5,7 +5,7 @@ using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
public class SpellingBeeGameEndedPanelTests
public class GameEndedPanelTests
{
[UnitySetUp]
public IEnumerator SetupFunction()
@@ -31,15 +31,15 @@ public class SpellingBeeGameEndedPanelTests
yield return new WaitForSeconds(1f);
spellingBeeController.ActivateEnd(true);
spellingBeeController.ActivateWin();
SpellingBeeGameEndedPanel SpellingBeeGameEndedPanel = (SpellingBeeGameEndedPanel)GameObject.FindObjectOfType(typeof(SpellingBeeGameEndedPanel));
Assert.NotNull(SpellingBeeGameEndedPanel);
Assert.AreEqual("Score: 0", SpellingBeeGameEndedPanel.scoreText.text);
Assert.AreEqual("1", SpellingBeeGameEndedPanel.lettersRightText.text);
Assert.AreEqual("2", SpellingBeeGameEndedPanel.lettersWrongText.text);
Assert.AreEqual("3", SpellingBeeGameEndedPanel.lettersTotalText.text);
Assert.AreEqual("00:01", SpellingBeeGameEndedPanel.timeText.text);
GameEndedPanel gameEndedPanel = (GameEndedPanel)GameObject.FindObjectOfType(typeof(GameEndedPanel));
Assert.NotNull(gameEndedPanel);
Assert.AreEqual("Score: 0", gameEndedPanel.scoreText.text);
Assert.AreEqual("1", gameEndedPanel.lettersRightText.text);
Assert.AreEqual("2", gameEndedPanel.lettersWrongText.text);
Assert.AreEqual("3", gameEndedPanel.lettersTotalText.text);
Assert.AreEqual("00:01", gameEndedPanel.timeText.text);
}
}

View File

@@ -36,12 +36,12 @@ public class SpellingBeeControllerTests
public IEnumerator ActivateGameOverTest()
{
SpellingBeeController spellingBeeController = (SpellingBeeController)GameObject.FindObjectOfType(typeof(SpellingBeeController));
spellingBeeController.ActivateEnd(false);
spellingBeeController.ActivateGameOver();
yield return new WaitForSeconds(0.2f);
SpellingBeeGameEndedPanel SpellingBeeGameEndedPanel = (SpellingBeeGameEndedPanel)GameObject.FindObjectOfType(typeof(SpellingBeeGameEndedPanel));
Assert.NotNull(SpellingBeeGameEndedPanel);
Assert.AreEqual("VERLOREN", SpellingBeeGameEndedPanel.endText.text);
GameEndedPanel gameEndedPanel = (GameEndedPanel)GameObject.FindObjectOfType(typeof(GameEndedPanel));
Assert.NotNull(gameEndedPanel);
Assert.AreEqual("VERLOREN", gameEndedPanel.endText.text);
}
@@ -49,12 +49,12 @@ public class SpellingBeeControllerTests
public IEnumerator ActivateWinTests()
{
SpellingBeeController spellingBeeController = (SpellingBeeController)GameObject.FindObjectOfType(typeof(SpellingBeeController));
spellingBeeController.ActivateEnd(true);
spellingBeeController.ActivateWin();
yield return new WaitForSeconds(0.2f);
SpellingBeeGameEndedPanel SpellingBeeGameEndedPanel = (SpellingBeeGameEndedPanel)GameObject.FindObjectOfType(typeof(SpellingBeeGameEndedPanel));
Assert.NotNull(SpellingBeeGameEndedPanel);
Assert.AreEqual("GEWONNEN", SpellingBeeGameEndedPanel.endText.text);
GameEndedPanel gameEndedPanel = (GameEndedPanel)GameObject.FindObjectOfType(typeof(GameEndedPanel));
Assert.NotNull(gameEndedPanel);
Assert.AreEqual("GEWONNEN", gameEndedPanel.endText.text);
}
[UnityTest]
@@ -64,8 +64,8 @@ public class SpellingBeeControllerTests
spellingBeeController.AddSeconds(-60);
yield return new WaitForSeconds(0.1f);
SpellingBeeGameEndedPanel SpellingBeeGameEndedPanel = (SpellingBeeGameEndedPanel)GameObject.FindObjectOfType(typeof(SpellingBeeGameEndedPanel));
Assert.NotNull(SpellingBeeGameEndedPanel);
Assert.AreEqual("VERLOREN", SpellingBeeGameEndedPanel.endText.text);
GameEndedPanel gameEndedPanel = (GameEndedPanel)GameObject.FindObjectOfType(typeof(GameEndedPanel));
Assert.NotNull(gameEndedPanel);
Assert.AreEqual("VERLOREN", gameEndedPanel.endText.text);
}
}

View File

@@ -8,9 +8,8 @@
"Unity.TextMeshPro",
"SpellingBeeScripts",
"AccountsScripts",
"ArchitectureScripts",
"SignPredictor",
"MinigameScripts"
"SystemArchitecture",
"SignPredictor"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -1432,8 +1432,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 5aa929dce1f59b340b4a0cca1bb68edc, type: 3}
m_Name:
m_EditorClassIdentifier:
scoreboardEntriesContainer: {fileID: 1499197559}
scoreboardEntry: {fileID: 9154151134820372555, guid: d4a3a228b08d61847acc6da35b44e52c, type: 3}
endText: {fileID: 1502459770}
lpmText: {fileID: 1172084829}
lettersRightText: {fileID: 994850063}
@@ -1443,6 +1441,8 @@ MonoBehaviour:
wordsText: {fileID: 1754130538}
timeText: {fileID: 1052827058}
scoreText: {fileID: 653157662}
scoreboardEntriesContainer: {fileID: 1499197559}
scoreboardEntry: {fileID: 9154151134820372555, guid: d4a3a228b08d61847acc6da35b44e52c, type: 3}
--- !u!1 &778704239
GameObject:
m_ObjectHideFlags: 0
@@ -3982,20 +3982,21 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
signPredictor: {fileID: 1592592444}
minigamelist: {fileID: 11400000, guid: 51453f9b41bc72f468ba3e67ab622f8f, type: 2}
feedbackProgress: {fileID: 967164046}
webcamScreen: {fileID: 1743003084}
gameEndedPanel: {fileID: 757133117}
themeList: {fileID: 11400000, guid: a247e2ce790f0f746a3bc521e6ab7d58, type: 2}
fingerspelling: {fileID: 11400000, guid: e02921b294fdad940b6e4d57e716d3bf, type: 2}
minigame: {fileID: 11400000, guid: 8a087d241d652634eb4f6352267ea7dc, type: 2}
minigamelist: {fileID: 11400000, guid: 51453f9b41bc72f468ba3e67ab622f8f, type: 2}
letterPrefab: {fileID: 4639383499500021565, guid: c3e66e8957864914cb022af914df6a28, type: 3}
letterContainer: {fileID: 1346005056}
wordImage: {fileID: 1338727891}
timerText: {fileID: 1843239267}
bonusTimeText: {fileID: 1812475780}
Scoreboard: {fileID: 862382568}
gameEndedPanel: {fileID: 757133117}
feedbackText: {fileID: 967164047}
feedbackProgress: {fileID: 967164046}
feedbackProgressImage: {fileID: 967164045}
webcamScreen: {fileID: 1743003084}
scoreDisplay: {fileID: 1985911006}
scoreBonus: {fileID: 1130901870}
--- !u!1 &1499197558

View File

@@ -4,19 +4,50 @@ using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Abstract class for all minigame-gameEndedPanels
/// </summary>
public abstract class AbstractGameEndedPanel : MonoBehaviour
public class GameEndedPanel : MonoBehaviour
{
/// <summary>
/// The index of minigame that needs a GameEndedPanel
/// "VERLOREN" or "GEWONNEN"
/// </summary>
protected abstract MinigameIndex minigameIndex
{
get;
}
public TMP_Text endText;
/// <summary>
/// LPM
/// </summary>
public TMP_Text lpmText;
/// <summary>
/// Letters ( right | wrong )
/// </summary>
public TMP_Text lettersRightText;
public TMP_Text lettersWrongText;
/// <summary>
/// Letters
/// </summary>
public TMP_Text lettersTotalText;
/// <summary>
/// Accuracy
/// </summary>
public TMP_Text accuracyText;
/// <summary>
/// Words
/// </summary>
public TMP_Text wordsText;
/// <summary>
/// Time
/// </summary>
public TMP_Text timeText;
/// <summary>
/// Score
/// </summary>
public TMP_Text scoreText;
/// <summary>
/// Reference to the scoreboard entries container
/// </summary>
@@ -32,10 +63,55 @@ public abstract class AbstractGameEndedPanel : MonoBehaviour
/// </summary>
public GameObject scoreboardEntry;
/// <summary>
/// Generate the content of the GameEnded panel
/// </summary>
/// <param name="startTime">Time of starting the minigame</param>
/// <param name="totalWords">Total number of words</param>
/// <param name="correctLetters">Total number of correctly spelled letters</param>
/// <param name="incorrectLetters">Total number of incorrectly spelled letters</param>
/// <param name="result">"VERLOREN" or "GEWONNEN"</param>
/// <param name="score">Final score</param>
public void GenerateContent(DateTime startTime, int totalWords, int correctLetters, int incorrectLetters, string result, int score)
{
// Final result
endText.text = result;
// LPM
TimeSpan duration = DateTime.Now.Subtract(startTime);
lpmText.text = (60f * correctLetters / duration.TotalSeconds).ToString("#") + " LPM";
// Letters ( right | wrong ) total
lettersRightText.text = correctLetters.ToString();
lettersWrongText.text = incorrectLetters.ToString();
lettersTotalText.text = (correctLetters + incorrectLetters).ToString();
// Accuracy
if (correctLetters + incorrectLetters > 0)
{
accuracyText.text = ((correctLetters) * 100f / (correctLetters + incorrectLetters)).ToString("#.##") + "%";
}
else
{
accuracyText.text = "-";
}
// Words
wordsText.text = $"{totalWords}";
// Time
timeText.text = duration.ToString(@"mm\:ss");
// Score
scoreText.text = $"Score: {score}";
SetScoreBoard();
}
/// <summary>
/// Sets the scoreboard
/// </summary>
protected void SetScoreBoard()
private void SetScoreBoard()
{
// Clean the previous scoreboard entries
for (int i = 0; i < scoreboardEntries.Count; i++)
@@ -50,7 +126,7 @@ public abstract class AbstractGameEndedPanel : MonoBehaviour
foreach (User user in UserList.GetUsers())
{
// Get user's progress for this minigame
var progress = user.GetMinigameProgress(minigameIndex);
var progress = user.GetMinigameProgress(MinigameIndex.SPELLING_BEE);
if (progress != null)
{
// Add scores to dictionary

View File

@@ -7,7 +7,7 @@ using TMPro;
using UnityEngine;
using UnityEngine.UI;
public partial class SpellingBeeController : AbstractMinigameController
public partial class SpellingBeeController : AbstractFeedback
{
/// <summary>
/// All of the words that can be used in this session
@@ -45,6 +45,11 @@ public partial class SpellingBeeController : AbstractMinigameController
/// </summary>
private float timerValue;
/// <summary>
/// Indicates if the game is still going
/// </summary>
private bool gameEnded;
/// <summary>
/// List of learnables to get the threshold for the letters
/// </summary>
@@ -76,6 +81,22 @@ public partial class SpellingBeeController : AbstractMinigameController
/// </summary>
private DateTime startTime;
/// <summary>
/// Reference to the current user
/// </summary>
private User user;
/// <summary>
/// Reference to the minigame ScriptableObject
/// </summary>
public Minigame minigame;
/// <summary>
/// We keep the minigamelist as well so that the minigame-index doesn't get reset
/// DO NOT REMOVE
/// </summary>
public MinigameList minigamelist;
/// <summary>
/// Letter prefab
/// </summary>
@@ -116,11 +137,21 @@ public partial class SpellingBeeController : AbstractMinigameController
/// </summary>
public Transform Scoreboard;
/// <summary>
/// Reference to the gameEnded panel, so we can update its display
/// </summary>
public GameObject gameEndedPanel;
/// <summary>
/// Reference to the feedback field
/// </summary>
public TMP_Text feedbackText;
/// <summary>
/// Reference to the progress bar
/// </summary>
public Slider feedbackProgress;
/// <summary>
/// Reference to the progress bar image, so we can add fancy colors
/// </summary>
@@ -141,6 +172,11 @@ public partial class SpellingBeeController : AbstractMinigameController
/// </summary>
protected string previousIncorrectSign = null;
/// <summary>
/// Reference used to set the webcam for the SignPredictor
/// </summary>
public RawImage webcamScreen;
/// <summary>
/// Reference to display the score
/// </summary>
@@ -162,11 +198,57 @@ public partial class SpellingBeeController : AbstractMinigameController
private int incorrectLettersScore = -5;
/// <summary>
/// Set the AbstractMinigameController variable to inform it of the theme for the signPredictor
/// Start is called before the first frame update
/// </summary>
protected override Theme signPredictorTheme
public void Start()
{
get { return fingerspelling; }
signPredictor.SetModel(ModelIndex.FINGERSPELLING);
signPredictor.SwapScreen(webcamScreen);
AddSelfAsListener();
StartController();
}
/// <summary>
/// Is called at the start of the scene AND when the game is replayed
/// </summary>
public void StartController()
{
correctLetters = 0;
incorrectLetters = 0;
words.Clear();
// We use -1 instead of 0 so SetNextWord can simply increment it each time
spelledWords = -1;
wordIndex = 0;
gameEnded = false;
timerValue = 30.0f;
bonusActiveRemaining = 0.0f;
startTime = DateTime.Now;
gameEndedPanel.SetActive(false);
bonusTimeText.SetActive(false);
// Create entry in current user for keeping track of progress
user = UserList.GetCurrentUser();
var progress = user.GetMinigameProgress(minigame.index);
if (progress == null)
{
progress = new PersistentDataController.SavedMinigameProgress();
progress.minigameIndex = MinigameIndex.SPELLING_BEE;
user.AddMinigameProgress(progress);
}
UserList.Save();
currentTheme = minigame.themeList.themes[minigame.themeList.currentThemeIndex];
//feedback.signPredictor.SetModel(currentTheme.modelIndex);
words.AddRange(currentTheme.learnables);
ShuffleWords();
NextWord();
scoreDisplay.text = $"Score: {CalculateScore()}";
scoreBonus.text = "";
}
/// <summary>
@@ -174,7 +256,7 @@ public partial class SpellingBeeController : AbstractMinigameController
/// </summary>
public void Update()
{
if (gameIsActive)
if (!gameEnded)
{
timerValue -= Time.deltaTime;
if (bonusActiveRemaining <= 0.0 && bonusTimeText.activeSelf)
@@ -190,8 +272,7 @@ public partial class SpellingBeeController : AbstractMinigameController
if (timerValue <= 0.0f)
{
timerValue = 0.0f;
//ActivateGameOver();
ActivateEnd(false);
ActivateGameOver();
}
int minutes = Mathf.FloorToInt(timerValue / 60.0f);
@@ -221,11 +302,89 @@ public partial class SpellingBeeController : AbstractMinigameController
/// Calculate the score
/// </summary>
/// <returns>The calculated score</returns>
public override int CalculateScore()
public int CalculateScore()
{
return correctLetters * correctLettersScore + incorrectLetters * incorrectLettersScore;
}
/// <summary>
/// Displays the game over panel and score values
/// </summary>
public void ActivateGameOver()
{
gameEnded = true;
DeleteWord();
// Save the scores and show the scoreboard
SaveScores();
gameEndedPanel.GetComponent<GameEndedPanel>().GenerateContent(
startTime: startTime,
totalWords: spelledWords,
correctLetters: correctLetters,
incorrectLetters: incorrectLetters,
result: "VERLOREN",
score: CalculateScore()
);
gameEndedPanel.SetActive(true);
}
/// <summary>
/// Display win screen
/// </summary>
public void ActivateWin()
{
gameEnded = true;
DeleteWord();
// Save the scores and show the scoreboard
SaveScores();
gameEndedPanel.GetComponent<GameEndedPanel>().GenerateContent(
startTime: startTime,
totalWords: spelledWords,
correctLetters: correctLetters,
incorrectLetters: incorrectLetters,
result: "GEWONNEN",
score: CalculateScore()
);
gameEndedPanel.SetActive(true);
}
/// <summary>
/// Update and save the scores
/// </summary>
public void SaveScores()
{
// Calculate new score
int newScore = CalculateScore();
// Save the score as a tuple: < int score, string time ago>
Score score = new Score();
score.scoreValue = newScore;
score.time = DateTime.Now.ToString();
// Save the new score
var progress = user.GetMinigameProgress(minigame.index);
// Get the current list of scores
List<Score> latestScores = progress.latestScores;
List<Score> highestScores = progress.highestScores;
// Add the new score
latestScores.Add(score);
highestScores.Add(score);
// Sort the scores
highestScores.Sort((a, b) => b.scoreValue.CompareTo(a.scoreValue));
// Only save the top 10 scores, so this list doesn't keep growing endlessly
progress.latestScores = latestScores.Take(10).ToList();
progress.highestScores = highestScores.Take(10).ToList();
UserList.Save();
}
/// <summary>
/// Delete all letter objects
/// </summary>
@@ -255,7 +414,7 @@ public partial class SpellingBeeController : AbstractMinigameController
/// <param name="successful">true if the letter was correctly signed, false otherwise</param>
public void NextLetter(bool successful)
{
if (!gameIsActive) { return; }
if (gameEnded) { return; }
// Change color of current letter (skip spaces)
if (successful)
@@ -310,8 +469,7 @@ public partial class SpellingBeeController : AbstractMinigameController
}
else
{
//ActivateWin();
ActivateEnd(true);
ActivateWin();
}
}
@@ -356,7 +514,7 @@ public partial class SpellingBeeController : AbstractMinigameController
Learnable letter = fingerspelling.learnables.Find(l => l.name == sign);
return letter.thresholdPercentage;
}
/*
/// <summary>
/// The updateFunction that is called when new probabilities become available
/// </summary>
@@ -367,7 +525,7 @@ public partial class SpellingBeeController : AbstractMinigameController
string currentSign = GetSign();
// Get the predicted sign
if (signPredictor != null && signPredictor.learnableProbabilities != null &&
currentSign != null && signPredictor.learnableProbabilities.ContainsKey(currentSign) && gameIsActive)
currentSign != null && signPredictor.learnableProbabilities.ContainsKey(currentSign))
{
float accCurrentSign = signPredictor.learnableProbabilities[currentSign];
float thresholdCurrentSign = GetTresholdPercentage(currentSign);
@@ -455,7 +613,6 @@ public partial class SpellingBeeController : AbstractMinigameController
}
yield return null;
}
*/
/// <summary>
/// Function to get the current letter that needs to be signed
/// </summary>
@@ -481,157 +638,4 @@ public partial class SpellingBeeController : AbstractMinigameController
}
NextLetter(successful);
}
/// <summary>
/// The logic to process the signs sent by the signPredictor
/// </summary>
/// <param name="accuracy">The accuracy of the passed sign</param>
/// <param name="predictedSign">The name of the passed sign</param>
protected override void ProcessMostProbableSign(float accuracy, string predictedSign)
{
string currentSign = GetSign();
float accPredictSign = accuracy;
float accCurrentSign = signPredictor.learnableProbabilities[currentSign];
float thresholdCurrentSign = GetTresholdPercentage(currentSign);
float thresholdPredictedSign = GetTresholdPercentage(predictedSign);
// If there is a feedback-object, we wil change its appearance
if (feedbackText != null && feedbackProgressImage != null)
{
Color col;
if (accPredictSign > thresholdCurrentSign)
{
feedbackText.text = "Goed";
col = new Color(0x8b / 255.0f, 0xd4 / 255.0f, 0x5e / 255.0f);
}
else if (accCurrentSign > 0.9 * thresholdCurrentSign)
{
feedbackText.text = "Bijna...";
col = new Color(0xf2 / 255.0f, 0x7f / 255.0f, 0x0c / 255.0f);
}
else if (accPredictSign > thresholdPredictedSign)
{
feedbackText.text = $"Verkeerde gebaar: '{predictedSign}'";
col = new Color(0xf5 / 255.0f, 0x49 / 255.0f, 0x3d / 255.0f);
accCurrentSign = 0.0f;
}
else
{
feedbackText.text = "Detecteren...";
col = new Color(0xf5 / 255.0f, 0x49 / 255.0f, 0x3d / 255.0f);
}
feedbackText.color = col;
feedbackProgressImage.color = col;
float oldValue = feedbackProgress.value;
// use an exponential scale
float newValue = Mathf.Exp(4 * (Mathf.Clamp(accCurrentSign / thresholdCurrentSign, 0.0f, 1.0f) - 1.0f));
feedbackProgress.gameObject.Tween("FeedbackUpdate", oldValue, newValue, 0.2f, TweenScaleFunctions.CubicEaseInOut, (t) =>
{
if (feedbackProgress != null)
{
feedbackProgress.value = t.CurrentValue;
}
});
}
// The logic for the internal workings of the game
if (accPredictSign > thresholdPredictedSign)
{
// Correct sign, instantly pass it along
if (predictedSign == currentSign)
{
PredictSign(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
// Incorrect sign, wait a bit before passing it along
else
{
if (previousIncorrectSign != predictedSign)
{
timer = DateTime.Now;
previousIncorrectSign = predictedSign;
}
else if (DateTime.Now - timer > TimeSpan.FromSeconds(2.0f))
{
PredictSign(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
}
}
}
/// <summary>
/// The logic to set the scoreboard of spellingbee
/// </summary>
/// <param name="victory">SHows whether or not the player won</param>
protected override void SetScoreBoard(bool victory)
{
string resultTxt;
if (victory)
{
resultTxt = "GEWONNEN";
}
else
{
resultTxt = "VERLOREN";
}
// Save the scores and show the scoreboard
gameEndedPanel.GetComponent<SpellingBeeGameEndedPanel>().GenerateContent(
startTime: startTime,
totalWords: spelledWords,
correctLetters: correctLetters,
incorrectLetters: incorrectLetters,
result: resultTxt,
score: CalculateScore()
);
}
/// <summary>
/// The spellinbee-specific logic that needs to be called at the start of the game
/// </summary>
protected override void StartGameLogic()
{
correctLetters = 0;
incorrectLetters = 0;
words.Clear();
// We use -1 instead of 0 so SetNextWord can simply increment it each time
spelledWords = -1;
wordIndex = 0;
gameIsActive = true;
timerValue = 30.0f;
bonusActiveRemaining = 0.0f;
startTime = DateTime.Now;
gameEndedPanel.SetActive(false);
bonusTimeText.SetActive(false);
currentTheme = minigame.themeList.themes[minigame.themeList.currentThemeIndex];
//feedback.signPredictor.SetModel(currentTheme.modelIndex);
words.AddRange(currentTheme.learnables);
ShuffleWords();
NextWord();
scoreDisplay.text = $"Score: {CalculateScore()}";
scoreBonus.text = "";
}
/// <summary>
/// The spellingbee-specific logic that needs to be called at the end of a game
/// </summary>
/// <param name="victory"></param>
protected override void EndGameLogic(bool victory)
{
gameIsActive = false;
DeleteWord();
}
}

View File

@@ -1,102 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class SpellingBeeGameEndedPanel : AbstractGameEndedPanel
{
/// <summary>
/// Tell the scoreboard that the scoreboard is for SpellingBee
/// </summary>
protected override MinigameIndex minigameIndex
{
get { return MinigameIndex.SPELLING_BEE; }
}
/// <summary>
/// "VERLOREN" or "GEWONNEN"
/// </summary>
public TMP_Text endText;
/// <summary>
/// LPM
/// </summary>
public TMP_Text lpmText;
/// <summary>
/// Letters ( right | wrong )
/// </summary>
public TMP_Text lettersRightText;
public TMP_Text lettersWrongText;
/// <summary>
/// Letters
/// </summary>
public TMP_Text lettersTotalText;
/// <summary>
/// Accuracy
/// </summary>
public TMP_Text accuracyText;
/// <summary>
/// Words
/// </summary>
public TMP_Text wordsText;
/// <summary>
/// Time
/// </summary>
public TMP_Text timeText;
/// <summary>
/// Score
/// </summary>
public TMP_Text scoreText;
/// <summary>
/// Generate the content of the GameEnded panel
/// </summary>
/// <param name="startTime">Time of starting the minigame</param>
/// <param name="totalWords">Total number of words</param>
/// <param name="correctLetters">Total number of correctly spelled letters</param>
/// <param name="incorrectLetters">Total number of incorrectly spelled letters</param>
/// <param name="result">"VERLOREN" or "GEWONNEN"</param>
/// <param name="score">Final score</param>
public void GenerateContent(DateTime startTime, int totalWords, int correctLetters, int incorrectLetters, string result, int score)
{
// Final result
endText.text = result;
// LPM
TimeSpan duration = DateTime.Now.Subtract(startTime);
lpmText.text = (60f * correctLetters / duration.TotalSeconds).ToString("#") + " LPM";
// Letters ( right | wrong ) total
lettersRightText.text = correctLetters.ToString();
lettersWrongText.text = incorrectLetters.ToString();
lettersTotalText.text = (correctLetters + incorrectLetters).ToString();
// Accuracy
if (correctLetters + incorrectLetters > 0)
{
accuracyText.text = ((correctLetters) * 100f / (correctLetters + incorrectLetters)).ToString("#.##") + "%";
}
else
{
accuracyText.text = "-";
}
// Words
wordsText.text = $"{totalWords}";
// Time
timeText.text = duration.ToString(@"mm\:ss");
// Score
scoreText.text = $"Score: {score}";
SetScoreBoard();
}
}

View File

@@ -8,8 +8,7 @@
"GUID:d0b6b39a21908f94fbbd9f2c196a9725",
"GUID:58e104b97fb3752438ada2902a36dcbf",
"GUID:e83ddf9a537a96b4a804a16bb7872ec1",
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25",
"GUID:403dd94a93598934eb522dc36df43d7b"
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -1,5 +1,5 @@
{
"name": "ArchitectureScripts",
"name": "SystemArchitecture",
"rootNamespace": "",
"references": [],
"includePlatforms": [],

View File

@@ -1,12 +1,14 @@
{
"name": "ArchitectureTests",
"name": "SystemArchitectureTests",
"rootNamespace": "",
"references": [
"UnityEditor.TestRunner",
"UnityEngine.TestRunner",
"ArchitectureScripts"
"SystemArchitecture"
],
"includePlatforms": [
"Editor"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,

Binary file not shown.

View File

@@ -168,8 +168,7 @@ PlayerSettings:
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 0}
- {fileID: 11400000, guid: 57fdfdc7df920454ba35444c783867d8, type: 2}
metroInputSource: 0
wsaTransparentSwapchain: 0
m_HolographicPauseOnTrackingLoss: 1

Binary file not shown.