101 Commits

Author SHA1 Message Date
hvbreuge
2736de811d Added timer to SpellingBee to see when a sign is accepted. Times display -> accepted 2023-05-18 14:57:09 +02:00
b729981edf New MacOS build 2023-05-18 10:23:24 +02:00
Jerome Coudron
a0dd0ae39f Merge branch 'WES-XX-SignPredictorDoc-Little-UI' into 'development'
Wes xx sign predictor doc little ui

See merge request wesign/unity-application!108
2023-05-17 17:36:33 +00:00
Dries Van Schuylenbergh
f63b49ebc7 Wes xx sign predictor doc little ui 2023-05-17 17:36:27 +00:00
Dries Van Schuylenbergh
3dded6c3e6 Merge branch 'WES-167-theme-descriptions' into 'development'
Theme descriptions added

See merge request wesign/unity-application!109
2023-05-17 15:52:10 +00:00
hvbreuge
10649e9b85 Theme descriptions added 2023-05-16 21:58:28 +02:00
Jerome Coudron
349ca1cb44 Merge branch 'WES-XX-SignPredictorDoc-Little-UI' into 'development'
Annotations + Removal of unneeded lines

See merge request wesign/unity-application!107
2023-05-16 11:09:45 +00:00
Jelle De Geest
785e56b883 Annotations + Removal of unneeded lines 2023-05-16 11:09:41 +00:00
Louis Adriaens
8c69ec3cc6 Merge branch 'WES-181-missing-code-doc' into 'development'
Resolve WES-181 "Missing code doc"

See merge request wesign/unity-application!95
2023-05-14 20:18:31 +00:00
Dries Van Schuylenbergh
3d99184717 Resolve WES-181 "Missing code doc" 2023-05-14 20:18:29 +00:00
Jerome Coudron
7505ae7262 Merge branch 'WES-XX-GPU-Test' into 'development'
Wes xx gpu test

See merge request wesign/unity-application!106
2023-05-14 19:41:40 +00:00
Jelle De Geest
1f5b855f61 Wes xx gpu test 2023-05-14 19:41:37 +00:00
Jelle De Geest
384c7a249d Merge branch 'WES-XX-Fix-JustSignGameEndedPanel' into 'development'
Resolve: FIxGameEndedPanel for JustSign

See merge request wesign/unity-application!105
2023-05-14 19:06:35 +00:00
Jerome Coudron
bdf3c05102 Resolve: FIxGameEndedPanel for JustSign 2023-05-14 19:06:34 +00:00
Louis Adriaens
584dc0d97d Merge branch 'WES-189-themes-pictures-wording' into 'development'
Resolve WES-189 "Themes pictures wording"

See merge request wesign/unity-application!103
2023-05-14 17:26:24 +00:00
Helena Van Breugel
084724c175 Resolve WES-189 "Themes pictures wording" 2023-05-14 17:26:22 +00:00
Dries Van Schuylenbergh
1cceb3cb89 Merge branch 'WES-160-fix-unity-editor-crashing' into 'development'
transcoded all video files

See merge request wesign/unity-application!102
2023-05-14 13:18:47 +00:00
Louis Adriaens
f99a73f230 transcoded all video files 2023-05-14 13:18:45 +00:00
Dries Van Schuylenbergh
e0c948292d Merge branch 'WES-XX-PreviewFix' into 'development'
Fixed preview and clothes bug

See merge request wesign/unity-application!100
2023-05-14 12:37:37 +00:00
Jerome Coudron
7df6ea1ecd Fixed preview and clothes bug 2023-05-14 12:37:36 +00:00
Dries Van Schuylenbergh
e9753a727e Merge branch 'WeSign-Logo' into 'development'
Logo icon

See merge request wesign/unity-application!98
2023-05-14 12:25:58 +00:00
Dries Van Schuylenbergh
a4c29a3146 Merge branch 'WES-187-unit-tests-justsign-and-spellingbee' into 'development'
Resolve WES-187 "Unit tests justsign and spellingbee"

See merge request wesign/unity-application!93
2023-05-14 11:58:09 +00:00
Helena Van Breugel
53fc361af4 Resolve WES-187 "Unit tests justsign and spellingbee" 2023-05-14 11:58:09 +00:00
Louis Adriaens
d593f22c10 Merge branch 'WES-166-Activity-Endscene' into 'development'
Resolve WES-166 "Activity endscene"

See merge request wesign/unity-application!101
2023-05-14 10:58:44 +00:00
Jelle De Geest
9b88537d70 Resolve WES-166 "Activity endscene" 2023-05-14 10:58:43 +00:00
Supaaah1
96b1c9f6bc Logo icon 2023-05-13 16:02:57 +02:00
Louis Adriaens
1e09f09dae Merge branch 'WES-204-user-progress-bug' into 'development'
Resolve WES-204 "User progress bug"

See merge request wesign/unity-application!97
2023-05-13 13:18:38 +00:00
Dries Van Schuylenbergh
1413f80d73 Resolve WES-204 "User progress bug" 2023-05-13 13:18:37 +00:00
Jerome Coudron
227099631e Merge branch 'WES-187-unit-tests-common-and-course' into 'development'
Resolve WES-187 "Unit tests common and course"

See merge request wesign/unity-application!94
2023-05-12 11:53:21 +00:00
Jelle De Geest
48b915acba Resolve WES-187 "Unit tests common and course" 2023-05-12 11:53:17 +00:00
Jelle De Geest
672ff367e2 Merge branch 'WES-203-Course-freezing' into 'development'
Fixed course freezing and other course-ending bugs

See merge request wesign/unity-application!92
2023-05-08 21:38:43 +00:00
CoudronJerome
ae29ef9835 Fixed course freezing and other course-ending bugs 2023-05-08 20:57:09 +02:00
c3ebf48cdc Added new MacOS build 2023-05-08 09:31:24 +02:00
Tibe Habils
59bf2392d8 add windows installer 2023-05-07 22:13:42 +02:00
Jelle De Geest
e9db885301 Merge branch 'new-basic-signs-model' into 'development'
New basic signs model

See merge request wesign/unity-application!90
2023-05-07 21:00:56 +00:00
Jerome Coudron
43887af670 New basic signs model 2023-05-07 21:00:52 +00:00
Jelle De Geest
06aa9206ac Merge branch 'WES-187-unit-tests-hangman-and-minigame' into 'development'
Resolve WES-187-unit-tests-hangman-and-minigame

See merge request wesign/unity-application!87
2023-05-06 11:36:52 +00:00
Jerome Coudron
c24fe037f6 Resolve WES-187-unit-tests-hangman-and-minigame 2023-05-06 11:36:51 +00:00
Dries Van Schuylenbergh
b10358930b Merge branch 'WES-192-fix-progress-screen' into 'development'
Resolve WES-192 "Fix progress screen"

See merge request wesign/unity-application!88
2023-05-04 09:09:53 +00:00
Dries Van Schuylenbergh
89f343780a Resolve WES-192 "Fix progress screen" 2023-05-04 09:09:49 +00:00
Jerome Coudron
259259ac9c Merge branch 'Wes-101-Speedup-Mediapipe' into 'development'
WES 101 speedup mediapipe

See merge request wesign/unity-application!89
2023-05-03 18:48:59 +00:00
Jelle De Geest
bc502361cd WES 101 speedup mediapipe 2023-05-03 18:48:56 +00:00
Jerome Coudron
fb234480f7 Merge branch 'WES-187-unit-tests-account-and-system' into 'development'
Resolve WES-187 "Unit tests account and system"

See merge request wesign/unity-application!86
2023-04-30 15:51:45 +00:00
Dries Van Schuylenbergh
b9bbef8dcf Resolve WES-187 "Unit tests account and system" 2023-04-30 15:51:41 +00:00
Tibe Habils
c4b6c60288 added windows installer 2023-04-24 00:28:36 +02:00
c457a195df New MacOS build 2023-04-24 00:01:54 +02:00
Jelle De Geest
67575cfbbd Merge branch 'fix-testing' into 'development'
Fix code coverage of testing

See merge request wesign/unity-application!83
2023-04-23 21:37:32 +00:00
Tibe Habils
05b230dd9d Fix code coverage of testing 2023-04-23 21:37:31 +00:00
Jelle De Geest
94d2ccfa8d Merge branch 'WES-132-Common-Abstract-Minigame' into 'development'
Resolve WES-132-Common-Abstract-Minigame

See merge request wesign/unity-application!81
2023-04-23 21:20:14 +00:00
Jerome Coudron
966475455a Resolve WES-132-Common-Abstract-Minigame 2023-04-23 21:20:12 +00:00
Jelle De Geest
4fdb8f95cb Merge branch 'demo-day-booth' into 'development'
Demo day booth

See merge request wesign/unity-application!84
2023-04-23 18:50:09 +00:00
Dries Van Schuylenbergh
fcd8acad1e Demo day booth 2023-04-23 18:50:07 +00: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
2300 changed files with 119555 additions and 137944 deletions

View File

@@ -27,7 +27,7 @@ steps:
pull: if-not-exists
image: sonarsource/sonar-scanner-cli
commands:
- sonar-scanner -Dsonar.host.url=$SONAR_HOST -Dsonar.login=$SONAR_TOKEN -Dsonar.projectKey=$SONAR_PROJECT_KEY -Dsonar.coverageReportPaths="./code_coverage/Report/SonarQube.xml"
- sonar-scanner -Dsonar.host.url=$SONAR_HOST -Dsonar.login=$SONAR_TOKEN -Dsonar.projectKey=$SONAR_PROJECT_KEY -Dsonar.projectVersion=$DRONE_REPO_BRANCH-$DRONE_COMMIT_AUTHOR-$DRONE_COMMIT -Dsonar.coverageReportPaths="./code_coverage/Report/SonarQube.xml"
environment:
SONAR_HOST:
from_secret: sonar_host
@@ -37,47 +37,3 @@ steps:
from_secret: sonar_project_key
---
kind: pipeline
name: builds
type: docker
trigger:
event:
- push
branch:
- master
- development
- Automatic-Builds
steps:
- name: unity-builds
image: docker.io/library/unity-runner:0.1
commands:
- mkdir -p ./wesign-builds/{tmp,WeSign-Windows,WeSign-Linux,WeSign-MacOS}
- /opt/unity/editors/2021.3.19f1/Editor/Unity -batchmode -nographics -projectPath . -buildWindowsPlayer ./wesign-builds/tmp/WeSign-Windows.exe -quit --headless || true
- chmod 777 -R .
- /opt/unity/editors/2021.3.19f1/Editor/Unity -batchmode -nographics -projectPath . -buildWindowsPlayer ./wesign-builds/WeSign-Windows/WeSign.exe -quit --headless
- /opt/unity/editors/2021.3.19f1/Editor/Unity -batchmode -nographics -projectPath . -buildLinux64Player ./wesign-builds/WeSign-Linux/WeSign -quit --headless
- /opt/unity/editors/2021.3.19f1/Editor/Unity -batchmode -nographics -projectPath . -buildOSXUniversalPlayer ./wesign-builds/WeSign-MacOS/WeSign.app -quit --headless
- name: commit-files
image: alpine/git
environment:
GIT_AUTHOR_NAME: DroneCI
GIT_AUTHOR_EMAIL: droneci@wesign.com
GIT_COMMITTER_NAME: DroneCI
GIT_COMMITTER_EMAIL: droneci@wesign.com
commands:
- git config --global user.name "DroneCI"
- git config --global user.email "droneci@wesign.com"
- zip -r ./wesign-builds/WeSign-Windows.zip ./wesign-builds/WeSign-Windows
- zip -r ./wesign-builds/WeSign-MacOS.zip ./wesign-builds/WeSign-MacOS
- zip -r ./wesign-builds/WeSign-Linux.zip ./wesign-builds/WeSign-Linux
- git add ./wesign-builds/WeSign-Windows.zip
- git add ./wesign-builds/WeSign-MacOS.zip
- git add ./wesign-builds/WeSign-Linux.zip
- git commit -m "Add build files [skip ci]"
- git push -f https://oauth2:ixKiNbp48zzmP5PF-epo@gitlab.ilabt.imec.be/wesign/unity-application/

5
.gitignore vendored
View File

@@ -9,6 +9,7 @@
/[Bb]uilds/
/[Ll]ogs/
/[Uu]ser[Ss]ettings/
/[Cc]ode[Cc]overage/
# MemoryCaptures can get excessive in size.
# They also could contain extremely sensitive data
@@ -72,4 +73,6 @@ crashlytics-build.properties
/[Aa]ssets/[Ss]treamingAssets/aa.meta
/[Aa]ssets/[Ss]treamingAssets/aa/*
.DS_Store
.DS_Store
/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json
CodeCoverage/

View File

@@ -0,0 +1,137 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &7141392721760992647
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7141392721760992646}
- component: {fileID: 7141392721760992644}
- component: {fileID: 2518831828376613321}
m_Layer: 5
m_Name: Axes Tick Marker
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7141392721760992646
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7141392721760992647}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 0, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 50, y: 50}
m_Pivot: {x: 0, y: 0}
--- !u!222 &7141392721760992644
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7141392721760992647}
m_CullTransparentMesh: 1
--- !u!114 &2518831828376613321
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7141392721760992647}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 0
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4279242768
m_fontColor: {r: 0.0627451, g: 0.0627451, b: 0.0627451, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 36
m_fontSizeBase: 36
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 36
m_fontStyle: 0
m_HorizontalAlignment: 4
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 0
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9d96609216a825843b33f6af9cdfa29b
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -343,6 +343,12 @@ MonoBehaviour:
button: {fileID: -2577348215772602702}
courseProgress:
entries: []
courseIndex: 0
progress: -1
completedLearnables: 0
inUseLearnables: 0
totalLearnables: 0
learnables: []
courseList: {fileID: 11400000, guid: a7ab583094b7897468bbca9243717608, type: 2}
thumbnail: {fileID: 5101881939775039227}
title: {fileID: 5101881939882359064}
@@ -491,15 +497,15 @@ MonoBehaviour:
m_Calls: []
m_text: <Minigame title>
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontAsset: {fileID: 11400000, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_sharedMaterial: {fileID: -2577534979213189211, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
rgba: 4284235525
m_fontColor: {r: 0.019607844, g: 0.24705882, b: 0.36078432, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
@@ -617,7 +623,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0.7529412, b: 1, a: 1}
m_Color: {r: 0.9490196, g: 0.49803922, b: 0.047058824, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1

View File

@@ -33,8 +33,8 @@ RectTransform:
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchorMin: {x: 1, y: 0.5}
m_AnchorMax: {x: 1, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 50, y: 50}
m_Pivot: {x: 0, y: 0}

View File

@@ -0,0 +1,515 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &758255480231791509
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 758255480231791508}
- component: {fileID: 758255480231791506}
- component: {fileID: 758255480231791507}
- component: {fileID: 8445851122681183268}
m_Layer: 0
m_Name: LearnableProgressCard
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &758255480231791508
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255480231791509}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 758255481422566555}
- {fileID: 758255481061489079}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &758255480231791506
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255480231791509}
m_CullTransparentMesh: 1
--- !u!114 &758255480231791507
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255480231791509}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 1
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &8445851122681183268
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255480231791509}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7835fc450a6bbf24d95f0a19491fb8c1, type: 3}
m_Name:
m_EditorClassIdentifier:
progress:
entries: []
index: 0
inUse: 0
name:
progress: 0
course: {fileID: 0}
thumbnail: {fileID: 758255481422566554}
stars:
- {fileID: 758255481653936988}
- {fileID: 758255481997431046}
- {fileID: 758255482122419822}
--- !u!1 &758255481061489032
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 758255481061489079}
- component: {fileID: 758255481061489077}
- component: {fileID: 758255481061489078}
- component: {fileID: 758255481061489076}
m_Layer: 0
m_Name: Status
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &758255481061489079
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481061489032}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 758255481653936989}
- {fileID: 758255481997431047}
- {fileID: 758255482122419823}
m_Father: {fileID: 758255480231791508}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: 0, y: -32}
m_SizeDelta: {x: -20, y: 45}
m_Pivot: {x: 0.5, y: 0}
--- !u!222 &758255481061489077
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481061489032}
m_CullTransparentMesh: 1
--- !u!114 &758255481061489078
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481061489032}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.5803922, g: 0.58431375, b: 0.6, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 1
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &758255481061489076
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481061489032}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Padding:
m_Left: 0
m_Right: 0
m_Top: 0
m_Bottom: 0
m_ChildAlignment: 4
m_Spacing: 0
m_ChildForceExpandWidth: 1
m_ChildForceExpandHeight: 1
m_ChildControlWidth: 1
m_ChildControlHeight: 0
m_ChildScaleWidth: 0
m_ChildScaleHeight: 0
m_ReverseArrangement: 0
--- !u!1 &758255481422566556
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 758255481422566555}
- component: {fileID: 758255481422566553}
- component: {fileID: 758255481422566554}
m_Layer: 0
m_Name: Image
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &758255481422566555
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481422566556}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 758255480231791508}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &758255481422566553
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481422566556}
m_CullTransparentMesh: 1
--- !u!114 &758255481422566554
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481422566556}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 5e3c345e006acf74791d272061159b89, type: 3}
m_Type: 0
m_PreserveAspect: 1
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &758255481653936990
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 758255481653936989}
- component: {fileID: 758255481653936987}
- component: {fileID: 758255481653936988}
m_Layer: 0
m_Name: Image
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &758255481653936989
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481653936990}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 758255481061489079}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 60}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &758255481653936987
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481653936990}
m_CullTransparentMesh: 1
--- !u!114 &758255481653936988
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481653936990}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -2086278073, guid: 4d6d852f751f20046ae733db5bdb1af1, type: 3}
m_Type: 0
m_PreserveAspect: 1
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &758255481997431064
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 758255481997431047}
- component: {fileID: 758255481997431045}
- component: {fileID: 758255481997431046}
m_Layer: 0
m_Name: Image (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &758255481997431047
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481997431064}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 758255481061489079}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 60}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &758255481997431045
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481997431064}
m_CullTransparentMesh: 1
--- !u!114 &758255481997431046
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255481997431064}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -2086278073, guid: 4d6d852f751f20046ae733db5bdb1af1, type: 3}
m_Type: 0
m_PreserveAspect: 1
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &758255482122419808
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 758255482122419823}
- component: {fileID: 758255482122419821}
- component: {fileID: 758255482122419822}
m_Layer: 0
m_Name: Image (2)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &758255482122419823
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255482122419808}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 758255481061489079}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 60}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &758255482122419821
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255482122419808}
m_CullTransparentMesh: 1
--- !u!114 &758255482122419822
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 758255482122419808}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -2086278073, guid: 4d6d852f751f20046ae733db5bdb1af1, type: 3}
m_Type: 0
m_PreserveAspect: 1
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b7a9a8ccb77a37740bfdf5579cb179ca
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -68,15 +68,15 @@ MonoBehaviour:
m_Calls: []
m_text: 'Topscore: 123456789'
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontAsset: {fileID: 11400000, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_sharedMaterial: {fileID: -2577534979213189211, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
rgba: 4284235525
m_fontColor: {r: 0.019607844, g: 0.24705882, b: 0.36078432, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
@@ -275,6 +275,9 @@ MonoBehaviour:
button: {fileID: 5555894415693752970}
minigameProgress:
entries: []
minigameIndex: 0
latestScores: []
highestScores: []
minigameList: {fileID: 11400000, guid: 51453f9b41bc72f468ba3e67ab622f8f, type: 2}
thumbnail: {fileID: 5101881939775039227}
title: {fileID: 5101881939882359064}
@@ -423,15 +426,15 @@ MonoBehaviour:
m_Calls: []
m_text: <Minigame title>
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontAsset: {fileID: 11400000, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_sharedMaterial: {fileID: -2577534979213189211, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
rgba: 4284235525
m_fontColor: {r: 0.019607844, g: 0.24705882, b: 0.36078432, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:

View File

@@ -96,7 +96,7 @@ MonoBehaviour:
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Transition: 0
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
@@ -277,15 +277,15 @@ MonoBehaviour:
m_Calls: []
m_text: Gebruiker
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 3602bedf0ebe5b64596873f09eddf57b, type: 2}
m_sharedMaterial: {fileID: -1030930060397404263, guid: 3602bedf0ebe5b64596873f09eddf57b, type: 2}
m_fontAsset: {fileID: 11400000, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_sharedMaterial: {fileID: -2577534979213189211, guid: 1baf2eae62f542f4585aaf3c9c3e229a, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4281479730
m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
rgba: 4284235525
m_fontColor: {r: 0.019607844, g: 0.24705882, b: 0.36078432, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
@@ -481,13 +481,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 172fcacd3bd90f442a6b94f0ff43a76a, type: 3}
m_Name:
m_EditorClassIdentifier:
userList: {fileID: 11400000, guid: 072bec636a40f7e4e93b0ac624a3bda2, type: 2}
user:
username:
avatar: {fileID: 0}
playtime: 0
courses: []
minigames: []
button: {fileID: 7566391564300576381}
avatar: {fileID: 5164936991071620901}
username: {fileID: 7566391564272109414}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 81765ea55baf15d45b01b02187b02429, type: 3}
m_Name: UserAvatarList
m_EditorClassIdentifier:
avatars:
- {fileID: 21300000, guid: 6feaaea4b64633f49b4538bfd4b3755c, type: 3}
- {fileID: 21300000, guid: 399cc241fc79bd74db314f146e7fb6b9, type: 3}
- {fileID: 21300000, guid: 4654657a2ebea444898fa6eeaefcd18d, type: 3}
- {fileID: 21300000, guid: 0cc198f1cde659246a199b30459720b1, type: 3}
- {fileID: 21300000, guid: 7f105a09a3d164547925ee1bdfa595ca, type: 3}
- {fileID: 21300000, guid: 313c22e5834595645989d609fe9d8853, type: 3}
- {fileID: 21300000, guid: e5f3d99cdf5298d4f86d42dd019a6bad, type: 3}
- {fileID: 21300000, guid: b67b99d98d1ceb1489743bcd78b9ab70, type: 3}
- {fileID: 21300000, guid: e4af402f37099cd4195d7d2654519744, type: 3}
- {fileID: 21300000, guid: 73f4e028d9efb644aa23538a749667c5, type: 3}
- {fileID: 21300000, guid: 61e56570163833e448037bc353ada0ac, type: 3}
- {fileID: 21300000, guid: 8e69dfd19237abb4f98eb11e74153109, type: 3}
- {fileID: 21300000, guid: bc11ff9d6cc35de45ab58f39f13d7142, type: 3}
- {fileID: 21300000, guid: f989719a5c45c7a4183b2bc12c1c4905, type: 3}
- {fileID: 21300000, guid: 1392922567cd59d4fb0beceea3f5917a, type: 3}
- {fileID: 21300000, guid: 3c314ae9e10eafb49b57c98c9e779bd8, type: 3}
- {fileID: 21300000, guid: 33509e453b5093e43a148a668a961c56, type: 3}
- {fileID: 21300000, guid: 39ac3b86729423846be1fbd56b8d92d6, type: 3}
- {fileID: 21300000, guid: 1118d67f50622cc4ab02aa23c6dc1fd4, type: 3}
- {fileID: 21300000, guid: e519f0f7b00e6834187963a0131cae52, type: 3}
- {fileID: 21300000, guid: eefb367ff44256d43a14cde2c3924321, type: 3}
- {fileID: 21300000, guid: 83d6525e4efa4954091e08095349f45a, type: 3}
- {fileID: 21300000, guid: db31bca6363270441ab54421f55c1263, type: 3}
- {fileID: 21300000, guid: 9d5771baa6ea6e041b066135d6798e1c, type: 3}
- {fileID: 21300000, guid: 2b01165a5836ab14593d7a5862bd6793, type: 3}
- {fileID: 21300000, guid: 8c304fe460423214ea0bb6ebc235ed2d, type: 3}
- {fileID: 21300000, guid: b203b4e5f8568ff46b2277ce6d61017a, type: 3}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 017ec1af3b6cc4d4ab2b506911a4edad
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,17 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3f3d6d68c3c3db64e91cf5ec9537ccda, type: 3}
m_Name: UserList
m_EditorClassIdentifier:
storedUserList:
currentUserIndex: 0
storedUsers: []

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 072bec636a40f7e4e93b0ac624a3bda2
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -3,6 +3,7 @@
"rootNamespace": "",
"references": [
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:e83ddf9a537a96b4a804a16bb7872ec1",
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25"
],
"includePlatforms": [],

View File

@@ -22,11 +22,6 @@ public class ChangeUserScreen : MonoBehaviour
/// </summary>
public GameObject error;
/// <summary>
/// Reference to the user list
/// </summary>
public UserList userList;
/// <summary>
/// Index of the current selected user in the UserList
/// </summary>
@@ -42,6 +37,7 @@ public class ChangeUserScreen : MonoBehaviour
/// </summary>
void Start()
{
PersistentDataController.GetInstance().Load();
error.SetActive(false);
DisplayUsers();
}
@@ -52,12 +48,10 @@ public class ChangeUserScreen : MonoBehaviour
private void DisplayUsers()
{
foreach (Transform child in usersContainer)
{
Destroy(child.gameObject);
}
List<User> users = userList.GetUsers();
currentUserIndex = userList.GetCurrentUserIndex();
List<User> users = UserList.GetUsers();
currentUserIndex = UserList.IndexOf(UserList.GetCurrentUser().GetUsername());
for (int i = 0; i < users.Count; i++)
{
User user = users[i];
@@ -78,7 +72,7 @@ public class ChangeUserScreen : MonoBehaviour
Image background = instance.GetComponent<Image>();
userBackgrounds.Add(background);
// Set background color
background.color = i == currentUserIndex ? Color.blue : Color.gray;
background.color = i == currentUserIndex ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
}
}
@@ -88,9 +82,9 @@ public class ChangeUserScreen : MonoBehaviour
/// <param name="index">Index to the user in the <c>this.userBackgrounds</c> list</param>
private void UpdateSelection(int index)
{
userBackgrounds[currentUserIndex].color = Color.gray;
userBackgrounds[currentUserIndex].color = new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
currentUserIndex = index;
userBackgrounds[currentUserIndex].color = Color.blue;
userBackgrounds[currentUserIndex].color = new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f);
}
/// <summary>
@@ -98,8 +92,7 @@ public class ChangeUserScreen : MonoBehaviour
/// </summary>
public void IChooseYou()
{
userList.ChangeCurrentUser(currentUserIndex);
userList.Save();
UserList.ChangeCurrentUser(currentUserIndex);
SystemController.GetInstance().BackToPreviousScene();
}

View File

@@ -21,10 +21,10 @@ public class CourseProgressCard : MonoBehaviour
/// <summary>
/// Reference to the progress so we can display a progress bar
/// </summary>
public Progress courseProgress;
public PersistentDataController.SavedCourseProgress courseProgress;
/// <summary>
/// Reference to the list of courses so we can query the correct course
/// Reference to the list of minigameCards so we can query the correct course
/// </summary>
public CourseList courseList;
@@ -48,11 +48,11 @@ public class CourseProgressCard : MonoBehaviour
/// </summary>
void Start()
{
Course course = courseList.GetCourseByIndex(courseProgress.Get<CourseIndex>("courseIndex"));
Course course = courseList.GetCourseByIndex(courseProgress.courseIndex);
thumbnail.sprite = course.thumbnail;
title.text = course.title;
progressBar.value = courseProgress.Get<float>("courseProgress");
progressBar.value = courseProgress.progress;
button.onClick.AddListener(selectActivity);
}
}

View File

@@ -0,0 +1,40 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Class to handle learnable progress card display
/// </summary>
public class LearnableProgressCard : MonoBehaviour
{
/// <summary>
/// Reference to the progress so we can display a progress bar
/// </summary>
public PersistentDataController.SavedLearnableProgress progress;
/// <summary>
/// Reference to the list of minigameCards so we can query the correct course
/// </summary>
public Learnable learnable;
/// <summary>
/// UI reference to the thumbnail of the course
/// </summary>
public Image thumbnail;
/// <summary>
/// UI refeerence to the title of the course
/// </summary>
public List<Image> stars;
/// <summary>
/// Start is called before the first frame update
/// </summary>
void Start()
{
thumbnail.sprite = learnable.image;
var starRewards = new float[] { 1.0f, 2.5f, 4.0f, };
for (int i = 0; i < 3; i++)
stars[i].color = starRewards[i] < progress.progress ? Color.white : Color.black;
}
}

View File

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

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
@@ -22,7 +23,7 @@ public class MinigameProgressCard : MonoBehaviour
/// <summary>
/// Reference to the minigame progress
/// </summary>
public Progress minigameProgress;
public PersistentDataController.SavedMinigameProgress minigameProgress;
/// <summary>
/// Reference to the minigame list
@@ -49,13 +50,15 @@ public class MinigameProgressCard : MonoBehaviour
/// </summary>
void Start()
{
Minigame minigame = minigameList.GetMinigameByIndex(minigameProgress.Get<MinigameIndex>("minigameIndex"));
Minigame minigame = minigameList.GetMinigameByIndex(minigameProgress.minigameIndex);
thumbnail.sprite = minigame.thumbnail;
title.text = minigame.title;
List<Score> highscores = minigameProgress.Get<List<Score>>("highestScores");
int score = highscores.Count > 0 ? highscores[0].scoreValue : 0;
highscore.text = $"Topscore: {score}";
button.onClick.AddListener(selectActivity);
List<Score> highscores = minigameProgress.highestScores;
if (0 < highscores.Count)
highscore.text = $"TOPSCORE: {highscores.Max((s) => s.scoreValue)}";
else
highscore.text = "(NOG) GEEN TOPSCORE";
}
}

View File

@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Class to handle course list progress display
/// </summary>
public class PanelCourseProgress : MonoBehaviour
{
/// <summary>
/// Reference to the current user
/// </summary>
private User user;
/// <summary>
/// Reference to the courses list
/// </summary>
public CourseList courseList;
/// <summary>
/// Prefab of a course card
/// </summary>
public GameObject courseCardPrefab;
/// <summary>
/// UI reference to the container holding all course cards
/// </summary>
public Transform coursesContainer;
/// <summary>
/// UI reference to the course info panel
/// </summary>
public GameObject courseInfo;
/// <summary>
/// UI reference to the message that displays when no course progress is present
/// </summary>
public GameObject emptyCourses;
/// <summary>
/// Reference to the title on the info panel
/// </summary>
public TMP_Text courseTitle;
/// <summary>
/// Reference to the learnable card prefab
/// </summary>
public GameObject learnableCardPrefab;
/// <summary>
/// Reference to the container for holding the learnable cards
/// </summary>
public Transform learnablesContainer;
/// <summary>
/// Reference to the course progress bar on the info panel
/// </summary>
public SlicedSlider progressBar;
/// <summary>
/// Current selected course
/// </summary>
private int selectedCourse = 0;
/// <summary>
/// List of course backgrounds and indices
/// </summary>
private List<Tuple<Image, CourseIndex>> courseCards = new List<Tuple<Image, CourseIndex>>();
/// <summary>
/// Start is called before the first frame update
/// </summary>
void Start()
{
PersistentDataController.GetInstance().Load();
user = UserList.GetCurrentUser();
var courses = user.GetCourses();
courseInfo.SetActive(0 < courses.Count);
emptyCourses.SetActive(courses.Count <= 0);
int i = 0;
foreach (var courseProgress in courses)
{
// Create instance of prefab
GameObject instance = GameObject.Instantiate(courseCardPrefab, coursesContainer);
int j = i++;
// Initialize card
CourseProgressCard cpc = instance.GetComponent<CourseProgressCard>();
cpc.courseProgress = courseProgress;
cpc.selectActivity = () => UpdateSelection(j);
// Store reference to background so we can apply fancy coloring
Image background = instance.GetComponent<Image>();
background.color = new Color(159 / 255f, 231 / 255f, 245 / 255f, 120/255f);
this.courseCards.Add(Tuple.Create(background, courseProgress.courseIndex));
}
if (0 < courses.Count)
UpdateSelection(0);
}
/// <summary>
/// Update the current selected course
/// </summary>
/// <param name="newCourse">Index to the new course</param>
private void UpdateSelection(int newCourse)
{
courseCards[selectedCourse].Item1.color = new Color(159 / 255f, 231 / 255f, 245 / 255f, 120 / 255f);
selectedCourse = newCourse;
courseCards[selectedCourse].Item1.color = new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f);
var progress = user.GetCourseProgress(courseCards[selectedCourse].Item2);
var course = courseList.GetCourseByIndex(progress.courseIndex);
courseTitle.text = course.title;
progressBar.fillAmount = progress.progress;
foreach (Transform child in learnablesContainer)
Destroy(child.gameObject);
for (int i = 0; i < course.theme.learnables.Count; i++)
{
GameObject instance = GameObject.Instantiate(learnableCardPrefab, learnablesContainer);
var script = instance.GetComponent<LearnableProgressCard>();
script.learnable = course.theme.learnables[i];
script.progress = progress.learnables[i];
}
}
}

View File

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

View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Class to handle minigame list progress display
/// </summary>
public class PanelMinigameProgress : MonoBehaviour
{
/// <summary>
/// Reference to the current user
/// </summary>
private User user;
/// <summary>
/// Reference to the minigames list
/// </summary>
public MinigameList minigameList;
/// <summary>
/// Prefab of a minigame card
/// </summary>
public GameObject minigameCardPrefab;
/// <summary>
/// UI reference to the container holding all the minigame cards
/// </summary>
public Transform minigamesContainer;
/// <summary>
/// UI reference to the minigame info panel
/// </summary>
public GameObject minigameInfo;
/// <summary>
/// UI reference to the message that displays when no minigame progress is present
/// </summary>
public GameObject emptyMinigames;
/// <summary>
/// UI reference to the plot
/// </summary>
public ProgressGraph progressGraph;
/// <summary>
/// Reference to the title of the minigame of the info panel
/// </summary>
public TMP_Text minigameTitle;
/// <summary>
/// Reference to the text that will display when an empty minigame progress object is selected
/// </summary>
public GameObject emptyHighscore;
/// <summary>
/// Current selected course
/// </summary>
private int selectedMinigame = 0;
/// <summary>
/// List of course backgrounds and indices
/// </summary>
private List<Tuple<Image, MinigameIndex>> minigameCards = new List<Tuple<Image, MinigameIndex>>();
/// <summary>
/// Start is called before the first frame update
/// </summary>
void Start()
{
PersistentDataController.GetInstance().Load();
user = UserList.GetCurrentUser();
var minigames = user.GetMinigames();
minigameInfo.SetActive(minigames.Count > 0);
emptyMinigames.SetActive(minigames.Count <= 0);
int i = 0;
foreach (var minigameProgress in minigames)
{
// Create instance of prefab
GameObject instance = GameObject.Instantiate(minigameCardPrefab, minigamesContainer);
int j = i++;
// Initialize card
MinigameProgressCard mpc = instance.GetComponent<MinigameProgressCard>();
mpc.minigameProgress = minigameProgress;
mpc.selectActivity = () => UpdateSelection(j);
// Store reference to background so we can apply fancy coloring
Image background = instance.GetComponent<Image>();
background.color = new Color(159 / 255f, 231 / 255f, 245 / 255f, 120 / 255f);
minigameCards.Add(Tuple.Create(background, minigameProgress.minigameIndex));
}
if (0 < minigames.Count)
UpdateSelection(0);
}
/// <summary>
/// Update the current selected course
/// </summary>
/// <param name="newMinigame">Index to the new course</param>
private void UpdateSelection(int newMinigame)
{
minigameCards[selectedMinigame].Item1.color = new Color(159 / 255f, 231 / 255f, 245 / 255f, 120 / 255f);
selectedMinigame = newMinigame;
minigameCards[selectedMinigame].Item1.color = new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f);
var progress = user.GetMinigameProgress(minigameCards[selectedMinigame].Item2);
minigameTitle.text = minigameList.GetMinigameByIndex(progress.minigameIndex).title;
List<Score> latestScores = progress.latestScores;
List<Score> highestScores = progress.highestScores;
if (0 < highestScores.Count)
{
emptyHighscore.SetActive(false);
progressGraph.gameObject.SetActive(true);
progressGraph.Plot(latestScores.ConvertAll<double>((s) => (double)s.scoreValue), highestScores.Max((s) => s.scoreValue));
}
else
{
emptyHighscore.SetActive(true);
progressGraph.gameObject.SetActive(false);
}
}
}

View File

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

View File

@@ -1,104 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
/// <summary>
/// A class for holding all progress belonging to a user
/// </summary>
[Serializable]
public class Progress
{
/// <summary>
/// A helper class for handling the stored progress
/// </summary>
[Serializable]
protected class DataEntry
{
/// <summary>
/// The key, used to reference the data object
/// </summary>
public string key;
/// <summary>
/// The object, representated as a list of byte (which can be serialized)
/// </summary>
public List<byte> bytes = new List<byte>();
public DataEntry(string key, byte[] data)
{
this.key = key;
this.bytes = new List<byte>(data);
}
}
/// <summary>
/// Entries in the <c>Progress</c> object
/// </summary>
[SerializeField]
private List<DataEntry> entries = new List<DataEntry>();
/// <summary>
/// Update the value of a certain key,
/// or add a new value if the key was not present
/// </summary>
/// <typeparam name="T">The type of the data to be added/updated</typeparam>
/// <param name="key">The key, used for referencing the data</param>
/// <param name="data">The object of type <typeparamref name="T"/></param>
/// <returns><c>true</c> if successful, <c>false</c> otherwise</returns>
public bool AddOrUpdate<T>(string key, T data)
{
if (data == null)
return false;
DataEntry entry = entries.Find(x => x.key == key);
// Hacky serialization stuff
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, data);
if (entry != null)
{
entry.bytes.Clear();
entry.bytes.AddRange(ms.ToArray());
}
else
{
entries.Add(new DataEntry(key, ms.ToArray()));
}
return true;
}
}
/// <summary>
/// Get the data object of a certain key
/// </summary>
/// <typeparam name="T">The type of the data object</typeparam>
/// <param name="key">The key referencing the data object</param>
/// <returns>The data, cast to a type <typeparamref name="T"/></returns>
/// <exception cref="KeyNotFoundException"></exception>
public T Get<T>(string key)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
// Find the correct key
foreach (DataEntry entry in entries)
{
if (entry.key == key)
{
// Hacky serialization stuff
byte[] data = entry.bytes.ToArray();
ms.Write(data, 0, data.Length);
ms.Seek(0, SeekOrigin.Begin);
return (T)bf.Deserialize(ms);
}
}
}
// Raise an exception when key is not found
throw new KeyNotFoundException();
}
}

View File

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

View File

@@ -0,0 +1,263 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Class to handle and draw a nice line graph to a Texture2D
/// </summary>
public class ProgressGraph : MonoBehaviour
{
/// <summary>
/// UI reference to the plot
/// </summary>
public RawImage progressGraph;
/// <summary>
/// Prefab of the highscore marker to display on the graph
/// </summary>
public GameObject highscoreMarker;
/// <summary>
/// Prefab of the axes tick marker to display on the graph
/// </summary>
public GameObject axesTickMarker;
/// <summary>
/// Color of the graph line
/// </summary>
public Color lineColor;
/// <summary>
/// Bckground color
/// </summary>
public Color backgroundColor;
/// <summary>
/// Color of the text and axes grid
/// </summary>
public Color textColor;
/// <summary>
/// Color of the highscore line
/// </summary>
public Color highscoreColor;
/// <summary>
/// Left and right padding of the graph
/// </summary>
private const int GRAPH_PADDING_X_PX = 50;
/// <summary>
/// Top and bottom padding of the graph
/// </summary>
private const int GRAPH_PADDING_Y_PX = 50;
/// <summary>
/// Radius of the point on the graph
/// </summary>
private const int GRAPH_POINT_RADIUS = 10;
/// <summary>
/// Size of the line on the graph
/// </summary>
private const int GRAPH_LINE_SIZE = 4;
/// <summary>
/// Plot points and a highscore on the graph
/// </summary>
/// <param name="scores">List of score values to plot</param>
/// <param name="highscore">Highscore value (this will be plotted in a fancy color)</param>
public void Plot(List<double> scores, double highscore)
{
// Remove previous marker(s)
foreach (Transform child in progressGraph.gameObject.transform)
Destroy(child.gameObject);
// Get texture reference
Texture2D tex = progressGraph.texture as Texture2D;
RectTransform rect = progressGraph.gameObject.transform as RectTransform;
if (tex == null)
{
tex = new Texture2D(
width: (int)rect.sizeDelta.x,
height: (int)rect.sizeDelta.y,
textureFormat: TextureFormat.ARGB32,
mipCount: 3,
linear: true
);
}
tex.filterMode = FilterMode.Point;
FillTexture(tex, backgroundColor);
// calculate positions and offsets
int x0 = GRAPH_PADDING_X_PX, x1 = tex.width - GRAPH_PADDING_X_PX;
int y0 = GRAPH_PADDING_Y_PX, y1 = tex.height - GRAPH_PADDING_Y_PX;
double min = scores.Min();
double max = scores.Max();
List<Tuple<int, int>> points = new List<Tuple<int, int>>();
for (int i = 0; i < scores.Count; i++)
{
int _x = x0 + (scores.Count > 1 ? i * ((x1 - x0) / (scores.Count - 1)) : (x1 - x0) / 2);
int _y = y0 + (int)((y1 - y0) * (min != max ? (scores[i] - min) / (max - min) : 0.5));
points.Add(Tuple.Create(_x, _y));
}
// Calculate scaling
const int NUMBER_OF_AXES = 5;
double spacing = 0.0;
if (min == max)
{
spacing = CalculateSpacing(highscore, NUMBER_OF_AXES);
max = highscore + 2 * spacing;
min = highscore - 2 * spacing;
}
spacing = CalculateSpacing(max - min, NUMBER_OF_AXES);
double begin = spacing * Math.Round(min / spacing);
// Draw axes
double pixels_per_unit = (y1 - y0) / (max - min);
double Y = begin;
int y = y0 + (int)(pixels_per_unit * (Y - min));
int total = 0;
do
{
if (y0 <= y)
{
DrawLine(tex, x0, y, x1, y, 2, textColor);
GameObject tick = GameObject.Instantiate(axesTickMarker, rect);
tick.GetComponent<RectTransform>().localPosition = new Vector3(-10 - rect.sizeDelta.y * rect.pivot.x, y - 25 - rect.sizeDelta.y * rect.pivot.y, 0);
TMP_Text txt = tick.GetComponent<TMP_Text>();
txt.text = $"{Y}";
txt.color = textColor;
}
total += 1;
Y += spacing;
y = y0 + (int)(pixels_per_unit * (Y - min));
// Fail save
if (2 * NUMBER_OF_AXES < total)
break;
} while (y <= y1);
// Draw highscore
if (min <= highscore && highscore <= max)
{
y = y0 + (int)(pixels_per_unit * (highscore - min));
DrawLine(tex, x0, y, x1, y, GRAPH_LINE_SIZE, highscoreColor);
GameObject marker = GameObject.Instantiate(highscoreMarker, rect);
marker.GetComponent<RectTransform>().localPosition = new Vector3(tex.width - 50 - rect.sizeDelta.x * rect.pivot.x, y - 25 - rect.sizeDelta.y * rect.pivot.y, 0);
}
// Draw points
for (int i = 0; i < points.Count; i++)
{
Tuple<int, int> p = points[i];
if (0 < i)
{
Tuple<int, int> q = points[i - 1];
DrawLine(tex, p.Item1, p.Item2, q.Item1, q.Item2, GRAPH_LINE_SIZE, lineColor);
}
DrawPoint(tex, p.Item1, p.Item2, GRAPH_POINT_RADIUS, lineColor);
}
// Apply to graph GameObject
tex.Apply();
progressGraph.texture = tex;
}
/// <summary>
/// Calculate nice spacing
/// </summary>
/// <param name="mu">Either `max - min` if max != min, otherwise `highscore`</param>
/// <param name="numberOfAxes">Number of horizontal axes grid lines shown on the graph</param>
/// <returns>Spacing between each axes grid line</returns>
private double CalculateSpacing(double mu, int numberOfAxes)
{
if (mu == 0)
return 1.0;
double[] otherSpacings = { 0.5, 1.0, 2.0, 5.0 };
int mag = (int)Math.Floor(Math.Log10(Math.Abs(mu)));
int MAG = (int)Math.Pow(10, mag);
double spacing = MAG;
foreach (double o in otherSpacings)
{
if (Math.Abs(mu - numberOfAxes * spacing) <= Math.Abs(mu - numberOfAxes * o * MAG))
continue;
spacing = o * MAG;
}
return spacing;
}
/// <summary>
/// Set all the pixels of a texture to a given color
/// </summary>
/// <param name="tex">Texture to fill</param>
/// <param name="color">Color to set the texture to</param>
private void FillTexture(Texture2D tex, Color color)
{
for (int y = 0; y < tex.height; y++)
for (int x = 0; x < tex.width; x++)
tex.SetPixel(x, y, color);
}
/// <summary>
/// Draw a point to a texture
/// </summary>
/// <param name="tex">Texture2D to plot point on</param>
/// <param name="xc">Center x-pos</param>
/// <param name="yc">Center y-pos</param>
/// <param name="r">Radius (aka width and height)</param>
/// <param name="color">Color of the point</param>
private void DrawPoint(Texture2D tex, int xc, int yc, int r, Color color)
{
for (int y = yc - r; y < yc + r; y++)
for (int x = xc - r; x < xc + r; x++)
tex.SetPixel(x, y, color);
}
/// <summary>
/// Draw a line to a texture
/// </summary>
/// <param name="tex">Texture2D to plot line on</param>
/// <param name="x0">Starting x-pos</param>
/// <param name="y0">Strating y-pos</param>
/// <param name="x1">Ending x-pos</param>
/// <param name="y1">Ending y-pos</param>
/// <param name="size">Size of the line (width)</param>
/// <param name="color">Color of the line</param>
private void DrawLine(Texture2D tex, int x0, int y0, int x1, int y1, int size, Color color)
{
int w = x1 - x0;
int h = y1 - y0;
int length = Mathf.Abs(x1 - x0);
if (Mathf.Abs(y1 - y0) > length)
length = Mathf.Abs(h);
double dx = w / (double)length;
double dy = h / (double)length;
double x = x0;
double y = y0;
double r = size / 2;
for (int i = 0; i <= length; i++)
{
for (int j = (int)(y - r); j < y + r; j++)
for (int k = (int)(x - r); k < x + r; k++)
tex.SetPixel(k, j, color);
x += dx;
y += dy;
}
}
}

View File

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

View File

@@ -1,104 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// SystemController singleton
/// </summary>
public class SystemController
{
/// <summary>
/// The instance controlling the singleton
/// </summary>
private static SystemController instance = null;
/// <summary>
/// Stack of the loaded scenes, used to easily go back to previous scenes
/// </summary>
private Stack<int> sceneStack = new Stack<int>();
/// <summary>
/// Get the instance loaded by the singleton
/// </summary>
/// <returns>SystemController instance</returns>
public static SystemController GetInstance()
{
// Create a new instance if non exists
if (instance == null)
{
instance = new SystemController();
instance.sceneStack.Push(SceneManager.GetActiveScene().buildIndex);
}
return instance;
}
/// <summary>
/// Load the scene and push on the stack
/// </summary>
/// <param name="scenePath">Path of the scene</param>
public void LoadNextScene(string scenePath)
{
LoadNextScene(SceneUtility.GetBuildIndexByScenePath(scenePath));
}
/// <summary>
/// Load the scene and push on the stack
/// </summary>
/// <param name="sceneIndex">Buildindex of the scene</param>
public void LoadNextScene(int sceneIndex)
{
sceneStack.Push(sceneIndex);
SceneManager.LoadScene(sceneIndex);
}
/// <summary>
/// Swap the current scene with the new scene on the stack
/// </summary>
/// <param name="scenePath">Path of the scene</param>
public void SwapScene(string scenePath)
{
SwapScene(SceneUtility.GetBuildIndexByScenePath(scenePath));
}
/// <summary>
/// Swap the current scene with the new scene on the stack
/// </summary>
/// <param name="sceneIndex">Buildindex of the scene</param>
public void SwapScene(int sceneIndex)
{
sceneStack.Pop();
LoadNextScene(sceneIndex);
}
/// <summary>
/// Go back to the previous scene and unload the current scene
/// </summary>
public void BackToPreviousScene()
{
sceneStack.Pop();
if (sceneStack.Count > 0) SceneManager.LoadScene(sceneStack.Peek());
else Application.Quit();
}
/// <summary>
/// Go back to a specific scene, unloading all the scenes on the way
/// </summary>
/// <param name="scenePath">Path of the scene</param>
public void BackToScene(string scenePath)
{
BackToScene(SceneUtility.GetBuildIndexByScenePath(scenePath));
}
/// <summary>
/// Go back to a specific scene, unloading all the scene on the way
/// </summary>
/// <param name="sceneIndex">Buildindex of the scene</param>
public void BackToScene(int sceneIndex)
{
while (0 < sceneStack.Count && sceneStack.Peek() != sceneIndex) sceneStack.Pop();
if (sceneStack.Count > 0) SceneManager.LoadScene(sceneStack.Peek());
else Application.Quit();
}
}

View File

@@ -2,92 +2,129 @@ using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using static PersistentDataController;
/// <summary>
/// A class holding all information of a user
/// </summary>
[Serializable]
public class User
{
/// <summary>
/// User nickname
/// Reference to the user stored data record
/// </summary>
public string username;
private SavedUserData storedUserData;
/// <summary>
/// The avatar of the user
/// Constructor
/// </summary>
public Sprite avatar;
/// <param name="data">Reference to the user stored data record</param>
public User(SavedUserData data)
{
this.storedUserData = data;
}
/// <summary>
/// The total playtime of the user
/// Get the username
/// </summary>
/// <remarks>TODO: needs to be implemented</remarks>
public double playtime;
public string GetUsername() { return storedUserData.username; }
/// <summary>
/// List of courses a user started/completed
/// Get the total playtime
/// </summary>
[SerializeField]
public List<Progress> courses = new List<Progress>();
public double GetPlaytime() { return storedUserData.playtime; }
/// <summary>
/// List of minigames a user played
/// Get the avatar
/// </summary>
[SerializeField]
public List<Progress> minigames = new List<Progress>();
public Sprite GetAvatar() { return UserList.AVATARS[storedUserData.avatarIndex]; }
/// <summary>
/// Get a list of all recently started courses
/// Get a list of all recently started minigameCards
/// </summary>
/// <returns>A <c>List</c> of <c>Tuples</c>, containing the <c>CourseIndex</c>
/// and a <c>float</c> holding the progress (value between 0 and 1) of the user in this course</returns>
public List<Tuple<CourseIndex, float>> GetRecentCourses()
{
// TODO: return better results (for now only return all courses)
// TODO: return better results (for now only return all minigameCards)
List<Tuple<CourseIndex, float>> recentCourses = new List<Tuple<CourseIndex, float>>();
foreach (Progress courseProgress in courses)
foreach (var courseProgress in storedUserData.courses)
{
CourseIndex idx = courseProgress.Get<CourseIndex>("courseIndex");
float progress = courseProgress.Get<float>("courseProgress");
CourseIndex idx = courseProgress.courseIndex;
float progress = courseProgress.progress;
recentCourses.Add(Tuple.Create<CourseIndex, float>(idx, progress));
}
return recentCourses.Take(3).ToList();
}
/// <summary>
/// Get a list of all recommended courses
/// Get a list of all recommended minigameCards
/// </summary>
/// <returns>A <c>List</c> of <c>Tuples</c>, containing the <c>CourseIndex</c>
/// and a <c>float</c> holding the progress (value between 0 and 1) of the user in this course</returns>
public List<Tuple<CourseIndex, float>> GetRecommendedCourses()
{
List<Tuple<CourseIndex, float>> recommenedCourses = new List<Tuple<CourseIndex, float>>();
if (courses.Count == 0)
if (storedUserData.courses.Count == 0)
{
recommenedCourses.Add(Tuple.Create<CourseIndex, float>(CourseIndex.FINGERSPELLING, 0.0f));
}
else
{
// TODO: return better results (for now only return all courses)
foreach (Progress courseProgress in courses)
// TODO: return better results (for now only return all minigameCards)
foreach (var courseProgress in storedUserData.courses)
{
CourseIndex idx = courseProgress.Get<CourseIndex>("courseIndex");
float progress = courseProgress.Get<float>("courseProgress");
CourseIndex idx = courseProgress.courseIndex;
float progress = courseProgress.progress;
recommenedCourses.Add(Tuple.Create<CourseIndex, float>(idx, progress));
}
}
return recommenedCourses.Take(3).ToList();
}
/// <summary>
/// Get the progress of all minigameCards the user did
/// </summary>
/// <returns></returns>
public List<SavedCourseProgress> GetCourses()
{
return storedUserData.courses;
}
/// <summary>
/// Get the progress of a certain course
/// </summary>
/// <param name="courseIndex">Index of course</param>
/// <returns><c>Progress</c> belonging to the <c>courseIndex</c>, <c>null</c> if course was not found</returns>
public Progress GetCourseProgress(CourseIndex courseIndex)
public SavedCourseProgress GetCourseProgress(CourseIndex courseIndex)
{
return courses.Find((p) => p.Get<CourseIndex>("courseIndex") == courseIndex);
return storedUserData.courses.Find((p) => p.courseIndex == courseIndex);
}
/// <summary>
/// Add new progress of a course
/// </summary>
/// <param name="progress">SavedCourseProgress data record</param>
public void AddCourseProgress(SavedCourseProgress progress)
{
storedUserData.courses.Add(progress);
}
/// <summary>
/// Reset progress of a course
/// </summary>
/// <param name="courseIndex">Index of course</param>
public void ResetCourseProgress(CourseIndex courseIndex)
{
storedUserData.courses.RemoveAll((p) => p.courseIndex == courseIndex);
}
/// <summary>
/// Get the progress of all minigames the user did
/// </summary>
/// <returns></returns>
public List<SavedMinigameProgress> GetMinigames()
{
return storedUserData.minigames;
}
/// <summary>
@@ -95,8 +132,26 @@ public class User
/// </summary>
/// <param name="minigameIndex">Index of the minigame</param>
/// <returns><c>Progress</c> belonging to the <c>minigameIndex</c>, <c>null</c> if minigame was not found</returns>
public Progress GetMinigameProgress(MinigameIndex minigameIndex)
public SavedMinigameProgress GetMinigameProgress(MinigameIndex minigameIndex)
{
return minigames.Find((p) => p.Get<MinigameIndex>("minigameIndex") == minigameIndex);
return storedUserData.minigames.Find((p) => p.minigameIndex == minigameIndex);
}
/// <summary>
/// Add new progress of a minigame
/// </summary>
/// <param name="progress">SavedMinigameProgress data record</param>
public void AddMinigameProgress(SavedMinigameProgress progress)
{
storedUserData.minigames.Add(progress);
}
/// <summary>
/// Reset progress of a minigame
/// </summary>
/// <param name="minigameIndex">Index of the minigame</param>
public void ResetMinigameProgress(MinigameIndex minigameIndex)
{
storedUserData.minigames.RemoveAll((p) => p.minigameIndex == minigameIndex);
}
}

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Container to hold a reference to all possible user avatar sprites
/// </summary>
[CreateAssetMenu(menuName = "Create new Scriptable/User Avatar List")]
public class UserAvatarList : ScriptableObject
{
/// <summary>
/// List of avatar sprites
/// </summary>
public List<Sprite> avatars = new List<Sprite>();
/// <summary>
/// Awake is called when the object gets created
/// </summary>
public void Awake()
{
UserList.AVATARS = avatars;
}
}

View File

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

View File

@@ -8,11 +8,6 @@ using UnityEngine.UI;
/// </summary>
public class UserCard : MonoBehaviour
{
/// <summary>
/// Reference to the userlist
/// </summary>
public UserList userList;
/// <summary>
/// User to upload info into this card
/// </summary>
@@ -53,8 +48,8 @@ public class UserCard : MonoBehaviour
/// </summary>
void Start()
{
avatar.sprite = user.avatar;
username.text = user.username;
avatar.sprite = user.GetAvatar();
username.text = user.GetUsername();
button.onClick.AddListener(selectUser);
}
@@ -63,10 +58,9 @@ public class UserCard : MonoBehaviour
/// </summary>
public void DeleteUser()
{
if (userList.DeleteUser(user))
if (UserList.DeleteUser(user.GetUsername()))
{
// User is removed, update and save
userList.Save();
updateUserCardContainer();
}
else

View File

@@ -34,20 +34,9 @@ public class UserCreationScreen : MonoBehaviour
/// </summary>
public GameObject avatarPrefab;
/// <summary>
/// List of all sprites that are supported as avatars
/// </summary>
public List<Sprite> sprites = new List<Sprite>();
/// <summary>
/// Reference to the UserList ScriptableObject
/// </summary>
public UserList users;
/// <summary>
/// Current selected avatar
/// </summary>
[SerializeField]
private int selectedAvatar = 0;
/// <summary>
@@ -55,6 +44,15 @@ public class UserCreationScreen : MonoBehaviour
/// </summary>
private List<Image> avatars = new List<Image>();
/// <summary>
/// Reference to the back button, so we can deactivate it when the user cannot go back (when no user is found at startup)
/// </summary>
public GameObject backButton;
/// <summary>
/// Boolean used to check whether the user can go back to the previous scene
/// </summary>
public static bool canGoBack = true;
/// <summary>
/// Start is called before the first frame update
@@ -62,8 +60,12 @@ public class UserCreationScreen : MonoBehaviour
void Start()
{
errorMessage.SetActive(false);
backButton.SetActive(canGoBack);
for (int i = 0; i < sprites.Count; i++)
// Reset to default value
UserCreationScreen.canGoBack = true;
for (int i = 0; i < UserList.AVATARS.Count; i++)
{
// Create instance of prefab
GameObject instance = GameObject.Instantiate(avatarPrefab, avatarsContainer);
@@ -77,9 +79,9 @@ public class UserCreationScreen : MonoBehaviour
Image background = instance.GetComponent<Image>();
avatars.Add(background);
// Set background color
background.color = selectedAvatar == i ? Color.blue : Color.gray;
background.color = selectedAvatar == i ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : Color.gray;
// Find correct component for setting the sprite
instance.transform.Find("Image").GetComponent<Image>().sprite = sprites[i];
instance.transform.Find("Image").GetComponent<Image>().sprite = UserList.AVATARS[i];
}
}
@@ -91,7 +93,7 @@ public class UserCreationScreen : MonoBehaviour
{
avatars[selectedAvatar].color = Color.gray;
selectedAvatar = newAvatar;
avatars[selectedAvatar].color = Color.blue;
avatars[selectedAvatar].color = new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f);
}
/// <summary>
@@ -113,10 +115,10 @@ public class UserCreationScreen : MonoBehaviour
string username = inputName.text;
if (IsValidUsername(username))
{
if (users.GetUserByUsername(username) == null)
if (UserList.GetUserByUsername(username) == null)
{
// Create a new entry in the UserList ScriptableObject
users.ChangeCurrentUser(users.CreateAndAddNewUser(username, sprites[selectedAvatar]));
UserList.AddUser(username, UserList.AVATARS[selectedAvatar]);
SystemController.GetInstance().BackToPreviousScene();
}
// Warn user that username already exists

View File

@@ -1,78 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
/// <summary>
/// Keep track of all users
/// </summary>
[CreateAssetMenu(menuName = "Create new Scriptable/UserList")]
public class UserList : ScriptableObject
public static class UserList
{
/// <summary>
/// Helper class to enable serialization of the UserList class
/// (<c>ScriptableObject</c>s cannot be serialized)
/// List of possible avatar sprites
/// </summary>
[Serializable]
public class StoredUserList
{
/// <summary>
/// The index of the current/last logged in user in the <c>storedUsers</c> list
/// </summary>
public int currentUserIndex;
/// <summary>
/// A list containing all users (which can be serialized)
/// </summary>
public List<User> storedUsers = new List<User>();
}
/// <summary>
/// Reference to the serializable version of <c>UserList</c>
/// </summary>
[SerializeField]
private StoredUserList storedUserList = new StoredUserList();
/// <summary>
/// Path of the <c>.json</c>-file to store all serialized data
/// </summary>
public static string PATH = null;
/// <summary>
/// OnEnable will make sure the <c>PATH</c>-variable is correctly initialized
/// </summary>
void OnEnable()
{
PATH = $"{Application.persistentDataPath}/users.json";
Load();
}
/// <summary>
/// Create a new user
/// </summary>
/// <param name="name">The username of the new user</param>
/// <param name="avatar">Reference to the user avatar</param>
/// <returns>A newly created user</returns>
public User CreateNewUser(string name, Sprite avatar)
{
User user = new User();
user.username = name;
user.avatar = avatar;
return user;
}
public static List<Sprite> AVATARS = new List<Sprite>();
/// <summary>
/// Create a new user and save (add to list)
/// </summary>
/// <param name="name">The username of the new user</param>
/// <param name="username">The username of the new user</param>
/// <param name="avatar">Reference to the user avatar</param>
/// <returns>A newly created user</returns>
public User CreateAndAddNewUser(string name, Sprite avatar)
public static User AddUser(string username, Sprite avatar)
{
User user = CreateNewUser(name, avatar);
storedUserList.storedUsers.Add(user);
Save();
return user;
PersistentDataController pdc = PersistentDataController.GetInstance();
PersistentDataController.SavedUserData data = new PersistentDataController.SavedUserData();
data.username = username;
data.playtime = 0.0;
data.avatarIndex = AVATARS.IndexOf(avatar);
pdc.AddUser(data);
return new User(data);
}
/// <summary>
@@ -80,110 +35,127 @@ public class UserList : ScriptableObject
/// </summary>
/// <param name="username">The username of the user</param>
/// <returns><c>User</c>-object if a user with such username was found, <c>null</c> otherwise</returns>
public User GetUserByUsername(string username)
public static User GetUserByUsername(string username)
{
foreach (User user in storedUserList.storedUsers)
if (user.username == username) return user;
foreach (User user in GetUsers())
if (user.GetUsername() == username) return user;
return null;
}
/// <summary>
/// Get a user by index
/// </summary>
/// <param name="index">The index of the user</param>
/// <returns>User object</returns>
/// <exception cref="IndexOutOfRangeException"></exception>
public static User GetUserByIndex(int index)
{
List<User> users = GetUsers();
if (index < 0 || users.Count <= index)
throw new IndexOutOfRangeException();
return users[index];
}
/// <summary>
/// Get a list of all users currently stored
/// </summary>
/// <returns>A list of all users</returns>
public List<User> GetUsers()
public static List<User> GetUsers()
{
return storedUserList.storedUsers;
PersistentDataController pdc = PersistentDataController.GetInstance();
return pdc.GetUsers().ConvertAll((d) => new User(d));
}
/// <summary>
/// Get the current logged in user
/// </summary>
/// <returns>The current logged in user</returns>
public User GetCurrentUser()
public static User GetCurrentUser()
{
return storedUserList.storedUsers[storedUserList.currentUserIndex];
List<User> users = GetUsers();
if (users.Count == 0)
return null;
return users[PersistentDataController.GetInstance().GetCurrentUser()];
}
/// <summary>
/// Get the index in the userlist of the current playing user
/// Get the index in the userlist of a user
/// </summary>
/// <returns></returns>
public int GetCurrentUserIndex()
public static int IndexOf(string username)
{
return storedUserList.currentUserIndex;
int idx = GetUsers().FindIndex((e) => e.GetUsername() == username);
if (idx < 0)
throw new KeyNotFoundException();
return idx;
}
/// <summary>
/// Change the current user
/// </summary>
/// <param name="index">Index of the user in the userlist</param>
public void ChangeCurrentUser(int index)
/// <exception cref="IndexOutOfRangeException"></exception>
public static void ChangeCurrentUser(int index)
{
storedUserList.currentUserIndex = index;
if (index < 0 || GetUsers().Count <= index)
throw new IndexOutOfRangeException();
PersistentDataController.GetInstance().SetCurrentUser(index, true);
}
/// <summary>
/// Change the current user
/// </summary>
/// <param name="user">Reference to the user in the userlist</param>
public void ChangeCurrentUser(User user)
/// <param name="index">Username of the user</param>
/// <exception cref="KeyNotFoundException"></exception>
public static void ChangeCurrentUser(string username)
{
storedUserList.currentUserIndex = storedUserList.storedUsers.IndexOf(user);
}
/// <summary>
/// Remove the user
/// </summary>
/// <param name="index">The index of the user in the userlist</param>
/// <returns>true if user was successful removed, false otherwise</returns>
public bool DeleteUser(int index)
{
return DeleteUser(storedUserList.storedUsers[index]);
int index = GetUsers().FindIndex((e) => e.GetUsername() == username);
try { ChangeCurrentUser(index); }
catch (IndexOutOfRangeException) { throw new KeyNotFoundException(); }
}
/// <summary>
/// I am inevitable, *snap*
/// </summary>
/// <param name="user">Reference to the user to be removed</param>
/// <param name="index">The index of the user in the userlist</param>
/// <returns>true if the user was successful removed, false otherwise</returns>
public bool DeleteUser(User user)
/// <exception cref="IndexOutOfRangeException"></exception>
public static bool DeleteUser(int index)
{
if (1 < storedUserList.storedUsers.Count)
{
if (storedUserList.currentUserIndex == storedUserList.storedUsers.Count - 1)
{
storedUserList.currentUserIndex--;
}
List<User> users = GetUsers();
if (index < 0 || users.Count <= index)
throw new IndexOutOfRangeException();
return storedUserList.storedUsers.Remove(user);
if (1 < users.Count)
{
PersistentDataController.GetInstance().DeleteUser(index);
return true;
}
return false;
}
/// <summary>
/// Save the users
/// Delete a user from the userliset
/// </summary>
public void Save()
/// <param name="username">Username of the user</param>
/// <returns>true if the user was successful removed, false otherwise</returns>
/// <exception cref="KeyNotFoundException"></exception>
public static bool DeleteUser(string username)
{
string json = JsonUtility.ToJson(storedUserList);
File.CreateText(PATH).Close();
File.WriteAllText(PATH, json);
int index = GetUsers().FindIndex((e) => e.GetUsername() == username);
try { return DeleteUser(index); }
catch (IndexOutOfRangeException) { throw new KeyNotFoundException(); }
}
/// <summary>
/// Override the current content of the userlist by what is stored on disk
/// Save the current UserList
/// </summary>
public void Load()
public static void Save()
{
try
{
storedUserList.storedUsers.Clear();
string text = File.ReadAllText(PATH);
storedUserList = JsonUtility.FromJson<StoredUserList>(text);
}
catch (FileNotFoundException) { Debug.Log($"Path '{PATH}' not found"); }
PersistentDataController.GetInstance().Save();
}
}

View File

@@ -1,6 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -10,11 +7,6 @@ using UnityEngine.UI;
/// </summary>
public class UserProgressScreen : MonoBehaviour
{
/// <summary>
/// Reference to the userlist
/// </summary>
public UserList userList;
/// <summary>
/// Reference to the current user
/// </summary>
@@ -31,80 +23,24 @@ public class UserProgressScreen : MonoBehaviour
public Image avatar;
/// <summary>
/// UI reference to the user total playtime
/// Reference to the courses panel
/// </summary>
public TMP_Text playtime;
public GameObject coursesPanel;
/// <summary>
/// Prefab of the highscore marker to display on the graph
/// Reference to the minigame panel
/// </summary>
public GameObject highscoreMarker;
public GameObject minigamesPanel;
/// <summary>
/// Prefab of a course card
/// Reference to the courses tab button (to set nice color)
/// </summary>
public GameObject courseCardPrefab;
public Image coursesTabButton;
/// <summary>
/// UI reference to the container holding all course cards
/// Reference to the minigames tab button (to set nice color)
/// </summary>
public GameObject coursesContainer;
/// <summary>
/// UI reference to the message that displays when no course progress is present
/// </summary>
public GameObject emptyCourses;
/// <summary>
/// Prefab of a minigame card
/// </summary>
public GameObject minigameCardPrefab;
/// <summary>
/// UI reference to the container holding all the minigame cards
/// </summary>
public GameObject minigamesContainer;
/// <summary>
/// UI reference to the message that displays when no minigame progress is present
/// </summary>
public GameObject emptyMinigames;
/// <summary>
/// UI reference to the plot
/// </summary>
public RawImage progressGraph;
/// <summary>
/// Left and right padding of the graph
/// </summary>
private const int GRAPH_PADDING_X_PX = 50;
/// <summary>
/// Top and bottom padding of the graph
/// </summary>
private const int GRAPH_PADDING_Y_PX = 50;
/// <summary>
/// Radius of the point on the graph
/// </summary>
private const int GRAPH_POINT_RADIUS = 10;
/// <summary>
/// Size of the line on the graph
/// </summary>
private const int GRAPH_LINE_SIZE = 4;
/// <summary>
/// Current selected activity draw to the graph
/// </summary>
private int selectedActivity = -1;
/// <summary>
/// List of activity backgrounds and indices
/// </summary>
private List<Tuple<Image, int>> activities = new List<Tuple<Image, int>>();
public Image minigamesTabButton;
/// <summary>
/// Start is called before the first frame update
@@ -112,266 +48,35 @@ public class UserProgressScreen : MonoBehaviour
void Start()
{
// Assign the current user
user = userList.GetCurrentUser();
PersistentDataController.GetInstance().Load();
user = UserList.GetCurrentUser();
// Set correct displayed items
username.text = user.username;
avatar.sprite = user.avatar;
// TODO: implement total playtime
//playtime.text = $"Totale speeltijd: {user.playtime.ToString("0.00")}";
username.text = user.GetUsername();
avatar.sprite = user.GetAvatar();
// Set graph inactive
progressGraph.gameObject.SetActive(false);
int i = 0;
// Display courses
coursesContainer.SetActive(user.courses.Count > 0);
emptyCourses.SetActive(user.courses.Count <= 0);
foreach (Progress courseProgress in user.courses)
{
// Create instance of prefab
GameObject instance = GameObject.Instantiate(courseCardPrefab, coursesContainer.transform.Find("Viewport").Find("Content").transform);
int j = i++;
// Initialize card
CourseProgressCard cpc = instance.GetComponent<CourseProgressCard>();
cpc.courseProgress = courseProgress;
cpc.selectActivity = () => UpdateSelection(j);
// Store reference to background so we can apply fancy coloring
Image background = instance.GetComponent<Image>();
background.color = Color.gray;
activities.Add(Tuple.Create(background, (int)courseProgress.Get<CourseIndex>("courseIndex")));
}
// Display minigames
minigamesContainer.SetActive(user.minigames.Count > 0);
emptyMinigames.SetActive(user.minigames.Count <= 0);
foreach (Progress minigameProgress in user.minigames)
{
// Create instance of prefab
GameObject instance = GameObject.Instantiate(minigameCardPrefab, minigamesContainer.transform.Find("Viewport").Find("Content").transform);
int j = i++;
// Initialize card
MinigameProgressCard mpc = instance.GetComponent<MinigameProgressCard>();
mpc.minigameProgress = minigameProgress;
mpc.selectActivity = () => UpdateSelection(j);
// Store reference to background so we can apply fancy coloring
Image background = instance.GetComponent<Image>();
background.color = Color.gray;
activities.Add(Tuple.Create(background, (int)minigameProgress.Get<MinigameIndex>("minigameIndex")));
}
DisplayCourses();
}
/// <summary>
/// Update the current selected activity
/// Switch to displaying the courses
/// </summary>
/// <param name="newActivity">Index to the new activity</param>
private void UpdateSelection(int newActivity)
public void DisplayCourses()
{
if (selectedActivity < 0)
{
progressGraph.gameObject.SetActive(true);
}
else
{
activities[selectedActivity].Item1.color = Color.gray;
}
selectedActivity = newActivity;
activities[selectedActivity].Item1.color = Color.blue;
if (selectedActivity < user.courses.Count)
{
// TODO: create a better graph
//DisplayCourseGraph((CourseIndex)activities[selectedActivity].Item2);
// For now: just deactivate graph rendering
progressGraph.gameObject.SetActive(false);
}
else
{
DisplayMinigameGraph((MinigameIndex)activities[selectedActivity].Item2);
// TODO: remove line, this is only because courses deactivates the graph
progressGraph.gameObject.SetActive(true);
}
coursesPanel.SetActive(true);
coursesTabButton.color = new Color(247 / 255f, 173 / 255f, 25 / 255f, 1f);
minigamesPanel.SetActive(false);
minigamesTabButton.color = new Color(247 / 255f, 173 / 255f, 25 / 255f, 120/255f);
}
/// <summary>
/// Plot the graph of a course
/// Switch to displaying the minigames
/// </summary>
/// <param name="index">Index of the course</param>
/// <remarks>TODO: create a better plot</remarks>
private void DisplayCourseGraph(CourseIndex index) { }
/// <summary>
/// Plot the graph of a minigame
/// </summary>
/// <param name="minigameIndex">Index of the minigame</param>
private void DisplayMinigameGraph(MinigameIndex minigameIndex)
public void DisplayMinigames()
{
Progress progress = user.GetMinigameProgress(minigameIndex);
List<Score> latestScores = progress.Get<List<Score>>("latestScores");
List<Score> highestScores = progress.Get<List<Score>>("highestScores");
if (0 < highestScores.Count)
{
PlotGraph(latestScores.ConvertAll<double>((s) => (double)s.scoreValue), highestScores[0].scoreValue);
}
else
{
progressGraph.gameObject.SetActive(false);
}
}
/// <summary>
/// Plot points and a highscore on the graph
/// </summary>
/// <param name="scores">List of score values to plot</param>
/// <param name="highscore">Highscore value (this will be plotted in a fancy color)</param>
private void PlotGraph(List<double> scores, double highscore)
{
// Remove previous marker(s)
foreach (Transform child in progressGraph.gameObject.transform)
{
Destroy(child.gameObject);
}
// Get texture reference
Texture2D tex = progressGraph.texture as Texture2D;
if (tex == null)
{
RectTransform rt = progressGraph.gameObject.transform as RectTransform;
tex = new Texture2D(
width: (int)rt.sizeDelta.x,
height: (int)rt.sizeDelta.y,
textureFormat: TextureFormat.ARGB32,
mipCount: 3,
linear: true
);
}
tex.filterMode = FilterMode.Point;
// calculate positions and offsets
int x0 = GRAPH_PADDING_X_PX, x1 = tex.width - GRAPH_PADDING_X_PX;
int y0 = GRAPH_PADDING_Y_PX, y1 = tex.height - GRAPH_PADDING_Y_PX;
double min = scores.Min();
double max = scores.Max();
List<Tuple<int, int>> points = new List<Tuple<int, int>>();
for (int i = 0; i < scores.Count; i++)
{
int x = x0 + (scores.Count > 1 ? i * ((x1 - x0) / (scores.Count - 1)) : (x1 - x0) / 2);
int y = y0 + (int)((y1 - y0) * (min != max ? (scores[i] - min) / (max - min) : 0.5));
points.Add(Tuple.Create(x, y));
}
// Calculate scaling
int mag = (int)Math.Round(Math.Log10(max));
int MAG = (int)Math.Pow(10, mag);
double c = max / MAG;
// Draw axes
if (min != max)
{
for (double d = c / 5.0; d < c; d += 0.2 * c)
{
int y = y0 + (int)((y1 - y0) * (MAG * d - min) / (max - min));
DrawLine(tex, x0, y, x1, y, 2, Color.gray);
}
}
else
{
int y = y0 + (int)((y1 - y0) * 0.5);
DrawLine(tex, x0, y0, x1, y0, 2, Color.gray);
DrawLine(tex, x0, y, x1, y, 2, Color.gray);
DrawLine(tex, x0, y1, x1, y1, 2, Color.gray);
}
// Draw highscore
if (min <= highscore && highscore <= max)
{
int y = y0 + (int)((y1 - y0) * (min != max ? (highscore - min) / (max - min) : 0.5));
DrawLine(tex, x0, y, x1, y, 3, new Color(255, 192, 0));
GameObject marker = GameObject.Instantiate(highscoreMarker, progressGraph.gameObject.transform);
RectTransform rect = marker.GetComponent<RectTransform>();
rect.localPosition = new Vector3(0, y - 25, 0);
}
// Draw points
for (int i = 0; i < points.Count; i++)
{
Tuple<int, int> p = points[i];
if (0 < i)
{
Tuple<int, int> q = points[i - 1];
DrawLine(tex, p.Item1, p.Item2, q.Item1, q.Item2, GRAPH_LINE_SIZE, Color.blue);
}
DrawPoint(tex, p.Item1, p.Item2, GRAPH_POINT_RADIUS, Color.blue);
}
// Apply to graph GameObject
tex.Apply();
progressGraph.texture = tex;
}
/// <summary>
/// Draw a point to a texture
/// </summary>
/// <param name="tex">Texture2D to plot point on</param>
/// <param name="xc">Center x-pos</param>
/// <param name="yc">Center y-pos</param>
/// <param name="r">Radius (aka width and height)</param>
/// <param name="color">Color of the point</param>
private void DrawPoint(Texture2D tex, int xc, int yc, int r, Color color)
{
for (int y = yc - r; y < yc + r; y++)
{
for (int x = xc - r; x < xc + r; x++)
{
tex.SetPixel(x, y, color);
}
}
}
/// <summary>
/// Draw a line to a texture
/// </summary>
/// <param name="tex">Texture2D to plot line on</param>
/// <param name="x0">Starting x-pos</param>
/// <param name="y0">Strating y-pos</param>
/// <param name="x1">Ending x-pos</param>
/// <param name="y1">Ending y-pos</param>
/// <param name="size">Size of the line (width)</param>
/// <param name="color">Color of the line</param>
private void DrawLine(Texture2D tex, int x0, int y0, int x1, int y1, int size, Color color)
{
int w = x1 - x0;
int h = y1 - y0;
int length = Mathf.Abs(x1 - x0);
if (Mathf.Abs(y1 - y0) > length)
{
length = Mathf.Abs(h);
}
double dx = w / (double)length;
double dy = h / (double)length;
double x = x0;
double y = y0;
double r = size / 2;
for (int i = 0; i <= length; i++)
{
for (int j = (int)(y - r); j < y + r; j++)
{
for (int k = (int)(x - r); k < x + r; k++)
{
tex.SetPixel(k, j, color);
}
}
x += dx;
y += dy;
}
coursesPanel.SetActive(false);
coursesTabButton.color = new Color(247 / 255f, 173 / 255f, 25 / 255f, 120 / 255f);
minigamesPanel.SetActive(true);
minigamesTabButton.color = new Color(247 / 255f, 173 / 255f, 25 / 255f, 1f);
}
}

View File

@@ -1,25 +0,0 @@
{
"name": "AccountsTests",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"AccountsScripts",
"InterfacesScripts"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

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

View File

@@ -0,0 +1,26 @@
{
"name": "AccountEditMode",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"AccountsScripts",
"InterfacesScripts",
"ArchitectureScripts"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,27 @@
using NUnit.Framework;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
/// <summary>
/// Test the UserAvatarList class
/// </summary>
[TestFixture]
public class UserAvatarListTests
{
/// <summary>
/// Test the UserAvatarList class correctly initializes the UserList class
/// </summary>
[Test]
public void Test_Awake()
{
UserList.AVATARS = new List<Sprite>();
var scriptableObject = AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset");
scriptableObject.Awake();
for (int i = 0; i < scriptableObject.avatars.Count; i++)
Assert.AreEqual(scriptableObject.avatars[i], UserList.AVATARS[i]);
}
}

View File

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

View File

@@ -0,0 +1,33 @@
using NUnit.Framework;
/// <summary>
/// Test the UserCreationScreen class
/// </summary>
[TestFixture]
public class UserCreationScreenTests
{
/// <summary>
/// Tets IsValidUsername will return <c>true</c> for an valid username
/// </summary>
[Test]
public void Test_UserCreationScreen_IsValidUsername_True()
{
foreach (char c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
Assert.IsTrue(UserCreationScreen.IsValidUsername(c.ToString()));
Assert.IsTrue(UserCreationScreen.IsValidUsername("123456789AbC"));
}
/// <summary>
/// Tets IsValidUsername will return <c>false</c> for an invalid username
/// </summary>
[Test]
public void Test_UserCreationScreen_IsValidUsername_False()
{
Assert.IsFalse(UserCreationScreen.IsValidUsername(string.Empty));
foreach (char c in " \n\t+-*/%_.,;:!?(){}[]\\'\"|&~^$")
Assert.IsFalse(UserCreationScreen.IsValidUsername(c.ToString()));
Assert.IsFalse(UserCreationScreen.IsValidUsername("123456789_10_11_12_13"));
}
}

View File

@@ -0,0 +1,444 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
/// <summary>
/// Test the UserList class
/// </summary>
[TestFixture]
public class UserListTests
{
/// <summary>
/// Create a new path so the existing .json file will not be overwritten
/// </summary>
private static string PATH = $"{Application.persistentDataPath}/wesign_unit_test.json";
/// <summary>
/// Helper variable for quick user creation
/// </summary>
private string username = "u5erNam3";
/// <summary>
/// Helper variable for quick user creation
/// </summary>
private Sprite avatar = null;
/// <summary>
/// Setup the tests
/// </summary>
[SetUp]
public void Setup_UserList()
{
PersistentDataController.PATH = UserListTests.PATH;
if (File.Exists(PATH))
File.Delete(PATH);
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
avatar = UserList.AVATARS[0];
}
/// <summary>
/// Cleanup after testing
/// </summary>
[TearDown]
public void TearDown_UserList()
{
PersistentDataController.PATH = null;
}
/// <summary>
/// Test for creation of a new UserList
/// </summary>
[Test]
public void Test_New_UserList()
{
Assert.Zero(UserList.GetUsers().Count);
}
/// <summary>
/// Test for creation of new user (without adding the user to the list)
/// </summary>
[Test]
public void Test_UserList_AddUser()
{
Assert.Zero(UserList.GetUsers().Count);
User user = UserList.AddUser(username, avatar);
Assert.IsNotNull(user);
Assert.AreEqual(1, UserList.GetUsers().Count);
Assert.AreEqual(username, user.GetUsername());
Assert.AreEqual(avatar, user.GetAvatar());
}
/// <summary>
/// Test whether an existing user can be found by its username
/// </summary>
[Test]
public void Test_UserList_GetUserByUsername_Valid()
{
User u = UserList.AddUser(username, avatar);
User v = UserList.GetUserByUsername(username);
Assert.AreEqual(u.GetUsername(), v.GetUsername());
}
/// <summary>
/// Test whether a non-existing user can not be found
/// </summary>
[Test]
public void Test_UserList_GetUserByUsername_Null()
{
User user = UserList.GetUserByUsername("not-a-user");
Assert.IsNull(user);
}
/// <summary>
/// Test whether an existing user can be found by its username
/// </summary>
[Test]
public void Test_UserList_GetUserByIndex_Valid()
{
User u = UserList.AddUser(username, avatar);
User v = UserList.GetUserByIndex(0);
Assert.AreEqual(u.GetUsername(), v.GetUsername());
}
/// <summary>
/// Test whether a non-existing user can not be found
/// </summary>
[Test]
public void Test_UserList_GetUserByIndex_Exception()
{
Assert.Throws<IndexOutOfRangeException>(delegate { UserList.GetUserByIndex(0); });
}
/// <summary>
/// Test whether a userlist is correctly returned
/// </summary>
[Test]
public void Test_UserList_GetUsers()
{
List<User> u = new List<User>();
for (int i = 0; i < 5; i++)
u.Add(UserList.AddUser($"{username}_{i}", avatar));
List<User> v = UserList.GetUsers();
Assert.AreEqual(v.Count, u.Count);
for (int i = 0; i < 5; i++)
Assert.AreEqual(u[i].GetUsername(), v[i].GetUsername());
}
/// <summary>
/// Test whether the correct current user is returned
/// </summary>
[Test]
public void Test_UserList_GetCurrentUser()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
UserList.ChangeCurrentUser(2);
User W = UserList.GetCurrentUser();
Assert.AreEqual(w.GetUsername(), W.GetUsername());
}
/// <summary>
/// Test whether a null user is returned when the userlist is empty
/// </summary>
[Test]
public void Test_UserList_GetCurrentUser_Empty()
{
User user = UserList.GetCurrentUser();
Assert.IsNull(user);
}
/// <summary>
/// Test whether the correct index is returned for the current user
/// </summary>
[Test]
public void Test_UserList_IndexOf_Valid()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
UserList.ChangeCurrentUser(2);
Assert.AreEqual(0, UserList.IndexOf(u.GetUsername()));
Assert.AreEqual(1, UserList.IndexOf(v.GetUsername()));
Assert.AreEqual(2, UserList.IndexOf(w.GetUsername()));
}
/// <summary>
/// Test whether the correct index is returned for the current user
/// </summary>
[Test]
public void Test_UserList_IndexOf_Exception()
{
User u = new User(new PersistentDataController.SavedUserData()
{
username = username,
avatarIndex = 0
});
Assert.Throws<KeyNotFoundException>(delegate { UserList.IndexOf(u.GetUsername()); });
}
/// <summary>
/// Test whether the current user (referenced by index) is correctly changed
/// </summary>
[Test]
public void Test_UserList_ChangeCurrentUser_ValidIndex()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
UserList.ChangeCurrentUser(2);
User W = UserList.GetCurrentUser();
Assert.AreEqual(w.GetUsername(), W.GetUsername());
}
/// <summary>
/// Test whether the current user is not changed when a bad index is given
/// </summary>
[Test]
public void Test_UserList_ChangeCurrentUser_InvalidIndex()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
Assert.Throws<IndexOutOfRangeException>(delegate { UserList.ChangeCurrentUser(-1); });
Assert.Throws<IndexOutOfRangeException>(delegate { UserList.ChangeCurrentUser(5); });
}
/// <summary>
/// Test whether the current user is not changed when a bad index is given
/// </summary>
[Test]
public void Test_UserList_ChangeCurrentUser_IndexEmpty()
{
Assert.Throws<IndexOutOfRangeException>(delegate { UserList.ChangeCurrentUser(0); });
}
/// <summary>
/// Test whether the current user is correctly changed
/// </summary>
[Test]
public void Test_UserList_ChangeCurrentUser_ValidUsername()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
UserList.ChangeCurrentUser(v.GetUsername());
User V = UserList.GetCurrentUser();
Assert.AreEqual(v.GetUsername(), V.GetUsername());
}
/// <summary>
/// Test whether the current user is not changed when a non-existing user is given
/// </summary>
[Test]
public void Test_UserList_ChangeCurrentUser_InvalidUsername()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = new User(new PersistentDataController.SavedUserData()
{
username = $"{username}_{'v'}",
avatarIndex = 0
});
Assert.Throws<KeyNotFoundException>(delegate { UserList.ChangeCurrentUser(v.GetUsername()); });
}
/// <summary>
/// Test whether the current user is not changed when a null user is given
/// </summary>
[Test]
public void Test_UserList_ChangeCurrentUser_NullUsername()
{
Assert.Throws<KeyNotFoundException>(delegate { UserList.ChangeCurrentUser(null); });
}
/// <summary>
/// Test whether deleting a existing user (referenced by index) will correctly be removed
/// </summary>
[Test]
public void Test_UserList_DeleteUser_ValidIndex()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
Assert.IsTrue(UserList.DeleteUser(1));
}
/// <summary>
/// Test whether deleting a non-existing user (referenced by wrong index) will fail
/// </summary>
[Test]
public void Test_UserList_DeleteUser_InvalidIndex()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
Assert.Throws<IndexOutOfRangeException>(delegate { UserList.DeleteUser(-1); });
Assert.Throws<IndexOutOfRangeException>(delegate { UserList.DeleteUser(5); });
}
/// <summary>
/// Test whether deleting any user from an empty userlist will fail
/// </summary>
[Test]
public void Test_UserList_DeleteUser_IndexEmpty()
{
Assert.Throws<IndexOutOfRangeException>(delegate { UserList.DeleteUser(0); });
}
/// <summary>
/// Test whether deleting an existing user will correctly remove the user and set the currentUserIndex correctly
/// </summary>
[Test]
public void Test_UserList_DeleteUser_BeforeCurrentUser()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
UserList.ChangeCurrentUser(1);
Assert.AreEqual(3, UserList.GetUsers().Count);
Assert.AreEqual(1, UserList.IndexOf(UserList.GetCurrentUser().GetUsername()));
Assert.IsTrue(UserList.DeleteUser(u.GetUsername()));
Assert.AreEqual(2, UserList.GetUsers().Count);
Assert.AreEqual(0, UserList.IndexOf(UserList.GetCurrentUser().GetUsername()));
}
/// <summary>
/// Test whether deleting an existing user will correctly remove the user and set the currentUserIndex correctly
/// </summary>
[Test]
public void Test_UserList_DeleteUser_CurrentUser()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
UserList.ChangeCurrentUser(1);
Assert.AreEqual(3, UserList.GetUsers().Count);
Assert.AreEqual(1, UserList.IndexOf(UserList.GetCurrentUser().GetUsername()));
Assert.IsTrue(UserList.DeleteUser(v.GetUsername()));
Assert.AreEqual(2, UserList.GetUsers().Count);
Assert.AreEqual(0, UserList.IndexOf(UserList.GetCurrentUser().GetUsername()));
}
/// <summary>
/// Test whether deleting an existing user will correctly remove the user and set the currentUserIndex correctly
/// </summary>
[Test]
public void Test_UserList_DeleteUser_AfterCurrentUser()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = UserList.AddUser($"{username}_{'v'}", avatar);
User w = UserList.AddUser($"{username}_{'w'}", avatar);
UserList.ChangeCurrentUser(1);
Assert.AreEqual(3, UserList.GetUsers().Count);
Assert.AreEqual(1, UserList.IndexOf(UserList.GetCurrentUser().GetUsername()));
Assert.IsTrue(UserList.DeleteUser(w.GetUsername()));
Assert.AreEqual(2, UserList.GetUsers().Count);
Assert.AreEqual(1, UserList.IndexOf(UserList.GetCurrentUser().GetUsername()));
}
/// <summary>
/// Test whether deleting a non-existing user will not affect the userlist
/// </summary>
[Test]
public void Test_UserList_DeleteUser_Invalid()
{
User u = UserList.AddUser($"{username}_{'u'}", avatar);
User v = new User(new PersistentDataController.SavedUserData()
{
username = $"{username}_{'v'}",
avatarIndex = 0
});
Assert.AreEqual(1, UserList.GetUsers().Count);
Assert.Throws<KeyNotFoundException>(delegate { UserList.DeleteUser(v.GetUsername()); });
Assert.AreEqual(1, UserList.GetUsers().Count);
}
/// <summary>
/// Test whether calling the DeleteUser function on an empty list will not affect the userlist
/// </summary>
[Test]
public void Test_UserList_DeleteUser_Empty()
{
User user = new User(new PersistentDataController.SavedUserData()
{
username = username,
avatarIndex = 0
});
Assert.Zero(UserList.GetUsers().Count);
Assert.Throws<KeyNotFoundException>(delegate { UserList.DeleteUser(user.GetUsername()); });
Assert.Zero(UserList.GetUsers().Count);
}
/// <summary>
/// Test whether a savefile is correctly constructed when no savefile is present
/// </summary>
[Test]
public void Test_UserList_Save_New()
{
List<User> u = new List<User>();
for (int i = 0; i < 5; i++)
u.Add(UserList.AddUser($"{username}_{i}", avatar));
UserList.ChangeCurrentUser(3);
UserList.Save();
FileAssert.Exists(PATH);
string content = File.ReadAllText(PATH);
string expected = $"{{\"version\":{PersistentDataController.VERSION},\"users\":[{{\"entries\":[],\"username\":\"u5erNam3_0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}},{{\"entries\":[],\"username\":\"u5erNam3_1\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}},{{\"entries\":[],\"username\":\"u5erNam3_2\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}},{{\"entries\":[],\"username\":\"u5erNam3_3\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}},{{\"entries\":[],\"username\":\"u5erNam3_4\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}}],\"currentUser\":3,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
Assert.AreEqual(expected, content);
}
/// <summary>
/// Test whether a savefile is correctly constructed when a savefile already exists
/// </summary>
[Test]
public void Test_UserList_Save_Existing()
{
if (!File.Exists(PATH))
{
File.CreateText(PATH).Close();
File.WriteAllText(PATH, "https://www.youtube.com/watch?v=dQw4w9WgXcQ");
}
List<User> u = new List<User>();
for (int i = 0; i < 5; i++)
u.Add(UserList.AddUser($"{username}_{i}", avatar));
UserList.ChangeCurrentUser(3);
UserList.Save();
FileAssert.Exists(PATH);
string content = File.ReadAllText(PATH);
string expected = $"{{\"version\":{PersistentDataController.VERSION},\"users\":[{{\"entries\":[],\"username\":\"u5erNam3_0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}},{{\"entries\":[],\"username\":\"u5erNam3_1\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}},{{\"entries\":[],\"username\":\"u5erNam3_2\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}},{{\"entries\":[],\"username\":\"u5erNam3_3\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}},{{\"entries\":[],\"username\":\"u5erNam3_4\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}}],\"currentUser\":3,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
Assert.AreEqual(expected, content);
}
/// <summary>
/// Test whether a save file is correctly constructed from an empty userlist
/// </summary>
[Test]
public void Test_UserList_Save_Empty()
{
UserList.Save();
FileAssert.Exists(PATH);
string content = File.ReadAllText(PATH);
string expected = $"{{\"version\":{PersistentDataController.VERSION},\"users\":[],\"currentUser\":-1,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
Assert.AreEqual(expected, content);
}
}

View File

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

View File

@@ -0,0 +1,225 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
/// <summary>
/// Test the User class
/// </summary>
[TestFixture]
public class UserTests
{
/// <summary>
/// Reference to the user to be tested
/// </summary>
private User user;
/// <summary>
/// Setup the tests
/// </summary>
[SetUp]
public void Setup_User()
{
PersistentDataController.SavedUserData data = new PersistentDataController.SavedUserData();
data.username = "username";
data.avatarIndex = 0;
user = new User(data);
}
/// <summary>
/// Test for the creation of a new user
/// </summary>
[Test]
public void Test_New_User()
{
Assert.NotNull(user);
Assert.Zero(user.GetCourses().Count);
Assert.Zero(user.GetMinigames().Count);
}
/// <summary>
/// Test whether the correct username is returned
/// </summary>
[Test]
public void Test_User_GetUsername()
{
Assert.AreEqual("username", user.GetUsername());
}
/// <summary>
/// Test whether the correct avatar is returned
/// </summary>
[Test]
public void Test_User_GetAvatar()
{
Assert.AreEqual(UserList.AVATARS[0], user.GetAvatar());
}
/// <summary>
/// Test whether the correct total playtime is returned
/// </summary>
[Test]
public void Test_User_GetPlaytime()
{
Assert.AreEqual(0.0, user.GetPlaytime());
}
/// <summary>
/// Test whether progress on a new course can be added
/// </summary>
[Test]
public void Test_User_AddCourse()
{
var p = new PersistentDataController.SavedCourseProgress();
user.AddCourseProgress(p);
Assert.AreEqual(user.GetCourses().Count, 1);
Assert.Zero(user.GetMinigames().Count);
}
/// <summary>
/// Test whether progress on a new minigame can be added
/// </summary>
[Test]
public void Test_User_AddMinigame()
{
var p = new PersistentDataController.SavedMinigameProgress();
user.AddMinigameProgress(p);
Assert.Zero(user.GetCourses().Count);
Assert.AreEqual(user.GetMinigames().Count, 1);
}
/// <summary>
/// Test GetRecentCourses will return empty when no progress is stored
/// </summary>
[Test]
public void Test_User_GetRecentCourses_Empty()
{
Assert.Zero(user.GetRecentCourses().Count);
}
/// <summary>
/// TEMPORARY test for GetRecentCourses will return all progress that is stored
/// </summary>
[Test]
public void Test_User_GetRecentCourses_All()
{
var p = new PersistentDataController.SavedCourseProgress();
p.courseIndex = CourseIndex.FINGERSPELLING;
p.progress = 0.5f;
user.AddCourseProgress(p);
List<Tuple<CourseIndex, float>> list = user.GetRecentCourses();
Assert.AreEqual(list.Count, 1);
Assert.AreEqual(list[0].Item1, CourseIndex.FINGERSPELLING);
Assert.AreEqual(list[0].Item2, 0.5f);
}
/// <summary>
/// Test GetRecommendedCourses will return <c>Tuple<CourseIndex.FINGERSPELLING, 0.0></c> when no progress is stored
/// </summary>
[Test]
public void Test_User_GetRecommendedCourses_Empty()
{
List<Tuple<CourseIndex, float>> list = user.GetRecommendedCourses();
Assert.AreEqual(list.Count, 1);
Assert.AreEqual(list[0].Item1, CourseIndex.FINGERSPELLING);
Assert.AreEqual(list[0].Item2, 0.0f);
}
/// <summary>
/// TEMPORARY test for GetRecommenedCourses will return all progress that is stored
/// </summary>
[Test]
public void Test_User_GetRecommendedCourses_All()
{
var p = new PersistentDataController.SavedCourseProgress();
p.courseIndex = CourseIndex.FINGERSPELLING;
p.progress = 0.5f;
user.AddCourseProgress(p);
List<Tuple<CourseIndex, float>> list = user.GetRecommendedCourses();
Assert.AreEqual(list.Count, 1);
Assert.AreEqual(list[0].Item1, CourseIndex.FINGERSPELLING);
Assert.AreEqual(list[0].Item2, 0.5f);
}
/// <summary>
/// Test GetCourseProgress returns null when course cannot be found
/// </summary>
[Test]
public void Test_User_GetCourseProgress_Null()
{
Assert.Null(user.GetCourseProgress(CourseIndex.FINGERSPELLING));
Assert.Null(user.GetCourseProgress((CourseIndex)100));
}
/// <summary>
/// Test GetCourseProgress returns correct progress object
/// </summary>
[Test]
public void Test_User_GetCourseProgress_Valid()
{
var p = new PersistentDataController.SavedCourseProgress();
p.courseIndex = CourseIndex.FINGERSPELLING;
p.progress = 3.14159265f;
user.AddCourseProgress(p);
var q = user.GetCourseProgress(CourseIndex.FINGERSPELLING);
Assert.AreEqual(q.courseIndex, CourseIndex.FINGERSPELLING);
Assert.AreEqual(q.progress, 3.14159265f);
}
/// <summary>
/// Test progress of a course is correctly reset (aka removed)
/// </summary>
[Test]
public void Test_User_ResetCourseProgres()
{
var p = new PersistentDataController.SavedCourseProgress();
p.courseIndex = CourseIndex.FINGERSPELLING;
user.AddCourseProgress(p);
Assert.IsNotNull(user.GetCourseProgress(CourseIndex.FINGERSPELLING));
user.ResetCourseProgress(CourseIndex.FINGERSPELLING);
Assert.IsNull(user.GetCourseProgress(CourseIndex.FINGERSPELLING));
}
/// <summary>
/// Test GetMinigameProgress returns null when minigame cannot be found
/// </summary>
[Test]
public void Test_User_GetMinigameProgress_Null()
{
Assert.Null(user.GetMinigameProgress(MinigameIndex.SPELLING_BEE));
Assert.Null(user.GetMinigameProgress((MinigameIndex)100));
var p = new PersistentDataController.SavedMinigameProgress();
p.minigameIndex = MinigameIndex.SPELLING_BEE;
user.AddMinigameProgress(p);
Assert.Null(user.GetMinigameProgress(MinigameIndex.HANGMAN));
}
/// <summary>
/// Test GetMinigameProgress returns correct progress object
/// </summary>
[Test]
public void Test_User_GetMinigameProgress_Valid()
{
var p = new PersistentDataController.SavedMinigameProgress();
p.minigameIndex = MinigameIndex.SPELLING_BEE;
user.AddMinigameProgress(p);
var q = user.GetMinigameProgress(MinigameIndex.SPELLING_BEE);
Assert.AreEqual(q.minigameIndex, MinigameIndex.SPELLING_BEE);
Assert.Zero(q.latestScores.Count);
Assert.Zero(q.highestScores.Count);
}
/// <summary>
/// Test progress of a minigame is correctly reset (aka removed)
/// </summary>
[Test]
public void Test_User_ResetMinigameProgres()
{
var p = new PersistentDataController.SavedMinigameProgress();
p.minigameIndex = MinigameIndex.SPELLING_BEE;
user.AddMinigameProgress(p);
Assert.IsNotNull(user.GetMinigameProgress(MinigameIndex.SPELLING_BEE));
user.ResetMinigameProgress(MinigameIndex.SPELLING_BEE);
Assert.IsNull(user.GetMinigameProgress(MinigameIndex.SPELLING_BEE));
}
}

View File

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

View File

@@ -0,0 +1,26 @@
{
"name": "AccountPlayMode",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"AccountsScripts",
"InterfacesScripts",
"Unity.TextMeshPro",
"CommonScripts",
"ArchitectureScripts"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

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

View File

@@ -0,0 +1,379 @@
using NUnit.Framework;
using System.Collections;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.UI;
/// <summary>
/// Test the ChangeUserScreen class
/// </summary>
[TestFixture]
public class ChangeUserScreenTests
{
/// <summary>
/// Wait time between scene transitions
/// </summary>
private const float WAIT_TIME = 0.2f;
/// <summary>
/// The default current user when dealing with multiple users
/// </summary>
private const int currentUser = 2;
/// <summary>
/// Setup the tests for a single user
/// </summary>
private IEnumerator Setup_ChangeUserScreen_SingleUser(string startScreen = "Accounts/Scenes/ChangeUserScreen")
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string oneUser = "{\"version\":1537,\"users\":[{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene(startScreen);
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Setup the tests for a multiple users
/// </summary>
private IEnumerator Setup_ChangeUserScreen_MultipleUsers(string startScreen = "Accounts/Scenes/ChangeUserScreen")
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string users = "";
for (int i = 0; i < 5; i++)
{
users += $"{{\"entries\":[],\"username\":\"Tester{i}\",\"avatarIndex\":{i},\"playtime\":0.0,\"minigames\":[],\"courses\":[]}}";
if (i < 4) users += ",";
}
const int currentUser = 2;
string fiveUsers = $"{{\"version\":1537,\"users\":[{users}],\"currentUser\":{currentUser},\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, fiveUsers);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene(startScreen);
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Cleanup after testing
/// </summary>
[TearDown]
public void TearDown_ChangeUserScreen()
{
PersistentDataController.PATH = null;
}
/// <summary>
/// Test whether every item that needs to be assign in the editor, is assigned
/// </summary>
[UnityTest]
public IEnumerator Test_EditorAssignments()
{
yield return Setup_ChangeUserScreen_SingleUser();
var changeUserController = GameObject.FindObjectOfType<ChangeUserScreen>();
Assert.IsNotNull(changeUserController);
Assert.IsNotNull(changeUserController.userPrefab);
Assert.IsNotNull(changeUserController.usersContainer);
Assert.IsNotNull(changeUserController.error);
}
/// <summary>
/// Test whether the screen is correctly initialized (one user)
/// </summary>
[UnityTest]
public IEnumerator Test_Start_SingleUser()
{
yield return Setup_ChangeUserScreen_SingleUser();
var changeUserController = GameObject.FindObjectOfType<ChangeUserScreen>();
Assert.IsFalse(changeUserController.error.activeSelf);
}
/// <summary>
/// Test whether the users is correctly displayed (one user)
/// </summary>
[UnityTest]
public IEnumerator Test_DisplayUsers_SingleUser()
{
yield return Setup_ChangeUserScreen_SingleUser();
// The content of the usercard is tested in the `UserCardTests`-script
// Here, we only check the amount of users
Assert.AreEqual(1, UserList.GetUsers().Count);
var cards = GameObject.FindObjectsOfType<UserCard>().ToList();
Assert.AreEqual(1, cards.Count);
Assert.AreEqual(new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f), cards[0].gameObject.GetComponent<Image>().color);
}
/// <summary>
/// Test whether the users are correctly displayed (mulitple users)
/// </summary>
[UnityTest]
public IEnumerator Test_DisplayUsers_MultipleUsers()
{
yield return Setup_ChangeUserScreen_MultipleUsers();
// The content of the usercard is tested in the `UserCardTests`-script
// Here, we only check the amount of users and whether the correct one is selected
Assert.AreEqual(UserList.GetCurrentUser().GetUsername(), UserList.GetUserByIndex(currentUser).GetUsername());
var cards = GameObject.FindObjectsOfType<UserCard>().ToList();
Assert.AreEqual(UserList.GetUsers().Count, cards.Count);
for (int i = 0; i < cards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, cards[i].gameObject.GetComponent<Image>().color);
}
}
/// <summary>
/// Test whether selecting a new user before the current one updates the display correctly
/// </summary>
[UnityTest]
public IEnumerator Test_UpdateSelection_BeforeCurrentUser()
{
yield return Setup_ChangeUserScreen_MultipleUsers();
var cards = GameObject.FindObjectsOfType<UserCard>().ToList();
// Before update
for (int i = 0; i < cards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, cards[i].gameObject.GetComponent<Image>().color);
}
// Update
const int newUser = 1;
cards[newUser].selectUser.Invoke();
// After update
for (int i = 0; i < cards.Count; i++)
{
Color expected = i == newUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, cards[i].gameObject.GetComponent<Image>().color);
}
}
/// <summary>
/// Test whether selecting the current user for update the display will change nothing
/// </summary>
[UnityTest]
public IEnumerator Test_UpdateSelection_CurrentUser()
{
yield return Setup_ChangeUserScreen_MultipleUsers();
var cards = GameObject.FindObjectsOfType<UserCard>().ToList();
// Before update
for (int i = 0; i < cards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, cards[i].gameObject.GetComponent<Image>().color);
}
// Update
cards[currentUser].selectUser.Invoke();
// After update
for (int i = 0; i < cards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, cards[i].gameObject.GetComponent<Image>().color);
}
}
/// <summary>
/// Test whether selecting a new user after the current one updates the display correctly
/// </summary>
[UnityTest]
public IEnumerator Test_UpdateSelection_AfterCurrentUser()
{
yield return Setup_ChangeUserScreen_MultipleUsers();
var cards = GameObject.FindObjectsOfType<UserCard>().ToList();
// Before update
for (int i = 0; i < cards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, cards[i].gameObject.GetComponent<Image>().color);
}
// Update
const int newUser = 3;
cards[newUser].selectUser.Invoke();
// After update
for (int i = 0; i < cards.Count; i++)
{
Color expected = i == newUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, cards[i].gameObject.GetComponent<Image>().color);
}
}
/// <summary>
/// Test IChooseYou callback
/// </summary>
[UnityTest]
public IEnumerator Test_IChooseYou()
{
yield return Setup_ChangeUserScreen_SingleUser("Common/Scenes/MainMenuScreen");
SystemController.GetInstance().LoadNextScene("Accounts/Scenes/ChangeUserScreen");
yield return new WaitForSeconds(WAIT_TIME);
var changeUserController = GameObject.FindObjectOfType<ChangeUserScreen>();
changeUserController.IChooseYou();
yield return new WaitForSeconds(WAIT_TIME);
Assert.AreEqual(SystemController.GetSceneIndex("Common/Scenes/MainMenuScreen"), SystemController.GetInstance().currentScene);
}
/// <summary>
/// Test the GotoUserCreation callback
/// </summary>
[UnityTest]
public IEnumerator Test_GotoUserCreation()
{
yield return Setup_ChangeUserScreen_SingleUser();
var changeUserController = GameObject.FindObjectOfType<ChangeUserScreen>();
changeUserController.GotoUserCreation();
yield return new WaitForSeconds(WAIT_TIME);
var userCreationController = GameObject.FindObjectOfType<UserCreationScreen>();
Assert.IsNotNull(userCreationController);
}
/// <summary>
/// Test the user cards DeleteUser callback
/// </summary>
[UnityTest]
public IEnumerator Test_UserCardDeleteUser_BeforeCurrentUser()
{
yield return Setup_ChangeUserScreen_MultipleUsers();
// Before update
var oldCards = GameObject.FindObjectsOfType<UserCard>().ToList();
for (int i = 0; i < oldCards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, oldCards[i].gameObject.GetComponent<Image>().color);
}
// Update
oldCards[1].DeleteUser();
var changeUserController = GameObject.FindObjectOfType<ChangeUserScreen>();
Assert.IsFalse(changeUserController.error.activeSelf);
yield return new WaitForSeconds(WAIT_TIME);
// After update
var newCards = GameObject.FindObjectsOfType<UserCard>().ToList();
Assert.AreEqual(oldCards.Count - 1, newCards.Count);
for (int i = 0; i < newCards.Count; i++)
{
Color expected = i == (currentUser - 1) ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, newCards[i].gameObject.GetComponent<Image>().color);
}
}
/// <summary>
/// Test the user cards DeleteUser callback
/// </summary>
[UnityTest]
public IEnumerator Test_UserCardDeleteUser_CurrentUser()
{
yield return Setup_ChangeUserScreen_MultipleUsers();
// Before update
var oldCards = GameObject.FindObjectsOfType<UserCard>().ToList();
for (int i = 0; i < oldCards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, oldCards[i].gameObject.GetComponent<Image>().color);
}
// Update
oldCards[currentUser].DeleteUser();
var changeUserController = GameObject.FindObjectOfType<ChangeUserScreen>();
Assert.IsFalse(changeUserController.error.activeSelf);
yield return new WaitForSeconds(WAIT_TIME);
// After update
var newCards = GameObject.FindObjectsOfType<UserCard>().ToList();
Assert.AreEqual(oldCards.Count - 1, newCards.Count);
for (int i = 0; i < newCards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, newCards[i].gameObject.GetComponent<Image>().color);
}
}
/// <summary>
/// Test the user cards DeleteUser callback
/// </summary>
[UnityTest]
public IEnumerator Test_UserCardDeleteUser_AfterCurrentUser()
{
yield return Setup_ChangeUserScreen_MultipleUsers();
// Before update
var oldCards = GameObject.FindObjectsOfType<UserCard>().ToList();
for (int i = 0; i < oldCards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, oldCards[i].gameObject.GetComponent<Image>().color);
}
// Update
oldCards[3].DeleteUser();
var changeUserController = GameObject.FindObjectOfType<ChangeUserScreen>();
Assert.IsFalse(changeUserController.error.activeSelf);
yield return new WaitForSeconds(WAIT_TIME);
// After update
var newCards = GameObject.FindObjectsOfType<UserCard>().ToList();
Assert.AreEqual(oldCards.Count - 1, newCards.Count);
for (int i = 0; i < newCards.Count; i++)
{
Color expected = i == currentUser ? new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f) : new Color(159 / 255f, 231 / 255f, 245 / 255f, 1f);
Assert.AreEqual(expected, newCards[i].gameObject.GetComponent<Image>().color);
}
}
/// <summary>
/// Test the user cards DeleteUser callback
/// </summary>
[UnityTest]
public IEnumerator Test_UserCardDeleteUser_Invalid()
{
yield return Setup_ChangeUserScreen_SingleUser();
// Before update
var oldCards = GameObject.FindObjectsOfType<UserCard>().ToList();
Assert.AreEqual(1, oldCards.Count);
Assert.AreEqual(new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f), oldCards[0].gameObject.GetComponent<Image>().color);
// Update
oldCards[0].DeleteUser();
var changeUserController = GameObject.FindObjectOfType<ChangeUserScreen>();
Assert.IsTrue(changeUserController.error.activeSelf);
// After update
var newCards = GameObject.FindObjectsOfType<UserCard>().ToList();
Assert.AreEqual(1, newCards.Count);
Assert.AreEqual(new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f), newCards[0].gameObject.GetComponent<Image>().color);
}
}

View File

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

View File

@@ -0,0 +1,80 @@
using NUnit.Framework;
using System.Collections;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
/// <summary>
/// Test the CourseProgressCard class
/// </summary>
[TestFixture]
public class CourseProgressCardTests
{
/// <summary>
/// Wait time between scene transitions
/// </summary>
private const float WAIT_TIME = 0.2f;
/// <summary>
/// Setup the CourseProgressCard tests
/// </summary>
[UnitySetUp]
public IEnumerator Setup_CourseProgressCard()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string course = "{\"entries\":[],\"courseIndex\":0,\"progress\":0.03846153989434242,\"completedLearnables\":1,\"inUseLearnables\":7,\"totalLearnables\":26,\"learnables\":[{\"entries\":[],\"index\":0,\"inUse\":true,\"name\":\"A\",\"progress\":3.0},{\"entries\":[],\"index\":1,\"inUse\":true,\"name\":\"B\",\"progress\":4.5},{\"entries\":[],\"index\":2,\"inUse\":true,\"name\":\"C\",\"progress\":1.5},{\"entries\":[],\"index\":3,\"inUse\":true,\"name\":\"D\",\"progress\":1.5},{\"entries\":[],\"index\":4,\"inUse\":true,\"name\":\"E\",\"progress\":1.5},{\"entries\":[],\"index\":5,\"inUse\":true,\"name\":\"F\",\"progress\":1.5},{\"entries\":[],\"index\":6,\"inUse\":true,\"name\":\"G\",\"progress\":0.0},{\"entries\":[],\"index\":7,\"inUse\":false,\"name\":\"H\",\"progress\":0.0},{\"entries\":[],\"index\":8,\"inUse\":false,\"name\":\"I\",\"progress\":0.0},{\"entries\":[],\"index\":9,\"inUse\":false,\"name\":\"J\",\"progress\":0.0},{\"entries\":[],\"index\":10,\"inUse\":false,\"name\":\"K\",\"progress\":0.0},{\"entries\":[],\"index\":11,\"inUse\":false,\"name\":\"L\",\"progress\":0.0},{\"entries\":[],\"index\":12,\"inUse\":false,\"name\":\"M\",\"progress\":0.0},{\"entries\":[],\"index\":13,\"inUse\":false,\"name\":\"N\",\"progress\":0.0},{\"entries\":[],\"index\":14,\"inUse\":false,\"name\":\"O\",\"progress\":0.0},{\"entries\":[],\"index\":15,\"inUse\":false,\"name\":\"P\",\"progress\":0.0},{\"entries\":[],\"index\":16,\"inUse\":false,\"name\":\"Q\",\"progress\":0.0},{\"entries\":[],\"index\":17,\"inUse\":false,\"name\":\"R\",\"progress\":0.0},{\"entries\":[],\"index\":18,\"inUse\":false,\"name\":\"S\",\"progress\":0.0},{\"entries\":[],\"index\":19,\"inUse\":false,\"name\":\"T\",\"progress\":0.0},{\"entries\":[],\"index\":20,\"inUse\":false,\"name\":\"U\",\"progress\":0.0},{\"entries\":[],\"index\":21,\"inUse\":false,\"name\":\"V\",\"progress\":0.0},{\"entries\":[],\"index\":22,\"inUse\":false,\"name\":\"W\",\"progress\":0.0},{\"entries\":[],\"index\":23,\"inUse\":false,\"name\":\"X\",\"progress\":0.0},{\"entries\":[],\"index\":24,\"inUse\":false,\"name\":\"Y\",\"progress\":0.0},{\"entries\":[],\"index\":25,\"inUse\":false,\"name\":\"Z\",\"progress\":0.0}]}";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[{course}]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
GameObject.FindObjectOfType<UserProgressScreen>().DisplayCourses();
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Cleanup after testing
/// </summary>
[TearDown]
public void TearDown_MinigameProgressCard()
{
PersistentDataController.PATH = null;
}
/// <summary>
/// Test whether every item that needs to be assign in the editor, is assigned
/// </summary>
[Test]
public void Test_EditorAssignments()
{
var card = GameObject.FindObjectOfType<CourseProgressCard>();
Assert.IsNotNull(card);
Assert.IsNotNull(card.button);
Assert.IsNotNull(card.courseProgress);
Assert.IsNotNull(card.courseList);
Assert.IsNotNull(card.thumbnail);
Assert.IsNotNull(card.title);
Assert.IsNotNull(card.progressBar);
}
/// <summary>
/// Test whether the card is correctly initialized
/// </summary>
[Test]
public void Test_Start()
{
var card = GameObject.FindObjectOfType<CourseProgressCard>();
Course course = card.courseList.GetCourseByIndex(card.courseProgress.courseIndex);
Assert.AreEqual(course.thumbnail, card.thumbnail.sprite);
Assert.AreEqual(course.title, card.title.text);
Assert.AreEqual(card.courseProgress.progress, card.progressBar.value);
}
}

View File

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

View File

@@ -0,0 +1,78 @@
using NUnit.Framework;
using System.Collections;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
/// <summary>
/// Test the LearnableProgressCard class
/// </summary>
[TestFixture]
public class LearnableProgressCardTests
{
/// <summary>
/// Wait time between scene transitions
/// </summary>
private const float WAIT_TIME = 0.2f;
/// <summary>
/// Setup the LearnableProgressCard tests
/// </summary>
[UnitySetUp]
public IEnumerator Setup_CourseProgressCard()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string course = "{\"entries\":[],\"courseIndex\":0,\"progress\":0.03846153989434242,\"completedLearnables\":1,\"inUseLearnables\":7,\"totalLearnables\":26,\"learnables\":[{\"entries\":[],\"index\":0,\"inUse\":true,\"name\":\"A\",\"progress\":3.0},{\"entries\":[],\"index\":1,\"inUse\":true,\"name\":\"B\",\"progress\":4.5},{\"entries\":[],\"index\":2,\"inUse\":true,\"name\":\"C\",\"progress\":1.5},{\"entries\":[],\"index\":3,\"inUse\":true,\"name\":\"D\",\"progress\":1.5},{\"entries\":[],\"index\":4,\"inUse\":true,\"name\":\"E\",\"progress\":1.5},{\"entries\":[],\"index\":5,\"inUse\":true,\"name\":\"F\",\"progress\":1.5},{\"entries\":[],\"index\":6,\"inUse\":true,\"name\":\"G\",\"progress\":0.0},{\"entries\":[],\"index\":7,\"inUse\":false,\"name\":\"H\",\"progress\":0.0},{\"entries\":[],\"index\":8,\"inUse\":false,\"name\":\"I\",\"progress\":0.0},{\"entries\":[],\"index\":9,\"inUse\":false,\"name\":\"J\",\"progress\":0.0},{\"entries\":[],\"index\":10,\"inUse\":false,\"name\":\"K\",\"progress\":0.0},{\"entries\":[],\"index\":11,\"inUse\":false,\"name\":\"L\",\"progress\":0.0},{\"entries\":[],\"index\":12,\"inUse\":false,\"name\":\"M\",\"progress\":0.0},{\"entries\":[],\"index\":13,\"inUse\":false,\"name\":\"N\",\"progress\":0.0},{\"entries\":[],\"index\":14,\"inUse\":false,\"name\":\"O\",\"progress\":0.0},{\"entries\":[],\"index\":15,\"inUse\":false,\"name\":\"P\",\"progress\":0.0},{\"entries\":[],\"index\":16,\"inUse\":false,\"name\":\"Q\",\"progress\":0.0},{\"entries\":[],\"index\":17,\"inUse\":false,\"name\":\"R\",\"progress\":0.0},{\"entries\":[],\"index\":18,\"inUse\":false,\"name\":\"S\",\"progress\":0.0},{\"entries\":[],\"index\":19,\"inUse\":false,\"name\":\"T\",\"progress\":0.0},{\"entries\":[],\"index\":20,\"inUse\":false,\"name\":\"U\",\"progress\":0.0},{\"entries\":[],\"index\":21,\"inUse\":false,\"name\":\"V\",\"progress\":0.0},{\"entries\":[],\"index\":22,\"inUse\":false,\"name\":\"W\",\"progress\":0.0},{\"entries\":[],\"index\":23,\"inUse\":false,\"name\":\"X\",\"progress\":0.0},{\"entries\":[],\"index\":24,\"inUse\":false,\"name\":\"Y\",\"progress\":0.0},{\"entries\":[],\"index\":25,\"inUse\":false,\"name\":\"Z\",\"progress\":0.0}]}";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[{course}]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
GameObject.FindObjectOfType<UserProgressScreen>().DisplayCourses();
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Cleanup after testing
/// </summary>
[TearDown]
public void TearDown_MinigameProgressCard()
{
PersistentDataController.PATH = null;
}
/// <summary>
/// Test whether every item that needs to be assign in the editor, is assigned
/// </summary>
[Test]
public void Test_EditorAssignments()
{
var card = GameObject.FindObjectOfType<LearnableProgressCard>();
Assert.IsNotNull(card);
Assert.IsNotNull(card.learnable);
Assert.IsNotNull(card.thumbnail);
Assert.IsNotNull(card.stars);
Assert.AreEqual(3, card.stars.Count);
}
/// <summary>
/// Test whether the card is correctly initialized
/// </summary>
[Test]
public void Test_Start()
{
var card = GameObject.FindObjectOfType<LearnableProgressCard>();
Assert.AreEqual(card.learnable.image, card.thumbnail.sprite);
var starRewards = new float[] { 1.0f, 2.5f, 4.0f, };
for (int i = 0; i < 3; i++)
Assert.AreEqual(starRewards[i] < card.progress.progress ? Color.white : Color.black, card.stars[i].color);
}
}

View File

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

View File

@@ -0,0 +1,120 @@
using NUnit.Framework;
using System.Collections;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
/// <summary>
/// Test the MinigameProgressCard class
/// </summary>
[TestFixture]
public class MinigameProgressCardTests
{
/// <summary>
/// Wait time between scene transitions
/// </summary>
private const float WAIT_TIME = 0.2f;
/// <summary>
/// Setup the MinigameProgressCard tests
/// </summary>
private IEnumerator Setup_MinigameProgressCard()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string minigame = "{\"entries\":[],\"minigameIndex\":1,\"latestScores\":[{\"scoreValue\":70,\"time\":\"19/04/2023 22:32:39\"},{\"scoreValue\":55,\"time\":\"20/04/2023 11:50:10\"},{\"scoreValue\":55,\"time\":\"20/04/2023 13:27:15\"}],\"highestScores\":[{\"scoreValue\":70,\"time\":\"19/04/2023 22:32:39\"},{\"scoreValue\":55,\"time\":\"20/04/2023 11:50:10\"},{\"scoreValue\":55,\"time\":\"20/04/2023 13:27:15\"}]}";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[{minigame}],\"courses\":[]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
GameObject.FindObjectOfType<UserProgressScreen>().DisplayMinigames();
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Setup the MinigameProgressCard tests
/// </summary>
private IEnumerator Setup_MinigameProgressCard_Empty()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string minigame = "{\"entries\":[],\"minigameIndex\":0,\"latestScores\":[],\"highestScores\":[]}";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[{minigame}],\"courses\":[]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
GameObject.FindObjectOfType<UserProgressScreen>().DisplayMinigames();
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Cleanup after testing
/// </summary>
[TearDown]
public void TearDown_MinigameProgressCard()
{
PersistentDataController.PATH = null;
}
/// <summary>
/// Test whether every item that needs to be assign in the editor, is assigned
/// </summary>
[UnityTest]
public IEnumerator Test_EditorAssignments()
{
yield return Setup_MinigameProgressCard();
var card = GameObject.FindObjectOfType<MinigameProgressCard>();
Assert.IsNotNull(card);
Assert.IsNotNull(card.button);
Assert.IsNotNull(card.minigameProgress);
Assert.IsNotNull(card.minigameList);
Assert.IsNotNull(card.thumbnail);
Assert.IsNotNull(card.title);
Assert.IsNotNull(card.highscore);
}
/// <summary>
/// Test whether the card is correctly initialized
/// </summary>
[UnityTest]
public IEnumerator Test_Start()
{
yield return Setup_MinigameProgressCard();
var card = GameObject.FindObjectOfType<MinigameProgressCard>();
Minigame minigame = card.minigameList.GetMinigameByIndex(card.minigameProgress.minigameIndex);
Assert.AreEqual(minigame.thumbnail, card.thumbnail.sprite);
Assert.AreEqual(minigame.title, card.title.text);
Assert.AreEqual($"TOPSCORE: {card.minigameProgress.highestScores[0].scoreValue}", card.highscore.text);
}
/// <summary>
/// Test whether an empty card is correctly initialized
/// </summary>
[UnityTest]
public IEnumerator Test_Start_Empty()
{
yield return Setup_MinigameProgressCard_Empty();
var card = GameObject.FindObjectOfType<MinigameProgressCard>();
Minigame minigame = card.minigameList.GetMinigameByIndex(card.minigameProgress.minigameIndex);
Assert.AreEqual(minigame.thumbnail, card.thumbnail.sprite);
Assert.AreEqual(minigame.title, card.title.text);
Assert.AreEqual("(NOG) GEEN TOPSCORE", card.highscore.text);
}
}

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -0,0 +1,199 @@
using NUnit.Framework;
using System.Collections;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.UI;
/// <summary>
/// Test the PanelMinigameProgress class
/// </summary>
[TestFixture]
public class PanelMinigameProgressTests
{
/// <summary>
/// Wait time between scene transitions
/// </summary>
private const float WAIT_TIME = 0.2f;
/// <summary>
/// Setup the PanelMinigameProgress tests
/// </summary>
private IEnumerator Setup_PanelMinigameProgress()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string minigame = "{\"entries\":[],\"minigameIndex\":1,\"latestScores\":[{\"scoreValue\":70,\"time\":\"19/04/2023 22:32:39\"},{\"scoreValue\":55,\"time\":\"20/04/2023 11:50:10\"},{\"scoreValue\":55,\"time\":\"20/04/2023 13:27:15\"}],\"highestScores\":[{\"scoreValue\":70,\"time\":\"19/04/2023 22:32:39\"},{\"scoreValue\":55,\"time\":\"20/04/2023 11:50:10\"},{\"scoreValue\":55,\"time\":\"20/04/2023 13:27:15\"}]}";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[{minigame}],\"courses\":[]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
GameObject.FindObjectOfType<UserProgressScreen>().DisplayMinigames();
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Setup the PanelMinigameProgress tests
/// </summary>
private IEnumerator Setup_PanelMinigameProgress_Empty()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
GameObject.FindObjectOfType<UserProgressScreen>().DisplayMinigames();
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Setup the PanelMinigameProgress tests
/// </summary>
private IEnumerator Setup_PanelMinigameProgress_NoScorePresent()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string minigame = "{\"entries\":[],\"minigameIndex\":1,\"latestScores\":[],\"highestScores\":[]}";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[{minigame}],\"courses\":[]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
GameObject.FindObjectOfType<UserProgressScreen>().DisplayMinigames();
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Setup the PanelMinigameProgress tests
/// </summary>
private IEnumerator Setup_PanelMinigameProgress_Multiple()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string minigame = "[{\"entries\":[],\"minigameIndex\":1,\"latestScores\":[{\"scoreValue\":70,\"time\":\"20/04/2023 16:08:43\"},{\"scoreValue\":20,\"time\":\"20/04/2023 16:11:53\"}],\"highestScores\":[{\"scoreValue\":70,\"time\":\"20/04/2023 16:08:43\"},{\"scoreValue\":20,\"time\":\"20/04/2023 16:11:53\"}]},{\"entries\":[],\"minigameIndex\":2,\"latestScores\":[{\"scoreValue\":194,\"time\":\"20/04/2023 15:27:41\"},{\"scoreValue\":155,\"time\":\"20/04/2023 15:28:56\"},{\"scoreValue\":84,\"time\":\"20/04/2023 15:42:10\"},{\"scoreValue\":465,\"time\":\"20/04/2023 16:14:22\"}],\"highestScores\":[{\"scoreValue\":465,\"time\":\"20/04/2023 16:14:22\"},{\"scoreValue\":194,\"time\":\"20/04/2023 15:27:41\"},{\"scoreValue\":155,\"time\":\"20/04/2023 15:28:56\"},{\"scoreValue\":84,\"time\":\"20/04/2023 15:42:10\"}]},{\"entries\":[],\"minigameIndex\":0,\"latestScores\":[{\"scoreValue\":1090,\"time\":\"20/04/2023 16:02:14\"},{\"scoreValue\":1180,\"time\":\"20/04/2023 16:07:02\"}],\"highestScores\":[{\"scoreValue\":1180,\"time\":\"20/04/2023 16:07:02\"},{\"scoreValue\":1090,\"time\":\"20/04/2023 16:02:14\"}]}]";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":{minigame},\"courses\":[]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
GameObject.FindObjectOfType<UserProgressScreen>().DisplayMinigames();
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Cleanup after testing
/// </summary>
[TearDown]
public void TearDown_PanelMinigameProgress()
{
PersistentDataController.PATH = null;
}
/// <summary>
/// Test whether every item that needs to be assign in the editor, is assigned
/// </summary>
[UnityTest]
public IEnumerator Test_EditorAssignments()
{
yield return Setup_PanelMinigameProgress();
var panel = GameObject.FindObjectOfType<PanelMinigameProgress>();
Assert.IsNotNull(panel);
Assert.IsNotNull(panel.minigameList);
Assert.IsNotNull(panel.minigameCardPrefab);
Assert.IsNotNull(panel.minigamesContainer);
Assert.IsNotNull(panel.minigameInfo);
Assert.IsNotNull(panel.emptyMinigames);
Assert.IsNotNull(panel.minigameTitle);
Assert.IsNotNull(panel.progressGraph);
Assert.IsNotNull(panel.emptyHighscore);
}
/// <summary>
/// Test whether the panel is correctly initialized
/// </summary>
[UnityTest]
public IEnumerator Test_Start()
{
yield return Setup_PanelMinigameProgress();
var panel = GameObject.FindObjectOfType<PanelMinigameProgress>();
Assert.IsTrue(panel.minigameInfo.gameObject.activeSelf);
Assert.IsFalse(panel.emptyMinigames.gameObject.activeSelf);
Assert.IsFalse(panel.emptyHighscore.gameObject.activeSelf);
Assert.IsTrue(panel.progressGraph.gameObject.activeSelf);
var minigame = panel.minigameList.GetMinigameByIndex(MinigameIndex.HANGMAN);
Assert.AreEqual(minigame.title, panel.minigameTitle.text);
}
/// <summary>
/// Test whether the panel is correctly initialized when there is no minigame progress
/// </summary>
[UnityTest]
public IEnumerator Test_Start_Empty()
{
yield return Setup_PanelMinigameProgress_Empty();
var panel = GameObject.FindObjectOfType<PanelMinigameProgress>();
Assert.IsFalse(panel.minigameInfo.gameObject.activeSelf);
Assert.IsTrue(panel.emptyMinigames.gameObject.activeSelf);
}
/// <summary>
/// Test whether the panel is correctly initialized when there are minigames, but these don't have any score
/// </summary>
[UnityTest]
public IEnumerator Test_Start_NoScorePresent()
{
yield return Setup_PanelMinigameProgress_NoScorePresent();
var panel = GameObject.FindObjectOfType<PanelMinigameProgress>();
Assert.IsTrue(panel.minigameInfo.gameObject.activeSelf);
Assert.IsFalse(panel.emptyMinigames.gameObject.activeSelf);
Assert.IsTrue(panel.emptyHighscore.gameObject.activeSelf);
Assert.IsFalse(panel.progressGraph.gameObject.activeSelf);
}
/// <summary>
/// Test whether we can select a minigame
/// </summary>
[UnityTest]
public IEnumerator Test_UpdateSelection()
{
yield return Setup_PanelMinigameProgress_Multiple();
var panel = GameObject.FindObjectOfType<PanelMinigameProgress>();
var minigames = GameObject.FindObjectsOfType<MinigameProgressCard>();
var currentMinigame = minigames[0];
currentMinigame.selectActivity.Invoke();
yield return new WaitForSeconds(WAIT_TIME);
Assert.AreEqual(new Color(66 / 255f, 158 / 255f, 189 / 255f, 1f), currentMinigame.GetComponent<Image>().color);
for (int i = 1; i < minigames.Length; i++)
Assert.AreEqual(new Color(159 / 255f, 231 / 255f, 245 / 255f, 120 / 255f), minigames[i].GetComponent<Image>().color);
Assert.AreEqual(currentMinigame.title.text, panel.minigameTitle.text);
}
}

View File

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

View File

@@ -0,0 +1,152 @@
using NUnit.Framework;
using System;
using System.Collections;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
/// <summary>
/// Test the ProgressGraph class
/// </summary>
[TestFixture]
public class ProgressGraphTests
{
/// <summary>
/// Wait time between scene transitions
/// </summary>
private const float WAIT_TIME = 0.2f;
/// <summary>
/// Setup the ProgressGraph tests
/// </summary>
private IEnumerator Setup_ProgressGraph(int amount, Func<int, int> f)
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string score = "";
for (int i = 0; i < amount; i++)
{
score += $"{{\"scoreValue\":{f(i)},\"time\":\"19/04/2023 22:32:39\"}}";
if (i < amount - 1)
score += ",";
}
string minigame = $"{{\"entries\":[],\"minigameIndex\":1,\"latestScores\":[{score}],\"highestScores\":[{score}]}}";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[{minigame}],\"courses\":[]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
GameObject.FindObjectOfType<UserProgressScreen>().DisplayMinigames();
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Test whether every item that needs to be assign in the editor, is assigned
/// </summary>
[UnityTest]
public IEnumerator Test_EditorAssignments()
{
yield return Setup_ProgressGraph(1, (i) => i);
var graph = GameObject.FindObjectOfType<ProgressGraph>();
Assert.IsNotNull(graph);
Assert.IsNotNull(graph.progressGraph);
Assert.IsNotNull(graph.highscoreMarker);
Assert.IsNotNull(graph.axesTickMarker);
}
/// <summary>
/// Test whether negative values are correctly plotted
/// </summary>
[UnityTest]
public IEnumerator Test_PlotGraph_Negative()
{
yield return Setup_ProgressGraph(4, (i) => -5 * (i + 1));
yield return new WaitForSeconds(5.0f);
Assert.IsTrue(true);
}
/// <summary>
/// Test whether values between 0 and 1 are correctly plotted
/// </summary>
[UnityTest]
public IEnumerator Test_PlotGraph_SmallerThen1()
{
yield return Setup_ProgressGraph(4, (i) => (3 * i + 20) / 100);
yield return new WaitForSeconds(5.0f);
Assert.IsTrue(true);
}
/// <summary>
/// Test whether values around 0 are correctly plotted
/// </summary>
[UnityTest]
public IEnumerator Test_PlotGraph_AroundZero()
{
yield return Setup_ProgressGraph(4, (i) => 5 * i - 10);
yield return new WaitForSeconds(5.0f);
Assert.IsTrue(true);
}
/// <summary>
/// Test whether all 0 values are correctly plotted
/// </summary>
[UnityTest]
public IEnumerator Test_PlotGraph_AllZeros()
{
yield return Setup_ProgressGraph(4, (i) => 0);
yield return new WaitForSeconds(5.0f);
Assert.IsTrue(true);
}
/// <summary>
/// Test whether values between -1 and 1 are correctly plotted
/// </summary>
[UnityTest]
public IEnumerator Test_PlotGraph_BetweenPos1AndNeg1()
{
yield return Setup_ProgressGraph(10, (i) => (i % 3) - 1);
yield return new WaitForSeconds(5.0f);
Assert.IsTrue(true);
}
/// <summary>
/// Test whether a single value is correctly plotted
/// </summary>
[UnityTest]
public IEnumerator Test_PlotGraph_Single()
{
yield return Setup_ProgressGraph(1, (i) => i + 500);
yield return new WaitForSeconds(5.0f);
Assert.IsTrue(true);
}
/// <summary>
/// Test whether multiple values are correctly plotted
/// </summary>
[UnityTest]
public IEnumerator Test_PlotGraph_Multiple()
{
yield return Setup_ProgressGraph(5, (i) => 5 * i);
yield return new WaitForSeconds(5.0f);
Assert.IsTrue(true);
}
/// <summary>
/// Test whether too many values (capped at 10) are correctly plotted
/// </summary>
[UnityTest]
public IEnumerator Test_PlotGraph_TooMany()
{
yield return Setup_ProgressGraph(10, (i) => 5 * i);
yield return new WaitForSeconds(5.0f);
Assert.IsTrue(true);
}
}

View File

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

View File

@@ -0,0 +1,71 @@
using NUnit.Framework;
using System.Collections;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
/// <summary>
/// Test the UserCard class
/// </summary>
[TestFixture]
public class UserCardTests
{
/// <summary>
/// Wait time between scene transitions
/// </summary>
private const float WAIT_TIME = 0.2f;
/// <summary>
/// Setup the tests
/// </summary>
[UnitySetUp]
public IEnumerator Setup_UserCard()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string oneUser = "{\"version\":1537,\"users\":[{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/ChangeUserScreen");
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Cleanup after testing
/// </summary>
[TearDown]
public void TearDown_UserCard()
{
PersistentDataController.PATH = null;
}
/// <summary>
/// Test whether every item that needs to be assign in the editor, is assigned
/// </summary>
[Test]
public void Test_EditorAssignments()
{
var card = GameObject.FindObjectOfType<UserCard>();
Assert.IsNotNull(card);
Assert.IsNotNull(card.user);
Assert.IsNotNull(card.button);
Assert.IsNotNull(card.avatar);
Assert.IsNotNull(card.username);
}
/// <summary>
/// Test whether the card is correctly initialized
/// </summary>
[Test]
public void Test_Start()
{
var card = GameObject.FindObjectOfType<UserCard>();
Assert.AreEqual(card.user.GetAvatar(), card.avatar.sprite);
Assert.AreEqual(card.user.GetUsername(), card.username.text);
}
}

View File

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

View File

@@ -0,0 +1,173 @@
using NUnit.Framework;
using System.Collections;
using System.IO;
using System.Linq;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.UI;
/// <summary>
/// Test the UserCreationScreen class
/// </summary>
[TestFixture]
public class UserCreationScreenTests
{
/// <summary>
/// Wait time between scene transitions
/// </summary>
private const float WAIT_TIME = 0.2f;
/// <summary>
/// Setup the UserCreationScreen tests
/// </summary>
private IEnumerator Setup_UserCreationScreen(string startScreen = "Accounts/Scenes/UserCreationScreen")
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string oneUser = "{\"version\":1537,\"users\":[{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[],\"courses\":[]}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene(startScreen);
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Cleanup after testing
/// </summary>
[TearDown]
public void TearDown_UserCreationScreen()
{
PersistentDataController.PATH = null;
}
/// <summary>
/// Test whether every item that needs to be assign in the editor, is assigned
/// </summary>
[UnityTest]
public IEnumerator Test_EditorAssignments()
{
yield return Setup_UserCreationScreen();
var userCreationController = GameObject.FindObjectOfType<UserCreationScreen>();
Assert.IsNotNull(userCreationController);
Assert.IsNotNull(userCreationController.errorMessage);
Assert.IsNotNull(userCreationController.inputName);
Assert.IsNotNull(userCreationController.avatarsContainer);
Assert.IsNotNull(userCreationController.avatarPrefab);
Assert.IsNotNull(userCreationController.backButton);
Assert.IsTrue(UserCreationScreen.canGoBack);
}
/// <summary>
/// Test whether the screen is correctly initialized
/// </summary>
[UnityTest]
public IEnumerator Test_Start()
{
yield return Setup_UserCreationScreen();
var userCreationController = GameObject.FindObjectOfType<UserCreationScreen>();
Assert.IsFalse(userCreationController.errorMessage.activeSelf);
Assert.IsTrue(userCreationController.backButton.activeSelf);
var avatars = userCreationController.avatarsContainer.GetComponentsInChildren<Button>().ToList()
.ConvertAll((b) => b.GetComponentsInChildren<Image>().Except(b.GetComponents<Image>()).First());
Assert.AreEqual(UserList.AVATARS.Count, avatars.Count);
for (int i = 0; i < avatars.Count; i++)
Assert.AreEqual(UserList.AVATARS[i], avatars[i].sprite);
}
/// <summary>
/// Test whether the screen is correctly initialized (from invalid savefile)
/// </summary>
[UnityTest]
public IEnumerator Test_Start_InvalidSavefile()
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
File.Delete(path);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Common/Scenes/Boot");
yield return new WaitForSeconds(WAIT_TIME);
var userCreationController = GameObject.FindObjectOfType<UserCreationScreen>();
Assert.IsNotNull(userCreationController);
Assert.IsFalse(userCreationController.backButton.activeSelf);
}
/// <summary>
/// Test whether a new user can be created
/// </summary>
[UnityTest]
public IEnumerator Test_CreateUser_Valid()
{
yield return Setup_UserCreationScreen("Common/Scenes/MainMenuScreen");
SystemController.GetInstance().LoadNextScene("Accounts/Scenes/UserCreationScreen");
yield return new WaitForSeconds(WAIT_TIME);
var userCreationController = GameObject.FindObjectOfType<UserCreationScreen>();
userCreationController.inputName.text = "newTester";
userCreationController.CreateUser();
Assert.IsFalse(userCreationController.errorMessage.activeSelf);
yield return new WaitForSeconds(WAIT_TIME);
Assert.AreEqual(SystemController.GetSceneIndex("Common/Scenes/MainMenuScreen"), SystemController.GetInstance().currentScene);
}
/// <summary>
/// Test whether no new user is created when the username is too long
/// </summary>
[UnityTest]
public IEnumerator Test_CreateUser_UsernameTooLong()
{
yield return Setup_UserCreationScreen();
var userCreationController = GameObject.FindObjectOfType<UserCreationScreen>();
userCreationController.inputName.text = "aninvalidsuperduperlongusername";
userCreationController.CreateUser();
Assert.IsTrue(userCreationController.errorMessage.activeSelf);
Assert.AreEqual("Je gebruikersnaam moet bestaan uit minimum 1 en maximum 12 letters (a-z en A-Z) of cijfers (0-9).", userCreationController.errorMessage.GetComponent<TMP_Text>().text);
}
/// <summary>
/// Test whether no new user is created when the username contains invalid characters
/// </summary>
[UnityTest]
public IEnumerator Test_CreateUser_UsernameInvalidChars()
{
yield return Setup_UserCreationScreen();
var userCreationController = GameObject.FindObjectOfType<UserCreationScreen>();
userCreationController.inputName.text = "! an invalid username !";
userCreationController.CreateUser();
Assert.IsTrue(userCreationController.errorMessage.activeSelf);
Assert.AreEqual("Je gebruikersnaam moet bestaan uit minimum 1 en maximum 12 letters (a-z en A-Z) of cijfers (0-9).", userCreationController.errorMessage.GetComponent<TMP_Text>().text);
}
/// <summary>
/// Test whether no new user is created when the username already exists
/// </summary>
[UnityTest]
public IEnumerator Test_CreateUser_UsernameAlreadyExists()
{
yield return Setup_UserCreationScreen();
var userCreationController = GameObject.FindObjectOfType<UserCreationScreen>();
userCreationController.inputName.text = "Tester0";
userCreationController.CreateUser();
Assert.IsTrue(userCreationController.errorMessage.activeSelf);
Assert.AreEqual("Deze gebruikersnaam bestaat al! Kies een andere.", userCreationController.errorMessage.GetComponent<TMP_Text>().text);
}
}

View File

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

View File

@@ -0,0 +1,138 @@
using NUnit.Framework;
using System.Collections;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
/// <summary>
/// Test the UserProgressScreen class
/// </summary>
[TestFixture]
public class UserProgressScreenTests
{
/// <summary>
/// Wait time between scene transitions
/// </summary>
private const float WAIT_TIME = 0.2f;
/// <summary>
/// Setup the UserProgressScreen tests
/// </summary>
private IEnumerator Setup_UserProgressScreen(bool progress = true)
{
string path = $"{Application.persistentDataPath}/wesign_unit_test.json";
string course = "{\"entries\":[],\"courseIndex\":0,\"progress\":0.03846153989434242,\"completedLearnables\":1,\"inUseLearnables\":7,\"totalLearnables\":26,\"learnables\":[{\"entries\":[],\"index\":0,\"inUse\":true,\"name\":\"A\",\"progress\":3.0},{\"entries\":[],\"index\":1,\"inUse\":true,\"name\":\"B\",\"progress\":4.5},{\"entries\":[],\"index\":2,\"inUse\":true,\"name\":\"C\",\"progress\":1.5},{\"entries\":[],\"index\":3,\"inUse\":true,\"name\":\"D\",\"progress\":1.5},{\"entries\":[],\"index\":4,\"inUse\":true,\"name\":\"E\",\"progress\":1.5},{\"entries\":[],\"index\":5,\"inUse\":true,\"name\":\"F\",\"progress\":1.5},{\"entries\":[],\"index\":6,\"inUse\":true,\"name\":\"G\",\"progress\":0.0},{\"entries\":[],\"index\":7,\"inUse\":false,\"name\":\"H\",\"progress\":0.0},{\"entries\":[],\"index\":8,\"inUse\":false,\"name\":\"I\",\"progress\":0.0},{\"entries\":[],\"index\":9,\"inUse\":false,\"name\":\"J\",\"progress\":0.0},{\"entries\":[],\"index\":10,\"inUse\":false,\"name\":\"K\",\"progress\":0.0},{\"entries\":[],\"index\":11,\"inUse\":false,\"name\":\"L\",\"progress\":0.0},{\"entries\":[],\"index\":12,\"inUse\":false,\"name\":\"M\",\"progress\":0.0},{\"entries\":[],\"index\":13,\"inUse\":false,\"name\":\"N\",\"progress\":0.0},{\"entries\":[],\"index\":14,\"inUse\":false,\"name\":\"O\",\"progress\":0.0},{\"entries\":[],\"index\":15,\"inUse\":false,\"name\":\"P\",\"progress\":0.0},{\"entries\":[],\"index\":16,\"inUse\":false,\"name\":\"Q\",\"progress\":0.0},{\"entries\":[],\"index\":17,\"inUse\":false,\"name\":\"R\",\"progress\":0.0},{\"entries\":[],\"index\":18,\"inUse\":false,\"name\":\"S\",\"progress\":0.0},{\"entries\":[],\"index\":19,\"inUse\":false,\"name\":\"T\",\"progress\":0.0},{\"entries\":[],\"index\":20,\"inUse\":false,\"name\":\"U\",\"progress\":0.0},{\"entries\":[],\"index\":21,\"inUse\":false,\"name\":\"V\",\"progress\":0.0},{\"entries\":[],\"index\":22,\"inUse\":false,\"name\":\"W\",\"progress\":0.0},{\"entries\":[],\"index\":23,\"inUse\":false,\"name\":\"X\",\"progress\":0.0},{\"entries\":[],\"index\":24,\"inUse\":false,\"name\":\"Y\",\"progress\":0.0},{\"entries\":[],\"index\":25,\"inUse\":false,\"name\":\"Z\",\"progress\":0.0}]}";
string minigame = "{\"entries\":[],\"minigameIndex\":1,\"latestScores\":[{\"scoreValue\":70,\"time\":\"19/04/2023 22:32:39\"},{\"scoreValue\":55,\"time\":\"20/04/2023 11:50:10\"},{\"scoreValue\":55,\"time\":\"20/04/2023 13:27:15\"}],\"highestScores\":[{\"scoreValue\":70,\"time\":\"19/04/2023 22:32:39\"},{\"scoreValue\":55,\"time\":\"20/04/2023 11:50:10\"},{\"scoreValue\":55,\"time\":\"20/04/2023 13:27:15\"}]}";
if (!progress) course = minigame = "";
string oneUser = $"{{\"version\":1537,\"users\":[{{\"entries\":[],\"username\":\"Tester0\",\"avatarIndex\":0,\"playtime\":0.0,\"minigames\":[{minigame}],\"courses\":[{course}]}}],\"currentUser\":0,\"currentMinigame\":0,\"currentCourse\":0,\"currentTheme\":0,\"useGPU\":false}}";
File.WriteAllText(path, oneUser);
PersistentDataController.PATH = path;
PersistentDataController.GetInstance().Load();
AssetDatabase.LoadAssetAtPath<UserAvatarList>("Assets/Accounts/ScriptableObjects/UserAvatarList.asset").Awake();
SystemController.GetInstance().SwapScene("Accounts/Scenes/UserProgressScreen");
yield return new WaitForSeconds(WAIT_TIME);
}
/// <summary>
/// Cleanup after testing
/// </summary>
[TearDown]
public void TearDown_UserProgressScreen()
{
PersistentDataController.PATH = null;
}
/// <summary>
/// Test whether every item that needs to be assign in the editor, is assigned
/// </summary>
[UnityTest]
public IEnumerator Test_EditorAssignments()
{
yield return Setup_UserProgressScreen();
var userProgressController = GameObject.FindObjectOfType<UserProgressScreen>();
Assert.IsNotNull(userProgressController.username);
Assert.IsNotNull(userProgressController.avatar);
Assert.IsNotNull(userProgressController.coursesPanel);
Assert.IsNotNull(userProgressController.coursesTabButton);
Assert.IsNotNull(userProgressController.minigamesPanel);
Assert.IsNotNull(userProgressController.minigamesTabButton);
}
/// <summary>
/// Test whether the screen is correctly initialized
/// </summary>
[UnityTest]
public IEnumerator Test_Start_NoProgress()
{
yield return Setup_UserProgressScreen(false);
var userProgressController = GameObject.FindObjectOfType<UserProgressScreen>();
var user = UserList.GetCurrentUser();
Assert.AreEqual(user.GetUsername(), userProgressController.username.text);
Assert.AreEqual(user.GetAvatar(), userProgressController.avatar.sprite);
Assert.IsTrue(userProgressController.coursesPanel.activeSelf);
Assert.Zero(GameObject.FindObjectsOfType<CourseProgressCard>().Length);
Assert.IsFalse(userProgressController.minigamesPanel.activeSelf);
}
/// <summary>
/// Test whether the screen is correctly initialized
/// </summary>
[UnityTest]
public IEnumerator Test_Start_WithProgress()
{
yield return Setup_UserProgressScreen();
var userProgressController = GameObject.FindObjectOfType<UserProgressScreen>();
var user = UserList.GetCurrentUser();
Assert.AreEqual(user.GetUsername(), userProgressController.username.text);
Assert.AreEqual(user.GetAvatar(), userProgressController.avatar.sprite);
Assert.IsTrue(userProgressController.coursesPanel.activeSelf);
var courses = GameObject.FindObjectsOfType<CourseProgressCard>().ToList();
Assert.AreEqual(user.GetCourses().Count, courses.Count);
Assert.IsFalse(userProgressController.minigamesPanel.activeSelf);
}
/// <summary>
/// Test whether the course panel is displayed
/// </summary>
[UnityTest]
public IEnumerator Test_DisplayCourses()
{
yield return Setup_UserProgressScreen();
var userProgressController = GameObject.FindObjectOfType<UserProgressScreen>();
userProgressController.DisplayCourses();
yield return new WaitForSeconds(WAIT_TIME);
Assert.IsTrue(userProgressController.coursesPanel.activeSelf);
Assert.IsFalse(userProgressController.minigamesPanel.activeSelf);
}
/// <summary>
/// Test whether the minigames panel is displayed
/// </summary>
/// <returns></returns>
[UnityTest]
public IEnumerator Test_DisplayMinigames()
{
yield return Setup_UserProgressScreen();
var userProgressController = GameObject.FindObjectOfType<UserProgressScreen>();
userProgressController.DisplayMinigames();
yield return new WaitForSeconds(WAIT_TIME);
Assert.IsFalse(userProgressController.coursesPanel.activeSelf);
Assert.IsTrue(userProgressController.minigamesPanel.activeSelf);
}
}

View File

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

View File

@@ -1,233 +0,0 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using UnityEngine;
/// <summary>
/// Test the Progress class
/// </summary>
public class TestProgress
{
/// <summary>
/// A dummy serializable struct to perform test operations on
/// </summary>
[Serializable]
private struct SerializableStruct
{
public int r, g, b;
public float x, y, z;
}
/// <summary>
/// A dummy non-serializable struct to perform test operations on
/// </summary>
private struct NonSerializableStruct
{
public int r, g, b;
public float x, y, z;
}
/// <summary>
/// Helper method
/// </summary>
/// <returns><c>true</c> if <c>Progress.AddOrUpdate(...)</c> throws a <c>SerializationException</c></returns>
private bool AddNonSerializableStruct()
{
Progress progress = new Progress();
NonSerializableStruct nss = new NonSerializableStruct();
try { progress.AddOrUpdate<NonSerializableStruct>("key", nss); }
catch (SerializationException) { return true; }
return false;
}
/// <summary>
/// Helper method
/// </summary>
/// <returns><c>true</c> if <c>Progress.Get(...)</c> throws a <c>KeyNotFoundException</c></returns>
private bool AccessInvalidKey()
{
Progress progress = new Progress();
try { progress.Get<int>("non-existing key"); }
catch (KeyNotFoundException) { return true; }
return false;
}
/// <summary>
/// Helper method
/// </summary>
/// <returns><c>true</c> if <c>Progress.Get(...)</c> throws a <c>InvalidCastException</c></returns>
private bool AccessInvalidType()
{
Progress progress = new Progress();
progress.AddOrUpdate<int>("key", 123456789);
try { progress.Get<double>("key"); }
catch (InvalidCastException) { return true; }
return false;
}
/// <summary>
/// Test for creation of a new progress
/// </summary>
[Test]
public void TestNewProgress()
{
Progress progress = new Progress();
Debug.Assert(progress != null);
}
/// <summary>
/// Test whether invalid data will not be added
/// </summary>
[Test]
public void TestProgressAddInvalidData()
{
Progress progress = new Progress();
Debug.Assert(!progress.AddOrUpdate<GameObject>("key", null));
}
/// <summary>
/// Test whether a duplicated key will be added
/// </summary>
[Test]
public void TestProgressAddDuplicateKey()
{
Progress progress = new Progress();
progress.AddOrUpdate<int>("key 1", 0);
Debug.Assert(progress.AddOrUpdate<int>("key 1", 1));
}
/// <summary>
/// Test whether a <c>int</c> value can be added
/// </summary>
[Test]
public void TestProgressAddInt()
{
Progress progress = new Progress();
Debug.Assert(progress.AddOrUpdate<int>("key", 1));
}
/// <summary>
/// Test whether a <c>double</c> value can be added
/// </summary>
[Test]
public void TestProgressAddDouble()
{
Progress progress = new Progress();
Debug.Assert(progress.AddOrUpdate<double>("key", 1.0));
}
/// <summary>
/// Test whether a <c>string</c> value can be added
/// </summary>
[Test]
public void TestProgressAddString()
{
Progress progress = new Progress();
Debug.Assert(progress.AddOrUpdate<string>("key", "Hello World!"));
}
/// <summary>
/// Test whether a serializable struct can be added
/// </summary>
[Test]
public void TestProgressAddSerializableStruct()
{
Progress progress = new Progress();
Debug.Assert(progress.AddOrUpdate<SerializableStruct>("key", new SerializableStruct()));
}
/// <summary>
/// Test whether a non-serializable struct will throw an error
/// </summary>
[Test]
public void TestProgressAddNonSerializableStruct()
{
Debug.Assert(AddNonSerializableStruct());
}
/// <summary>
/// Test whether an invalid key will throw an error
/// </summary>
[Test]
public void TestProgressGetInvalidKey()
{
Debug.Assert(AccessInvalidKey());
}
/// <summary>
/// Test whether an invalid type will throw an error
/// </summary>
[Test]
public void TestProgressGetInvalidType()
{
Debug.Assert(AccessInvalidType());
}
/// <summary>
/// Test whether a value is correctly updated
/// </summary>
[Test]
public void TestProgressUpdate()
{
Progress progress = new Progress();
progress.AddOrUpdate<int>("key", 1);
Debug.Assert(progress.Get<int>("key") == 1);
progress.AddOrUpdate<int>("key", 2);
Debug.Assert(progress.Get<int>("key") == 2);
}
/// <summary>
/// Test whether a <c>int</c> value can be read
/// </summary>
[Test]
public void TestProgressGetInt()
{
Progress progress = new Progress();
progress.AddOrUpdate<int>("key", 1);
Debug.Assert(progress.Get<int>("key") == 1);
}
/// <summary>
/// Test whether a <c>double</c> value can be read
/// </summary>
[Test]
public void TestProgressGetDouble()
{
Progress progress = new Progress();
progress.AddOrUpdate<double>("key", 1.0);
Debug.Assert(progress.Get<double>("key") == 1.0);
}
/// <summary>
/// Test whether a <c>string</c> value can be read
/// </summary>
[Test]
public void TestProgressGetString()
{
Progress progress = new Progress();
progress.AddOrUpdate<string>("key", "Hello World!");
Debug.Assert(progress.Get<string>("key") == "Hello World!");
}
/// <summary>
/// Test whether a serializable struct can be read
/// </summary>
[Test]
public void TestProgressGetStruct()
{
Progress progress = new Progress();
int R = 1, G = 10, B = 100;
float X = 0.1f, Y = 0.01f, Z = 0.001f;
SerializableStruct data = new SerializableStruct { r = R, g = G, b = B, x = X, y = Y, z = Z };
progress.AddOrUpdate<SerializableStruct>("key", data);
SerializableStruct result = progress.Get<SerializableStruct>("key");
Debug.Assert(result.r == R);
Debug.Assert(result.g == G);
Debug.Assert(result.b == B);
Debug.Assert(result.x == X);
Debug.Assert(result.y == Y);
Debug.Assert(result.z == Z);
}
}

View File

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

View File

@@ -1,30 +0,0 @@
using UnityEngine;
/// <summary>
/// Test the UserCreationScreen class
/// </summary>
public class TestUserCreationScreen
{
/// <summary>
/// Tets IsValidUsername will return <c>true</c> for an valid username
/// </summary>
public void TestIsValidUsernameTrue()
{
foreach (char c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
Debug.Assert(UserCreationScreen.IsValidUsername(c.ToString()));
Debug.Assert(UserCreationScreen.IsValidUsername("123456789AbC"));
}
/// <summary>
/// Tets IsValidUsername will return <c>false</c> for an invalid username
/// </summary>
public void TestIsValidUsernameFalse()
{
Debug.Assert(!UserCreationScreen.IsValidUsername(string.Empty));
foreach (char c in " \n\t+-*/%_.,;:!?(){}[]\\'\"|&~^$")
Debug.Assert(!UserCreationScreen.IsValidUsername(c.ToString()));
Debug.Assert(!UserCreationScreen.IsValidUsername("123456789_10_11_12_13"));
}
}

View File

@@ -1,150 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Test the User class
/// </summary>
public class TestUser
{
/// <summary>
/// Test for the creation of a new user
/// </summary>
public void TestNewUser()
{
User user = new User();
Debug.Assert(user != null);
Debug.Assert(user.courses.Count == 0);
Debug.Assert(user.minigames.Count == 0);
}
/// <summary>
/// Test whether progress on a new course can be added
/// </summary>
public void TestUserAddCourse()
{
User user = new User();
Progress p = new Progress();
user.courses.Add(p);
Debug.Assert(user.courses.Count == 1);
Debug.Assert(user.minigames.Count == 0);
}
/// <summary>
/// Test whether progress on a new minigame can be added
/// </summary>
public void TestUserAddMinigame()
{
User user = new User();
Progress p = new Progress();
user.minigames.Add(p);
Debug.Assert(user.courses.Count == 0);
Debug.Assert(user.minigames.Count == 1);
}
/// <summary>
/// Test GetRecentCourses will return empty when no progress is stored
/// </summary>
public void TestGetRecentCoursesEmpty()
{
User user = new User();
Debug.Assert(user.GetRecentCourses().Count == 0);
}
/// <summary>
/// Temporary test for GetRecentCourses will return all progress that is stored
/// </summary>
public void TestGetRecentCoursesAll()
{
User user = new User();
Progress p = new Progress();
p.AddOrUpdate<CourseIndex>("courseIndex", CourseIndex.FINGERSPELLING);
p.AddOrUpdate<float>("courseProgress", 0.5f);
user.courses.Add(p);
List<Tuple<CourseIndex, float>> list = user.GetRecentCourses();
Debug.Assert(list.Count == 1);
Debug.Assert(list[0].Item1 == CourseIndex.FINGERSPELLING);
Debug.Assert(list[0].Item2 == 0.5f);
}
/// <summary>
/// Test GetRecommendedCourses will return <c>Tuple<CourseIndex.FINGERSPELLING, 0.0></c> when no progress is stored
/// </summary>
public void TestGetRecommendedCoursesEmpty()
{
User user = new User();
List<Tuple<CourseIndex, float>> list = user.GetRecommendedCourses();
Debug.Assert(list.Count == 1);
Debug.Assert(list[0].Item1 == CourseIndex.FINGERSPELLING);
Debug.Assert(list[0].Item2 == 0.0f);
}
/// <summary>
/// Temporary test for GetRecommenedCourses will return all progress that is stored
/// </summary>
public void TestGetRecommendedCoursesAll()
{
User user = new User();
Progress p = new Progress();
p.AddOrUpdate<CourseIndex>("courseIndex", CourseIndex.FINGERSPELLING);
p.AddOrUpdate<float>("courseProgress", 0.5f);
user.courses.Add(p);
List<Tuple<CourseIndex, float>> list = user.GetRecommendedCourses();
Debug.Assert(list.Count == 1);
Debug.Assert(list[0].Item1 == CourseIndex.FINGERSPELLING);
Debug.Assert(list[0].Item2 == 0.5f);
}
/// <summary>
/// Test GetCourseProgress returns null when course cannot be found
/// </summary>
public void TestGetCourseProgressNull()
{
User user = new User();
Debug.Assert(user.GetCourseProgress(CourseIndex.FINGERSPELLING) == null);
Debug.Assert(user.GetCourseProgress((CourseIndex)100) == null);
}
/// <summary>
/// Test GetCourseProgress returns correct progress object
/// </summary>
public void TestGetCourseProgressValid()
{
User user = new User();
Progress p = new Progress();
p.AddOrUpdate<CourseIndex>("courseIndex", CourseIndex.FINGERSPELLING);
p.AddOrUpdate<float>("courseProgress", 3.14159265f);
user.courses.Add(p);
Progress q = user.GetCourseProgress(CourseIndex.FINGERSPELLING);
Debug.Assert(q.Get<CourseIndex>("courseIndex") == CourseIndex.FINGERSPELLING);
Debug.Assert(q.Get<float>("courseProgress") == 3.14159265f);
}
/// <summary>
/// Test GetMinigameProgress returns null when minigame cannot be found
/// </summary>
public void TestGetMinigameProgressNull()
{
User user = new User();
Debug.Assert(user.GetMinigameProgress(MinigameIndex.SPELLING_BEE) == null);
Debug.Assert(user.GetMinigameProgress((MinigameIndex)100) == null);
Progress p = new Progress();
p.AddOrUpdate<MinigameIndex>("minigameIndex", MinigameIndex.SPELLING_BEE);
user.minigames.Add(p);
Debug.Assert(user.GetMinigameProgress(MinigameIndex.HANGMAN) == null);
}
/// <summary>
/// Test GetMinigameProgress returns correct progress object
/// </summary>
public void TestGetMinigameProgressValid()
{
User user = new User();
Progress p = new Progress();
p.AddOrUpdate<MinigameIndex>("minigameIndex", MinigameIndex.SPELLING_BEE);
user.minigames.Add(p);
Progress q = user.GetMinigameProgress(MinigameIndex.SPELLING_BEE);
Debug.Assert(q.Get<CourseIndex>("minigameIndex") == CourseIndex.FINGERSPELLING);
}
}

8
Assets/Architecture.meta Normal file
View File

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

View File

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

View File

@@ -0,0 +1,14 @@
{
"name": "ArchitectureScripts",
"rootNamespace": "",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

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

View File

@@ -5,6 +5,7 @@
public enum CourseIndex
{
FINGERSPELLING,
BASIC_SIGNS,
CLOTHING,
ANIMALS,
FOOD,

View File

@@ -0,0 +1,674 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
/// <summary>
/// PersistentDataController singleton
/// </summary>
public class PersistentDataController
{
/// <summary>
/// The instance controlling the singleton
/// </summary>
private static PersistentDataController instance = null;
/// <summary>
/// Current implementation version of the PersistentDataController
/// </summary>
/// <remarks>MSB represent sprint version, LSB represent subversion</remarks>
public const int VERSION = 0x06_01;
/// <summary>
/// Path of the <c>.json</c>-file to store all serialized data
/// </summary>
public static string PATH = null;
/// <summary>
/// Class to hold a list of data records
/// </summary>
[Serializable]
public class PersistentDataContainer
{
/// <summary>
/// A helper class for handling the stored progress
/// </summary>
[Serializable]
public class PersistentDataEntry
{
/// <summary>
/// The key, used to reference the data object
/// </summary>
public string key;
/// <summary>
/// The object, representated as a list of byte (which can be serialized)
/// </summary>
public List<byte> data = new List<byte>();
/// <summary>
/// Create a new PersistentDataEntry
/// </summary>
/// <param name="key"></param>
/// <param name="data"></param>
public PersistentDataEntry(string key, byte[] data) : this(key, data.ToList())
{ }
/// <summary>
/// Create a new PersistentDataEntry
/// </summary>
public PersistentDataEntry(string key, List<byte> data)
{
this.key = key;
this.data = data;
}
}
/// <summary>
/// List of data records
/// </summary>
public List<PersistentDataEntry> entries = new List<PersistentDataEntry>();
/// <summary>
/// Update the value of a certain key,
/// or add a new value if the key was not present.
/// </summary>
/// <typeparam name="T">The type of the data to be added/updated</typeparam>
/// <param name="key">The key, used for referencing the data</param>
/// <param name="data">The object of type <typeparamref name="T"/></param>
/// <returns><c>true</c> if successful, <c>false</c> otherwise</returns>
public bool Set<T>(string key, T data)
{
if (data == null)
return false;
PersistentDataEntry entry = entries.Find(x => x.key == key);
// Hacky serialization stuff
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, data);
if (entry != null)
{
entry.data.Clear();
entry.data.AddRange(ms.ToArray());
}
else
{
entries.Add(new PersistentDataEntry(key, ms.ToArray()));
}
return true;
}
}
/// <summary>
/// Get the data object of a certain key
/// </summary>
/// <typeparam name="T">The type of the data object</typeparam>
/// <param name="key">The key referencing the data object</param>
/// <returns>The data, cast to a type <typeparamref name="T"/></returns>
/// <exception cref="KeyNotFoundException"></exception>
/// <exception cref="InvalidCastException"></exception>
public T Get<T>(string key)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
// Find the correct key
foreach (PersistentDataEntry entry in entries)
{
if (entry.key == key)
{
// Hacky serialization stuff
byte[] data = entry.data.ToArray();
ms.Write(data, 0, data.Length);
ms.Seek(0, SeekOrigin.Begin);
return (T)bf.Deserialize(ms);
}
}
}
// Raise an exception when key is not found
throw new KeyNotFoundException();
}
/// <summary>
/// Remove a key-value from the data.
/// </summary>
/// <param name="key">The key referencing the data object</param>
/// <exception cref="KeyNotFoundException"></exception>
public void Remove(string key)
{
if (!Has(key))
throw new KeyNotFoundException();
entries.Remove(entries.Find(x => x.key == key));
}
/// <summary>
/// Remove and return value from the data.
/// </summary>
/// <typeparam name="T">The type of the data object</typeparam>
/// <param name="key">The key referencing the data object</param>
/// <param name="save">Whether the removal of the data should also be saved to disk</param>
/// <returns></returns>
public T Pop<T>(string key)
{
T data = Get<T>(key);
Remove(key);
return data;
}
/// <summary>
/// Check whether a key is present
/// </summary>
/// <param name="key">The key to check</param>
/// <returns>true if a item can be found with the specified key</returns>
public bool Has(string key)
{
return entries.Find(x => x.key == key) != null;
}
}
/// <summary>
/// Stored user data record
/// </summary>
[Serializable]
public class SavedUserData : PersistentDataContainer
{
/// <summary>
/// The user's username
/// </summary>
public string username = null;
/// <summary>
/// The index of the user's avatar in the UserList.AVATARS list
/// </summary>
public int avatarIndex = -1;
/// <summary>
/// The total playtime of the user
/// </summary>
/// <remarks>Not implemented yet</remarks>
public double playtime = 0.0;
/// <summary>
/// A list of progress on minigames the user has
/// </summary>
public List<SavedMinigameProgress> minigames = new List<SavedMinigameProgress>();
/// <summary>
/// A list of progress on courses the user has
/// </summary>
public List<SavedCourseProgress> courses = new List<SavedCourseProgress>();
}
/// <summary>
/// Stored course progress data record
/// </summary>
[Serializable]
public class SavedCourseProgress : PersistentDataContainer
{
/// <summary>
/// Update the progress value of the SavedLearnableProgress with the given learnableName.
/// </summary>
/// <param name="learnableName"></param>
/// <param name="addValue"></param>
public void UpdateLearnable(string learnableName, float addValue)
{
SavedLearnableProgress learnable = learnables.Find(l => l.name == learnableName);
if (learnable == null)
throw new KeyNotFoundException();
// Update the progress value of the SavedLearnableProgress
learnable.progress += addValue;
// crop the learnable progress around -5 and 5
if (learnable.progress > 5.0f)
learnable.progress = 5.0f;
else if (learnable.progress < -5.0f)
learnable.progress = -5.0f;
// if learnable progress is big enough it is "completed"
if (learnable.progress > 3)
completedLearnables++;
}
/// <summary>
/// Check whether there are enough inUse Learnables
/// </summary>
/// <returns></returns>
private bool EnoughLearnables()
{
// There need to be more then 5 non completed learnables
return inUseLearnables - completedLearnables > 5 || totalLearnables == inUseLearnables;
}
/// <summary>
/// Find a SavedLearnableProgress with the given name
/// </summary>
/// <param name="name"></param>
/// <returns> SavedLearnableProgress with the given name </returns>
public SavedLearnableProgress FindLearnable(string name)
{
return learnables.Find(l => l.name == name);
}
/// <summary>
/// Find learnable in learnables which is not yet in use, and set it active
/// </summary>
/// <returns> SavedLearnableProgress learnable </returns>
private SavedLearnableProgress UseUnusedLearnable()
{
SavedLearnableProgress learnable = learnables.Find(l => !l.inUse);
if (learnable == null)
return null;
learnable.inUse = true;
inUseLearnables++;
return learnable;
}
/// <summary>
/// Gets a random inUse learnable
/// </summary>
/// <returns> a randomly selected inUse SavedLearnable which is not yet completed</returns>
public SavedLearnableProgress GetRandomLearnable()
{
if (!EnoughLearnables())
return UseUnusedLearnable();
// only select inUse learnables which are not yet completed (progress < 3.5f)
List<SavedLearnableProgress> inUseLearnables = learnables.FindAll(l => l.inUse && l.progress <= 3.5f);
if (inUseLearnables.Count == 0)
return null;
// Select a random index from the in-use learnables list
int randomIndex = UnityEngine.Random.Range(0, inUseLearnables.Count);
return inUseLearnables[randomIndex];
}
/// <summary>
/// Create new SavedLearnableProgress object and assigns the index and name values
/// </summary>
/// <param name="name"></param>
/// <param name="index"></param>
/// <returns> bool which indicates the success of the function</returns>
public bool AddLearnable(string name, int index)
{
if (learnables.Any(learnable => learnable.name == name || learnable.index == index))
return false;
SavedLearnableProgress savedLearnableProgress = new SavedLearnableProgress();
savedLearnableProgress.index = index;
savedLearnableProgress.name = name;
learnables.Add(savedLearnableProgress);
totalLearnables++;
return true;
}
public CourseIndex courseIndex;
public float progress = -1.0f;
public int completedLearnables = 0;
public int inUseLearnables = 0;
public int totalLearnables = 0;
public List<SavedLearnableProgress> learnables = new List<SavedLearnableProgress>();
}
/// <summary>
/// Stored individual learnable progress
/// </summary>
[Serializable]
public class SavedLearnableProgress : PersistentDataContainer
{
/// <summary>
/// Index of the Learnbable in its Theme
/// </summary>
public int index;
/// <summary>
/// Bool that indicated whether the user already started learning this Learnable
/// </summary>
public bool inUse = false;
/// <summary>
/// Display name of the Learnable
/// </summary>
public string name;
/// <summary>
/// Progress of the learnabe, a number between -5.0 and +5.0
/// </summary>
public float progress = 0.0f;
}
/// <summary>
/// Stored minigame progress data record
/// </summary>
[Serializable]
public class SavedMinigameProgress : PersistentDataContainer
{
/// <summary>
/// Index of the minigame
/// </summary>
public MinigameIndex minigameIndex;
/// <summary>
/// The 10 last scores of a user
/// </summary>
public List<Score> latestScores = new List<Score>();
/// <summary>
/// Top 10 scores of a user
/// </summary>
public List<Score> highestScores = new List<Score>();
}
/// <summary>
/// Stored WeSign data record
/// </summary>
[Serializable]
private class SavedDataStructure
{
/// <summary>
/// The version of the PersistentDataController with which this savefile is created
/// </summary>
public int version = VERSION;
/// <summary>
/// A list of all users
/// </summary>
public List<SavedUserData> users = new List<SavedUserData>();
/// <summary>
/// The index of the current user in the this.users list
/// </summary>
public int currentUser = -1;
/// <summary>
/// The index of the current minigame
/// </summary>
public MinigameIndex currentMinigame;
/// <summary>
/// The index of the current course
/// </summary>
public CourseIndex currentCourse;
/// <summary>
/// The index of the current theme
/// </summary>
public ThemeIndex currentTheme;
/// <summary>
/// The use hardware acceleration user preferences
/// </summary>
public bool useGPU = false;
/// <summary>
/// Initiate the SavedDataStructure, by setting the user preferences
/// </summary>
public SavedDataStructure()
{
RestoreSettings();
}
/// <summary>
/// Reset the user preferences to the default values
/// </summary>
public void RestoreSettings()
{
useGPU = false;
}
}
/// <summary>
/// The object holding the data references
/// </summary>
private SavedDataStructure json = new SavedDataStructure();
/// <summary>
/// Get the instance loaded by the singleton
/// </summary>
/// <returns><c>PersistentDataController</c> instance</returns>
public static PersistentDataController GetInstance()
{
// Create a new instance if non exists
if (instance == null || PATH == null)
{
if (PATH == null)
PersistentDataController.PATH = $"{Application.persistentDataPath}/wesign_saved_data.json";
instance = new PersistentDataController();
}
return instance;
}
/// <summary>
/// PersistentDataController contructor
/// </summary>
private PersistentDataController()
{
Load();
}
/// <summary>
/// Clear everything stored in the PersistentDataController, won't save to disk
/// </summary>
public void Clear()
{
json.users.Clear();
json.currentUser = -1;
json.useGPU = false;
}
/// <summary>
/// Save all data to disk
/// </summary>
public void Save()
{
string text = JsonUtility.ToJson(json);
File.CreateText(PATH).Close();
File.WriteAllText(PATH, text);
}
/// <summary>
/// Override current data with the data from disk, will just clear if no data was found.
/// </summary>
/// <param name="overrideOnFail"><c>true</c> if you want to override the existing file if it exists and the loading failed.</param>
/// <remarks>If the data on disk is outdated (version number is lower than the current version), the loading will also fail</remarks>
/// <returns><c>true</c> if successful, <c>false</c> otherwise</returns>
public bool Load(bool overrideOnFail = true)
{
Clear();
if (!File.Exists(PATH))
goto failed;
try
{
string text = File.ReadAllText(PATH);
SavedDataStructure newJson = JsonUtility.FromJson<SavedDataStructure>(text);
if (newJson == null || newJson.version != VERSION)
goto failed;
json = newJson;
return true;
}
catch (Exception) { goto failed; }
failed:
if (overrideOnFail)
Save();
return false;
}
/// <summary>
/// Add a user to the WeSign data record
/// </summary>
/// <param name="user">User data record</param>
/// <param name="save">Whether to save the addition immediately to disk</param>
public void AddUser(SavedUserData user, bool save = true)
{
if (json.users.Count == 0)
json.currentUser = 0;
json.users.Add(user);
if (save)
Save();
}
/// <summary>
/// Get a list of all user data records
/// </summary>
/// <returns></returns>
public List<SavedUserData> GetUsers()
{
return json.users;
}
/// <summary>
/// Get the index of the current user
/// </summary>
/// <returns></returns>
public int GetCurrentUser()
{
return json.currentUser;
}
/// <summary>
/// Set the index of the current record
/// </summary>
/// <param name="index">New index</param>
/// <param name="save">Whether to save the change immediately to disk</param>
/// <exception cref="IndexOutOfRangeException"></exception>
public void SetCurrentUser(int index, bool save = true)
{
if (index < 0 || json.users.Count <= index)
throw new IndexOutOfRangeException();
json.currentUser = index;
if (save)
Save();
}
/// <summary>
/// Remove a user data record
/// </summary>
/// <param name="index">Index of the user</param>
/// <param name="save">Whether to save the deletion immediately to disk</param>
/// <exception cref="IndexOutOfRangeException"></exception>
public void DeleteUser(int index, bool save = true)
{
if (index < 0 || json.users.Count <= index)
throw new IndexOutOfRangeException();
if (0 < json.currentUser && index <= json.currentUser)
json.currentUser--;
json.users.RemoveAt(index);
if (save)
Save();
}
/// <summary>
/// Get the current course
/// </summary>
/// <returns></returns>
public CourseIndex GetCurrentCourse()
{
return json.currentCourse;
}
/// <summary>
/// Set the current course
/// </summary>
/// <param name="course">New course index</param>
/// <param name="save">Whether to save the change immediately to disk</param>
public void SetCurrentCourse(CourseIndex course, bool save = true)
{
json.currentCourse = course;
if (save)
Save();
}
/// <summary>
/// Get the current minigame
/// </summary>
/// <returns></returns>
public MinigameIndex GetCurrentMinigame()
{
return json.currentMinigame;
}
/// <summary>
/// Set the current minigame
/// </summary>
/// <param name="minigame">New minigame index</param>
/// <param name="save">Whether to save the change immediately to disk</param>
public void SetCurrentMinigame(MinigameIndex minigame, bool save = true)
{
json.currentMinigame = minigame;
if (save)
Save();
}
/// <summary>
/// Get the current theme
/// </summary>
/// <returns></returns>
public ThemeIndex GetCurrentTheme()
{
return json.currentTheme;
}
/// <summary>
/// Set the current theme
/// </summary>
/// <param name="theme">New theme index</param>
/// <param name="save">Whether to save the change immediately to disk</param>
public void SetCurrentTheme(ThemeIndex theme, bool save = true)
{
json.currentTheme = theme;
if (save)
Save();
}
/// <summary>
/// Whether the user wants to use hardware acceleration or not
/// </summary>
public bool IsUsingGPU()
{
return json.useGPU;
}
/// <summary>
/// Set the preference of the user for hardware acceleration
/// </summary>
/// <param name="value">Value of the preference</param>
/// <param name="save">Whether to save the change immediately to disk</param>
public void SetGPUUsage(bool value, bool save = true)
{
json.useGPU = value;
if (save)
Save();
}
/// <summary>
/// Restore preferences to default factory settings
/// </summary>
/// <param name="save">Whether to save the change immediately to disk</param>
public void RestoreSettings(bool save = true)
{
json.RestoreSettings();
if (save)
Save();
}
}

View File

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

View File

@@ -0,0 +1,19 @@
using System;
/// <summary>
/// Score class
/// </summary>
[Serializable]
public class Score
{
/// <summary>
/// The actual score
/// </summary>
public int scoreValue;
/// <summary>
/// The time when the score is achieved, in string format
/// </summary>
public string time;
}

View File

@@ -0,0 +1,647 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
using UnityEngine.U2D;
#endif
using Sprites = UnityEngine.Sprites;
#if UNITY_EDITOR
using UnityEditor;
// Custom Editor to order the variables in the Inspector similar to Image component
[CustomEditor(typeof(SlicedSlider)), CanEditMultipleObjects]
public class SlicedSliderEditor : Editor
{
private SerializedProperty spriteProp, colorProp;
private GUIContent spriteLabel;
private void OnEnable()
{
spriteProp = serializedObject.FindProperty("m_Sprite");
colorProp = serializedObject.FindProperty("m_Color");
spriteLabel = new GUIContent("Source Image");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(spriteProp, spriteLabel);
EditorGUILayout.PropertyField(colorProp);
DrawPropertiesExcluding(serializedObject, "m_Script", "m_Sprite", "m_Color", "m_OnCullStateChanged");
serializedObject.ApplyModifiedProperties();
}
}
#endif
// Credit: https://bitbucket.org/Unity-Technologies/ui/src/2018.4/UnityEngine.UI/UI/Core/Image.cs
[RequireComponent(typeof(CanvasRenderer))]
[AddComponentMenu("UI/Sliced Slider", 11)]
public class SlicedSlider : MaskableGraphic, ISerializationCallbackReceiver, ILayoutElement, ICanvasRaycastFilter
{
private static class SetPropertyUtility
{
public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct
{
if (EqualityComparer<T>.Default.Equals(currentValue, newValue))
return false;
currentValue = newValue;
return true;
}
public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
{
if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
return false;
currentValue = newValue;
return true;
}
}
public enum FillDirection { Right = 0, Left = 1, Up = 2, Down = 3 }
private static readonly Vector3[] s_Vertices = new Vector3[4];
private static readonly Vector2[] s_UVs = new Vector2[4];
private static readonly Vector2[] s_SlicedVertices = new Vector2[4];
private static readonly Vector2[] s_SlicedUVs = new Vector2[4];
#pragma warning disable 1692
#pragma warning disable IDE1006 // Suppress 'Naming rule violation' warnings
#pragma warning disable 0649
[SerializeField]
private Sprite m_Sprite;
public Sprite sprite
{
get { return m_Sprite; }
set
{
if (SetPropertyUtility.SetClass(ref m_Sprite, value))
{
SetAllDirty();
TrackImage();
}
}
}
[SerializeField]
private FillDirection m_FillDirection;
public FillDirection fillDirection
{
get { return m_FillDirection; }
set
{
if (SetPropertyUtility.SetStruct(ref m_FillDirection, value))
SetVerticesDirty();
}
}
[Range(0, 1)]
[SerializeField]
private float m_FillAmount = 1f;
public float fillAmount
{
get { return m_FillAmount; }
set
{
if (SetPropertyUtility.SetStruct(ref m_FillAmount, Mathf.Clamp01(value)))
SetVerticesDirty();
}
}
[SerializeField]
private bool m_FillCenter = true;
public bool fillCenter
{
get { return m_FillCenter; }
set
{
if (SetPropertyUtility.SetStruct(ref m_FillCenter, value))
SetVerticesDirty();
}
}
[SerializeField]
private float m_PixelsPerUnitMultiplier = 1f;
public float pixelsPerUnitMultiplier
{
get { return m_PixelsPerUnitMultiplier; }
set { m_PixelsPerUnitMultiplier = Mathf.Max(0.01f, value); }
}
public float pixelsPerUnit
{
get
{
float spritePixelsPerUnit = 100;
if (activeSprite)
spritePixelsPerUnit = activeSprite.pixelsPerUnit;
float referencePixelsPerUnit = 100;
if (canvas)
referencePixelsPerUnit = canvas.referencePixelsPerUnit;
return m_PixelsPerUnitMultiplier * spritePixelsPerUnit / referencePixelsPerUnit;
}
}
#pragma warning restore 0649
[NonSerialized]
private Sprite m_OverrideSprite;
public Sprite overrideSprite
{
get { return activeSprite; }
set
{
if (SetPropertyUtility.SetClass(ref m_OverrideSprite, value))
{
SetAllDirty();
TrackImage();
}
}
}
private Sprite activeSprite { get { return m_OverrideSprite != null ? m_OverrideSprite : m_Sprite; } }
public override Texture mainTexture
{
get
{
if (activeSprite != null)
return activeSprite.texture;
return material != null && material.mainTexture != null ? material.mainTexture : s_WhiteTexture;
}
}
public bool hasBorder
{
get
{
if (activeSprite != null)
{
Vector4 v = activeSprite.border;
return v.sqrMagnitude > 0f;
}
return false;
}
}
public override Material material
{
get
{
if (m_Material != null)
return m_Material;
if (activeSprite && activeSprite.associatedAlphaSplitTexture != null)
{
#if UNITY_EDITOR
if (Application.isPlaying)
#endif
return Image.defaultETC1GraphicMaterial;
}
return defaultMaterial;
}
set { base.material = value; }
}
public float alphaHitTestMinimumThreshold { get; set; }
#pragma warning restore IDE1006
#pragma warning restore 1692
protected SlicedSlider()
{
useLegacyMeshGeneration = false;
}
protected override void OnEnable()
{
base.OnEnable();
TrackImage();
}
protected override void OnDisable()
{
base.OnDisable();
if (m_Tracked)
UnTrackImage();
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
m_PixelsPerUnitMultiplier = Mathf.Max(0.01f, m_PixelsPerUnitMultiplier);
}
#endif
protected override void OnPopulateMesh(VertexHelper vh)
{
if (activeSprite == null)
{
base.OnPopulateMesh(vh);
return;
}
GenerateSlicedFilledSprite(vh);
}
/// <summary>
/// Update the renderer's material.
/// </summary>
protected override void UpdateMaterial()
{
base.UpdateMaterial();
// Check if this sprite has an associated alpha texture (generated when splitting RGBA = RGB + A as two textures without alpha)
if (activeSprite == null)
{
canvasRenderer.SetAlphaTexture(null);
return;
}
Texture2D alphaTex = activeSprite.associatedAlphaSplitTexture;
if (alphaTex != null)
canvasRenderer.SetAlphaTexture(alphaTex);
}
private void GenerateSlicedFilledSprite(VertexHelper vh)
{
vh.Clear();
if (m_FillAmount < 0.001f)
return;
Rect rect = GetPixelAdjustedRect();
Vector4 outer = Sprites.DataUtility.GetOuterUV(activeSprite);
Vector4 padding = Sprites.DataUtility.GetPadding(activeSprite);
if (!hasBorder)
{
Vector2 size = activeSprite.rect.size;
int spriteW = Mathf.RoundToInt(size.x);
int spriteH = Mathf.RoundToInt(size.y);
// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top.
Vector4 vertices = new Vector4(
rect.x + rect.width * (padding.x / spriteW),
rect.y + rect.height * (padding.y / spriteH),
rect.x + rect.width * ((spriteW - padding.z) / spriteW),
rect.y + rect.height * ((spriteH - padding.w) / spriteH));
GenerateFilledSprite(vh, vertices, outer, m_FillAmount);
return;
}
Vector4 inner = Sprites.DataUtility.GetInnerUV(activeSprite);
Vector4 border = GetAdjustedBorders(activeSprite.border / pixelsPerUnit, rect);
padding = padding / pixelsPerUnit;
s_SlicedVertices[0] = new Vector2(padding.x, padding.y);
s_SlicedVertices[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);
s_SlicedVertices[1].x = border.x;
s_SlicedVertices[1].y = border.y;
s_SlicedVertices[2].x = rect.width - border.z;
s_SlicedVertices[2].y = rect.height - border.w;
for (int i = 0; i < 4; ++i)
{
s_SlicedVertices[i].x += rect.x;
s_SlicedVertices[i].y += rect.y;
}
s_SlicedUVs[0] = new Vector2(outer.x, outer.y);
s_SlicedUVs[1] = new Vector2(inner.x, inner.y);
s_SlicedUVs[2] = new Vector2(inner.z, inner.w);
s_SlicedUVs[3] = new Vector2(outer.z, outer.w);
float rectStartPos;
float _1OverTotalSize;
if (m_FillDirection == FillDirection.Left || m_FillDirection == FillDirection.Right)
{
rectStartPos = s_SlicedVertices[0].x;
float totalSize = (s_SlicedVertices[3].x - s_SlicedVertices[0].x);
_1OverTotalSize = totalSize > 0f ? 1f / totalSize : 1f;
}
else
{
rectStartPos = s_SlicedVertices[0].y;
float totalSize = (s_SlicedVertices[3].y - s_SlicedVertices[0].y);
_1OverTotalSize = totalSize > 0f ? 1f / totalSize : 1f;
}
for (int x = 0; x < 3; x++)
{
int x2 = x + 1;
for (int y = 0; y < 3; y++)
{
if (!m_FillCenter && x == 1 && y == 1)
continue;
int y2 = y + 1;
float sliceStart, sliceEnd;
switch (m_FillDirection)
{
case FillDirection.Right:
sliceStart = (s_SlicedVertices[x].x - rectStartPos) * _1OverTotalSize;
sliceEnd = (s_SlicedVertices[x2].x - rectStartPos) * _1OverTotalSize;
break;
case FillDirection.Up:
sliceStart = (s_SlicedVertices[y].y - rectStartPos) * _1OverTotalSize;
sliceEnd = (s_SlicedVertices[y2].y - rectStartPos) * _1OverTotalSize;
break;
case FillDirection.Left:
sliceStart = 1f - (s_SlicedVertices[x2].x - rectStartPos) * _1OverTotalSize;
sliceEnd = 1f - (s_SlicedVertices[x].x - rectStartPos) * _1OverTotalSize;
break;
case FillDirection.Down:
sliceStart = 1f - (s_SlicedVertices[y2].y - rectStartPos) * _1OverTotalSize;
sliceEnd = 1f - (s_SlicedVertices[y].y - rectStartPos) * _1OverTotalSize;
break;
default: // Just there to get rid of the "Use of unassigned local variable" compiler error
sliceStart = sliceEnd = 0f;
break;
}
if (sliceStart >= m_FillAmount)
continue;
Vector4 vertices = new Vector4(s_SlicedVertices[x].x, s_SlicedVertices[y].y, s_SlicedVertices[x2].x, s_SlicedVertices[y2].y);
Vector4 uvs = new Vector4(s_SlicedUVs[x].x, s_SlicedUVs[y].y, s_SlicedUVs[x2].x, s_SlicedUVs[y2].y);
float fillAmount = (m_FillAmount - sliceStart) / (sliceEnd - sliceStart);
GenerateFilledSprite(vh, vertices, uvs, fillAmount);
}
}
}
private Vector4 GetAdjustedBorders(Vector4 border, Rect adjustedRect)
{
Rect originalRect = rectTransform.rect;
for (int axis = 0; axis <= 1; axis++)
{
float borderScaleRatio;
// The adjusted rect (adjusted for pixel correctness) may be slightly larger than the original rect.
// Adjust the border to match the adjustedRect to avoid small gaps between borders (case 833201).
if (originalRect.size[axis] != 0)
{
borderScaleRatio = adjustedRect.size[axis] / originalRect.size[axis];
border[axis] *= borderScaleRatio;
border[axis + 2] *= borderScaleRatio;
}
// If the rect is smaller than the combined borders, then there's not room for the borders at their normal size.
// In order to avoid artefacts with overlapping borders, we scale the borders down to fit.
float combinedBorders = border[axis] + border[axis + 2];
if (adjustedRect.size[axis] < combinedBorders && combinedBorders != 0)
{
borderScaleRatio = adjustedRect.size[axis] / combinedBorders;
border[axis] *= borderScaleRatio;
border[axis + 2] *= borderScaleRatio;
}
}
return border;
}
private void GenerateFilledSprite(VertexHelper vh, Vector4 vertices, Vector4 uvs, float fillAmount)
{
if (m_FillAmount < 0.001f)
return;
float uvLeft = uvs.x;
float uvBottom = uvs.y;
float uvRight = uvs.z;
float uvTop = uvs.w;
if (fillAmount < 1f)
{
if (m_FillDirection == FillDirection.Left || m_FillDirection == FillDirection.Right)
{
if (m_FillDirection == FillDirection.Left)
{
vertices.x = vertices.z - (vertices.z - vertices.x) * fillAmount;
uvLeft = uvRight - (uvRight - uvLeft) * fillAmount;
}
else
{
vertices.z = vertices.x + (vertices.z - vertices.x) * fillAmount;
uvRight = uvLeft + (uvRight - uvLeft) * fillAmount;
}
}
else
{
if (m_FillDirection == FillDirection.Down)
{
vertices.y = vertices.w - (vertices.w - vertices.y) * fillAmount;
uvBottom = uvTop - (uvTop - uvBottom) * fillAmount;
}
else
{
vertices.w = vertices.y + (vertices.w - vertices.y) * fillAmount;
uvTop = uvBottom + (uvTop - uvBottom) * fillAmount;
}
}
}
s_Vertices[0] = new Vector3(vertices.x, vertices.y);
s_Vertices[1] = new Vector3(vertices.x, vertices.w);
s_Vertices[2] = new Vector3(vertices.z, vertices.w);
s_Vertices[3] = new Vector3(vertices.z, vertices.y);
s_UVs[0] = new Vector2(uvLeft, uvBottom);
s_UVs[1] = new Vector2(uvLeft, uvTop);
s_UVs[2] = new Vector2(uvRight, uvTop);
s_UVs[3] = new Vector2(uvRight, uvBottom);
int startIndex = vh.currentVertCount;
for (int i = 0; i < 4; i++)
vh.AddVert(s_Vertices[i], color, s_UVs[i]);
vh.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
vh.AddTriangle(startIndex + 2, startIndex + 3, startIndex);
}
int ILayoutElement.layoutPriority { get { return 0; } }
float ILayoutElement.minWidth { get { return 0; } }
float ILayoutElement.minHeight { get { return 0; } }
float ILayoutElement.flexibleWidth { get { return -1; } }
float ILayoutElement.flexibleHeight { get { return -1; } }
float ILayoutElement.preferredWidth
{
get
{
if (activeSprite == null)
return 0;
return Sprites.DataUtility.GetMinSize(activeSprite).x / pixelsPerUnit;
}
}
float ILayoutElement.preferredHeight
{
get
{
if (activeSprite == null)
return 0;
return Sprites.DataUtility.GetMinSize(activeSprite).y / pixelsPerUnit;
}
}
void ILayoutElement.CalculateLayoutInputHorizontal() { }
void ILayoutElement.CalculateLayoutInputVertical() { }
bool ICanvasRaycastFilter.IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
{
if (alphaHitTestMinimumThreshold <= 0)
return true;
if (alphaHitTestMinimumThreshold > 1)
return false;
if (activeSprite == null)
return true;
Vector2 local;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local))
return false;
Rect rect = GetPixelAdjustedRect();
// Convert to have lower left corner as reference point.
local.x += rectTransform.pivot.x * rect.width;
local.y += rectTransform.pivot.y * rect.height;
Rect spriteRect = activeSprite.rect;
Vector4 border = activeSprite.border;
Vector4 adjustedBorder = GetAdjustedBorders(border / pixelsPerUnit, rect);
for (int i = 0; i < 2; i++)
{
if (local[i] <= adjustedBorder[i])
continue;
if (rect.size[i] - local[i] <= adjustedBorder[i + 2])
{
local[i] -= (rect.size[i] - spriteRect.size[i]);
continue;
}
float lerp = Mathf.InverseLerp(adjustedBorder[i], rect.size[i] - adjustedBorder[i + 2], local[i]);
local[i] = Mathf.Lerp(border[i], spriteRect.size[i] - border[i + 2], lerp);
}
// Normalize local coordinates.
Rect textureRect = activeSprite.textureRect;
Vector2 normalized = new Vector2(local.x / textureRect.width, local.y / textureRect.height);
// Convert to texture space.
float x = Mathf.Lerp(textureRect.x, textureRect.xMax, normalized.x) / activeSprite.texture.width;
float y = Mathf.Lerp(textureRect.y, textureRect.yMax, normalized.y) / activeSprite.texture.height;
switch (m_FillDirection)
{
case FillDirection.Right:
if (x > m_FillAmount)
return false;
break;
case FillDirection.Left:
if (1f - x > m_FillAmount)
return false;
break;
case FillDirection.Up:
if (y > m_FillAmount)
return false;
break;
case FillDirection.Down:
if (1f - y > m_FillAmount)
return false;
break;
}
try
{
return activeSprite.texture.GetPixelBilinear(x, y).a >= alphaHitTestMinimumThreshold;
}
catch (UnityException e)
{
Debug.LogError("Using alphaHitTestMinimumThreshold greater than 0 on Image whose sprite texture cannot be read. " + e.Message + " Also make sure to disable sprite packing for this sprite.", this);
return true;
}
}
void ISerializationCallbackReceiver.OnBeforeSerialize() { }
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
m_FillAmount = Mathf.Clamp01(m_FillAmount);
}
// Whether this is being tracked for Atlas Binding
private bool m_Tracked = false;
#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
private static List<SlicedSlider> m_TrackedTexturelessImages = new List<SlicedSlider>();
private static bool s_Initialized;
#endif
private void TrackImage()
{
if (activeSprite != null && activeSprite.texture == null)
{
#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
if (!s_Initialized)
{
SpriteAtlasManager.atlasRegistered += RebuildImage;
s_Initialized = true;
}
m_TrackedTexturelessImages.Add(this);
#endif
m_Tracked = true;
}
}
private void UnTrackImage()
{
#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
m_TrackedTexturelessImages.Remove(this);
#endif
m_Tracked = false;
}
#if UNITY_2017_4 || UNITY_2018_2_OR_NEWER
private static void RebuildImage(SpriteAtlas spriteAtlas)
{
for (int i = m_TrackedTexturelessImages.Count - 1; i >= 0; i--)
{
SlicedSlider image = m_TrackedTexturelessImages[i];
if (spriteAtlas.CanBindTo(image.activeSprite))
{
image.SetAllDirty();
m_TrackedTexturelessImages.RemoveAt(i);
}
}
}
#endif
}

View File

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

View File

@@ -0,0 +1,133 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// SystemController singleton
/// </summary>
public class SystemController
{
/// <summary>
/// The instance controlling the singleton
/// </summary>
private static SystemController instance = null;
/// <summary>
/// Stack of the loaded scenes, used to easily go back to previous scenes
/// </summary>
private Stack<int> sceneStack = new Stack<int>();
/// <summary>
/// Index of the previous loaded scene
/// </summary>
public int previousScene = -1;
/// <summary>
/// Index of the current loaded scene
/// </summary>
public int currentScene = -1;
/// <summary>
/// Get the instance loaded by the singleton
/// </summary>
/// <returns>SystemController instance</returns>
public static SystemController GetInstance()
{
// Create a new instance if non exists
if (instance == null)
{
instance = new SystemController();
instance.currentScene = SceneManager.GetActiveScene().buildIndex;
instance.sceneStack.Push(instance.currentScene);
}
return instance;
}
/// <summary>
/// Get the number of passively 'active' scenes
/// </summary>
public int GetSceneStackSize() { return sceneStack.Count; }
/// <summary>
/// Load the scene and push on the stack
/// </summary>
/// <param name="scenePath">Path of the scene</param>
public void LoadNextScene(string scenePath)
{
LoadNextScene(SystemController.GetSceneIndex(scenePath));
}
/// <summary>
/// Get the index of a given scene
/// </summary>
/// <param name="scenePath">Path of the scene</param>
/// <returns></returns>
public static int GetSceneIndex(string scenePath)
{
return SceneUtility.GetBuildIndexByScenePath(scenePath);
}
/// <summary>
/// Load the scene and push on the stack
/// </summary>
/// <param name="sceneIndex">Buildindex of the scene</param>
public void LoadNextScene(int sceneIndex)
{
previousScene = currentScene;
currentScene = sceneIndex;
sceneStack.Push(currentScene);
SceneManager.LoadScene(currentScene);
}
/// <summary>
/// Swap the current scene with the new scene on the stack
/// </summary>
/// <param name="scenePath">Path of the scene</param>
public void SwapScene(string scenePath)
{
SwapScene(SystemController.GetSceneIndex(scenePath));
}
/// <summary>
/// Swap the current scene with the new scene on the stack
/// </summary>
/// <param name="sceneIndex">Buildindex of the scene</param>
public void SwapScene(int sceneIndex)
{
currentScene = sceneStack.Pop();
LoadNextScene(sceneIndex);
}
/// <summary>
/// Go back to the previous scene and unload the current scene
/// </summary>
public void BackToPreviousScene()
{
previousScene = sceneStack.Pop();
if (sceneStack.Count > 0) SceneManager.LoadScene(currentScene = sceneStack.Peek());
else Application.Quit();
}
/// <summary>
/// Go back to a specific scene, unloading all the scenes on the way
/// </summary>
/// <param name="scenePath">Path of the scene</param>
public void BackToScene(string scenePath)
{
BackToScene(SystemController.GetSceneIndex(scenePath));
}
/// <summary>
/// Go back to a specific scene, unloading all the scene on the way
/// </summary>
/// <param name="sceneIndex">Buildindex of the scene</param>
public void BackToScene(int sceneIndex)
{
previousScene = currentScene;
while (0 < sceneStack.Count && sceneStack.Peek() != sceneIndex) sceneStack.Pop();
if (sceneStack.Count > 0) SceneManager.LoadScene(currentScene = sceneStack.Peek());
else Application.Quit();
}
}

View File

@@ -0,0 +1,19 @@
/// <summary>
/// Enum for easy indexing and checking if a course is of a certain kind
/// </summary>
public enum ThemeIndex
{
SIGN_ALPHABET,
SIGN_BASICS,
SIGN_CLOTHING,
SIGN_ANIMALS,
SIGN_FOOD,
SIGN_HOBBIES,
SIGN_HOUSE,
SIGN_FAMILY,
SPELLING_COUNTRIES,
SPELLING_WILD,
SPELLING_FARM,
SPELLING_CAPITALS
}

View File

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

View File

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

View File

@@ -0,0 +1,24 @@
{
"name": "ArchitectureEditMode",
"rootNamespace": "",
"references": [
"UnityEditor.TestRunner",
"UnityEngine.TestRunner",
"ArchitectureScripts"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

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

Some files were not shown because too many files have changed in this diff Show More