32 Commits

Author SHA1 Message Date
Supaaah1
1fdf5f425f deleted stale dependency 2023-04-01 20:54:02 +02:00
CoudronJerome
670a8de6cd FInalized changes pre-merge
Removed items in abstract class that don't need to be there
2023-03-30 20:35:28 +02:00
CoudronJerome
c9c22e6426 Warnings fixed 2023-03-30 20:00:28 +02:00
CoudronJerome
c9ebec1724 Implemented changes to use abstract feedback 2023-03-30 19:38:58 +02:00
CoudronJerome
a44532b94a Changed GetCurrentModel to return the model instead of the tuple 2023-03-29 19:17:42 +02:00
CoudronJerome
be8885a508 Implemented MR feedback 2023-03-28 15:37:17 +02:00
CoudronJerome
dfdb2ab10b Updated getter in ModelList and cleaned Assembly 2023-03-28 08:52:02 +02:00
CoudronJerome
be7457236c Added test-code and moved scripts to correct folders 2023-03-27 10:57:30 +02:00
CoudronJerome
78f4d961f7 Made modelChanging internal in SIgnPredictor
Made it so that there is a function inside SignPredictor that is used to change its model
2023-03-26 23:15:09 +02:00
CoudronJerome
51ee8b0658 Merge branch 'development' into WES-127-Refactor-ThemeList 2023-03-26 22:58:02 +02:00
CoudronJerome
2183397a0f Succesfully removed model from theme 2023-03-26 22:51:02 +02:00
CoudronJerome
9f0f48e257 Tested new implementation for models 2023-03-26 22:44:43 +02: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
316 changed files with 32696 additions and 1765 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/

3
.gitignore vendored
View File

@@ -72,4 +72,5 @@ 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

View File

@@ -42,6 +42,7 @@ public class ChangeUserScreen : MonoBehaviour
/// </summary>
void Start()
{
userList.Load();
error.SetActive(false);
DisplayUsers();
}

View File

@@ -101,4 +101,14 @@ public class Progress
// Raise an exception when key is not found
throw new KeyNotFoundException();
}
/// <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;
}
}

View File

@@ -19,7 +19,7 @@ public class UserList : ScriptableObject
/// <summary>
/// The index of the current/last logged in user in the <c>storedUsers</c> list
/// </summary>
public int currentUserIndex;
public int currentUserIndex = -1;
/// <summary>
/// A list containing all users (which can be serialized)
@@ -43,8 +43,13 @@ public class UserList : ScriptableObject
/// </summary>
void OnEnable()
{
PATH = $"{Application.persistentDataPath}/users.json";
Load();
// The PATH variable can be set by the testing framework,
// so we don't overwrite the actual userlist with test data
if (PATH == null)
{
PATH = $"{Application.persistentDataPath}/users.json";
Load();
}
}
/// <summary>
@@ -71,6 +76,10 @@ public class UserList : ScriptableObject
{
User user = CreateNewUser(name, avatar);
storedUserList.storedUsers.Add(user);
if (storedUserList.storedUsers.Count == 1)
{
storedUserList.currentUserIndex = 0;
}
Save();
return user;
}
@@ -103,6 +112,10 @@ public class UserList : ScriptableObject
/// <returns>The current logged in user</returns>
public User GetCurrentUser()
{
if (storedUserList.storedUsers.Count == 0)
{
return null;
}
return storedUserList.storedUsers[storedUserList.currentUserIndex];
}
@@ -119,28 +132,46 @@ public class UserList : ScriptableObject
/// Change the current user
/// </summary>
/// <param name="index">Index of the user in the userlist</param>
/// <exception cref="IndexOutOfRangeException"></exception>
public void ChangeCurrentUser(int index)
{
storedUserList.currentUserIndex = index;
if (0 <= index && index < storedUserList.storedUsers.Count)
{
storedUserList.currentUserIndex = index;
}
else
{
throw new IndexOutOfRangeException();
}
}
/// <summary>
/// Change the current user
/// </summary>
/// <param name="user">Reference to the user in the userlist</param>
/// <exception cref="KeyNotFoundException"></exception>
public void ChangeCurrentUser(User user)
{
storedUserList.currentUserIndex = storedUserList.storedUsers.IndexOf(user);
int idx = storedUserList.storedUsers.IndexOf(user);
if (idx < 0)
{
throw new KeyNotFoundException();
}
storedUserList.currentUserIndex = idx;
}
/// <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>
/// <returns>true if user was successful removed, false otherwise</returns
public bool DeleteUser(int index)
{
return DeleteUser(storedUserList.storedUsers[index]);
if (0 <= index && index < storedUserList.storedUsers.Count)
{
return DeleteUser(storedUserList.storedUsers[index]);
}
return false;
}
/// <summary>
@@ -177,13 +208,14 @@ public class UserList : ScriptableObject
/// </summary>
public void Load()
{
try
{
storedUserList.storedUsers.Clear();
storedUserList.storedUsers.Clear();
storedUserList.currentUserIndex = -1;
string text = File.ReadAllText(PATH);
storedUserList = JsonUtility.FromJson<StoredUserList>(text);
if (!File.Exists(PATH))
{
Save();
}
catch (FileNotFoundException) { Debug.Log($"Path '{PATH}' not found"); }
string text = File.ReadAllText(PATH);
storedUserList = JsonUtility.FromJson<StoredUserList>(text);
}
}

View File

@@ -112,6 +112,7 @@ public class UserProgressScreen : MonoBehaviour
void Start()
{
// Assign the current user
userList.Load();
user = userList.GetCurrentUser();
// Set correct displayed items

View File

@@ -7,8 +7,14 @@ using UnityEngine;
/// <summary>
/// Test the Progress class
/// </summary>
public class TestProgress
[TestFixture]
public class ProgressTest
{
/// <summary>
/// Reference to the progress object to be tested
/// </summary>
private Progress progress;
/// <summary>
/// A dummy serializable struct to perform test operations on
/// </summary>
@@ -29,205 +35,179 @@ public class TestProgress
}
/// <summary>
/// Helper method
/// Setup the tests
/// </summary>
/// <returns><c>true</c> if <c>Progress.AddOrUpdate(...)</c> throws a <c>SerializationException</c></returns>
private bool AddNonSerializableStruct()
[SetUp]
public void Setup_Progress()
{
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;
progress = new Progress();
}
/// <summary>
/// Test for creation of a new progress
/// </summary>
[Test]
public void TestNewProgress()
public void Test_New_Progress()
{
Progress progress = new Progress();
Debug.Assert(progress != null);
Assert.IsNotNull(progress);
}
/// <summary>
/// Test whether invalid data will not be added
/// </summary>
[Test]
public void TestProgressAddInvalidData()
public void Test_Progress_Add_InvalidData()
{
Progress progress = new Progress();
Debug.Assert(!progress.AddOrUpdate<GameObject>("key", null));
Assert.IsFalse(progress.AddOrUpdate<GameObject>("key", null));
}
/// <summary>
/// Test whether a duplicated key will be added
/// </summary>
[Test]
public void TestProgressAddDuplicateKey()
public void Test_Progress_Add_DuplicateKey()
{
Progress progress = new Progress();
progress.AddOrUpdate<int>("key 1", 0);
Debug.Assert(progress.AddOrUpdate<int>("key 1", 1));
Assert.IsTrue(progress.AddOrUpdate<int>("key 1", 1));
}
/// <summary>
/// Test whether a <c>int</c> value can be added
/// </summary>
[Test]
public void TestProgressAddInt()
public void Test_Progress_Add_Int()
{
Progress progress = new Progress();
Debug.Assert(progress.AddOrUpdate<int>("key", 1));
Assert.IsTrue(progress.AddOrUpdate<int>("key", 1));
}
/// <summary>
/// Test whether a <c>double</c> value can be added
/// </summary>
[Test]
public void TestProgressAddDouble()
public void Test_Progress_Add_Double()
{
Progress progress = new Progress();
Debug.Assert(progress.AddOrUpdate<double>("key", 1.0));
Assert.IsTrue(progress.AddOrUpdate<double>("key", 1.0));
}
/// <summary>
/// Test whether a <c>string</c> value can be added
/// </summary>
[Test]
public void TestProgressAddString()
public void Test_Progress_Add_String()
{
Progress progress = new Progress();
Debug.Assert(progress.AddOrUpdate<string>("key", "Hello World!"));
Assert.IsTrue(progress.AddOrUpdate<string>("key", "Hello World!"));
}
/// <summary>
/// Test whether a serializable struct can be added
/// </summary>
[Test]
public void TestProgressAddSerializableStruct()
public void Test_Progress_Add_SerializableStruct()
{
Progress progress = new Progress();
Debug.Assert(progress.AddOrUpdate<SerializableStruct>("key", new SerializableStruct()));
Assert.IsTrue(progress.AddOrUpdate<SerializableStruct>("key", new SerializableStruct()));
}
/// <summary>
/// Test whether a non-serializable struct will throw an error
/// </summary>
[Test]
public void TestProgressAddNonSerializableStruct()
public void Test_Progress_Add_NonSerializableStruct()
{
Debug.Assert(AddNonSerializableStruct());
NonSerializableStruct nss = new NonSerializableStruct();
Assert.Throws<SerializationException>(delegate { progress.AddOrUpdate<NonSerializableStruct>("key", nss); });
}
/// <summary>
/// Test whether a key is present
/// </summary>
[Test]
public void Test_Progress_Has_ValidKey()
{
progress.AddOrUpdate<int>("key", 1);
Assert.IsTrue(progress.Has("key"));
}
/// <summary>
/// Test whether a key is not present
/// </summary>
[Test]
public void Test_Progress_Has_InvalidKey()
{
Assert.IsFalse(progress.Has("non-existing key"));
}
/// <summary>
/// Test whether an invalid key will throw an error
/// </summary>
[Test]
public void TestProgressGetInvalidKey()
public void Test_Progress_Get_InvalidKey()
{
Debug.Assert(AccessInvalidKey());
Assert.Throws<KeyNotFoundException>(delegate { progress.Get<int>("non-existing key"); });
}
/// <summary>
/// Test whether an invalid type will throw an error
/// </summary>
[Test]
public void TestProgressGetInvalidType()
public void Test_Progress_Get_InvalidType()
{
Debug.Assert(AccessInvalidType());
progress.AddOrUpdate<int>("key", 123456789);
Assert.Throws<InvalidCastException>(delegate { progress.Get<double>("key"); });
}
/// <summary>
/// Test whether a value is correctly updated
/// </summary>
[Test]
public void TestProgressUpdate()
public void Test_Progress_Update()
{
Progress progress = new Progress();
progress.AddOrUpdate<int>("key", 1);
Debug.Assert(progress.Get<int>("key") == 1);
Assert.AreEqual(progress.Get<int>("key"), 1);
progress.AddOrUpdate<int>("key", 2);
Debug.Assert(progress.Get<int>("key") == 2);
Assert.AreEqual(progress.Get<int>("key"), 2);
}
/// <summary>
/// Test whether a <c>int</c> value can be read
/// </summary>
[Test]
public void TestProgressGetInt()
public void Test_Progress_Get_Int()
{
Progress progress = new Progress();
progress.AddOrUpdate<int>("key", 1);
Debug.Assert(progress.Get<int>("key") == 1);
Assert.AreEqual(progress.Get<int>("key"), 1);
}
/// <summary>
/// Test whether a <c>double</c> value can be read
/// </summary>
[Test]
public void TestProgressGetDouble()
public void Test_Progress_Get_Double()
{
Progress progress = new Progress();
progress.AddOrUpdate<double>("key", 1.0);
Debug.Assert(progress.Get<double>("key") == 1.0);
Assert.AreEqual(progress.Get<double>("key"), 1.0);
}
/// <summary>
/// Test whether a <c>string</c> value can be read
/// </summary>
[Test]
public void TestProgressGetString()
public void Test_Progress_Get_String()
{
Progress progress = new Progress();
progress.AddOrUpdate<string>("key", "Hello World!");
Debug.Assert(progress.Get<string>("key") == "Hello World!");
Assert.AreEqual(progress.Get<string>("key"), "Hello World!");
}
/// <summary>
/// Test whether a serializable struct can be read
/// </summary>
[Test]
public void TestProgressGetStruct()
public void Test_Progress_Get_Struct()
{
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);
Assert.AreEqual(result, data);
}
}

View File

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

View File

@@ -0,0 +1,470 @@
using NUnit.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
/// <summary>
/// Test the UserList class
/// </summary>
[TestFixture]
public class UserListTest
{
/// <summary>
/// Create a new path so the existing users.json file will not be overwritten
/// </summary>
private static string PATH = $"{Application.persistentDataPath}/unit_test_users.json";
/// <summary>
/// NUnit test magic (for skipping the setup)
/// </summary>
public const string SKIP_SETUP = "SKIP_SETUP";
/// <summary>
/// Reference to the userlist to be tested
/// </summary>
private UserList userList;
/// <summary>
/// Helper variable for quick user creation
/// </summary>
private string username = "u5erNam3";
/// <summary>
/// Helper variable for quick user creation
/// </summary>
private Sprite avatar = Sprite.Create(
Texture2D.blackTexture,
new Rect(0, 0, Texture2D.blackTexture.width, Texture2D.blackTexture.height),
new Vector2(0.5f, 0.5f)
);
/// <summary>
/// Setup the tests
/// </summary>
[SetUp]
public void Setup_UserList()
{
// Check whether the current test needs to skip the setup
ArrayList cat = TestContext.CurrentContext.Test.Properties["_CATEGORIES"] as ArrayList;
bool skip = cat != null && cat.Contains(SKIP_SETUP);
if (!skip)
{
// The actual setup code
UserList.PATH = UserListTest.PATH;
userList = ScriptableObject.CreateInstance<UserList>();
}
}
/// <summary>
/// Test whether the UserList.PATH is correctly set
/// </summary>
[Test]
[Category(SKIP_SETUP)]
public void Test_UserList_OnEnable()
{
UserList.PATH = null;
userList = ScriptableObject.CreateInstance<UserList>();
Assert.AreEqual($"{Application.persistentDataPath}/users.json", UserList.PATH);
}
/// <summary>
/// Test for creation of a new UserList
/// </summary>
[Test]
public void Test_New_UserList()
{
Assert.IsNotNull(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_CreateNewUser()
{
User user = userList.CreateNewUser(username, avatar);
Assert.IsNotNull(user);
Assert.Zero(userList.GetUsers().Count);
Assert.AreEqual(username, user.username);
Assert.AreEqual(avatar, user.avatar);
}
/// <summary>
/// Test for creating a new user and adding the user to the list
/// </summary>
public void Test_UserList_CreateAndAddNewUser()
{
Assert.AreEqual(-1, userList.GetCurrentUserIndex());
User user = userList.CreateAndAddNewUser(username, avatar);
Assert.IsNotNull(user);
Assert.AreEqual(1, userList.GetUsers().Count);
Assert.AreEqual(user, userList.GetUsers()[0]);
Assert.Zero(userList.GetCurrentUserIndex());
}
/// <summary>
/// Test whether an existing user can be found by its username
/// </summary>
[Test]
public void Test_UserList_GetUserByUsername_Valid()
{
User u = userList.CreateAndAddNewUser(username, avatar);
User v = userList.GetUserByUsername(username);
Assert.AreEqual(u, v);
}
/// <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 the correct current user is returned
/// </summary>
[Test]
public void Test_UserList_GetCurrentUser()
{
User u = userList.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateAndAddNewUser($"{username}_{'v'}", avatar);
User w = userList.CreateAndAddNewUser($"{username}_{'w'}", avatar);
userList.ChangeCurrentUser(2);
User W = userList.GetCurrentUser();
Assert.AreEqual(w, W);
}
/// <summary>
/// Test whether a null user is returned when the userlist is empty
/// </summary>
[Test]
public void Test_UserList_GetCurrent_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_GetCurrentUserIndex()
{
User u = userList.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateAndAddNewUser($"{username}_{'v'}", avatar);
User w = userList.CreateAndAddNewUser($"{username}_{'w'}", avatar);
userList.ChangeCurrentUser(2);
int idx = userList.GetCurrentUserIndex();
Assert.AreEqual(2, idx);
}
/// <summary>
/// Test whether a bad index is returned when the userlist is empty
/// </summary>
[Test]
public void Test_UserList_GetCurrentUserIndex_Empty()
{
Assert.AreEqual(-1, userList.GetCurrentUserIndex());
}
/// <summary>
/// Test whether the current user (referenced by index) is correctly changed
/// </summary>
[Test]
public void Test_UserList_ChangeCurrentUser_ValidIndex()
{
User u = userList.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateAndAddNewUser($"{username}_{'v'}", avatar);
User w = userList.CreateAndAddNewUser($"{username}_{'w'}", avatar);
userList.ChangeCurrentUser(2);
User W = userList.GetCurrentUser();
Assert.AreEqual(w, W);
}
/// <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.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateAndAddNewUser($"{username}_{'v'}", avatar);
User w = userList.CreateAndAddNewUser($"{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_ValidUser()
{
User u = userList.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateAndAddNewUser($"{username}_{'v'}", avatar);
User w = userList.CreateAndAddNewUser($"{username}_{'w'}", avatar);
userList.ChangeCurrentUser(v);
User V = userList.GetCurrentUser();
Assert.AreEqual(v, V);
}
/// <summary>
/// Test whether the current user is not changed when a non-existing user is given
/// </summary>
[Test]
public void Test_UserList_ChangeCurrentUser_InvalidUser()
{
User u = userList.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateNewUser($"{username}_{'v'}", avatar);
Assert.Throws<KeyNotFoundException>(delegate { userList.ChangeCurrentUser(v); });
}
/// <summary>
/// Test whether the current user is not changed when a null user is given
/// </summary>
[Test]
public void Test_UserList_ChangeCurrentUser_NullUser()
{
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.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateAndAddNewUser($"{username}_{'v'}", avatar);
User w = userList.CreateAndAddNewUser($"{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.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateAndAddNewUser($"{username}_{'v'}", avatar);
User w = userList.CreateAndAddNewUser($"{username}_{'w'}", avatar);
Assert.IsFalse(userList.DeleteUser(-1));
Assert.IsFalse(userList.DeleteUser(5));
}
/// <summary>
/// Test whether deleting any user from an empty userlist will fail
/// </summary>
[Test]
public void Test_UserList_DeleteUser_IndexEmpty()
{
Assert.IsFalse(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_LastValid()
{
User u = userList.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateAndAddNewUser($"{username}_{'v'}", avatar);
User w = userList.CreateAndAddNewUser($"{username}_{'w'}", avatar);
userList.ChangeCurrentUser(2);
Assert.AreEqual(3, userList.GetUsers().Count);
Assert.AreEqual(2, userList.GetCurrentUserIndex());
Assert.IsTrue(userList.DeleteUser(w));
Assert.AreEqual(2, userList.GetUsers().Count);
Assert.AreEqual(1, userList.GetCurrentUserIndex());
}
/// <summary>
/// Test whether deleting an existing user will remove the user correctly
/// </summary>
/// <remarks>This will change the currentUserIndex to point to another user</remarks>
[Test]
public void Test_UserList_DeleteUser_FirstValid()
{
User u = userList.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateAndAddNewUser($"{username}_{'v'}", avatar);
User w = userList.CreateAndAddNewUser($"{username}_{'w'}", avatar);
userList.ChangeCurrentUser(0);
Assert.AreEqual(3, userList.GetUsers().Count);
Assert.AreEqual(0, userList.GetCurrentUserIndex());
Assert.IsTrue(userList.DeleteUser(u));
Assert.AreEqual(2, userList.GetUsers().Count);
Assert.AreEqual(0, userList.GetCurrentUserIndex());
}
/// <summary>
/// Test whether deleting a non-existing user will not affect the userlist
/// </summary>
[Test]
public void Test_UserList_DeleteUser_Invalid()
{
User u = userList.CreateAndAddNewUser($"{username}_{'u'}", avatar);
User v = userList.CreateNewUser($"{username}_{'v'}", avatar);
Assert.AreEqual(1, userList.GetUsers().Count);
Assert.IsFalse(userList.DeleteUser(v));
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 = userList.CreateNewUser(username, avatar);
Assert.Zero(userList.GetUsers().Count);
Assert.IsFalse(userList.DeleteUser(user));
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()
{
if (File.Exists(PATH))
{
File.Delete(PATH);
}
List<User> u = new List<User>();
for (int i = 0; i < 5; i++)
{
u.Add(userList.CreateAndAddNewUser($"{username}_{i}", avatar));
}
userList.ChangeCurrentUser(3);
userList.Save();
FileAssert.Exists(PATH);
string content = File.ReadAllText(PATH);
int id = avatar.GetInstanceID();
string expected = $"{{\"currentUserIndex\":3,\"storedUsers\":[{{\"username\":\"u5erNam3_0\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}},{{\"username\":\"u5erNam3_1\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}},{{\"username\":\"u5erNam3_2\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}},{{\"username\":\"u5erNam3_3\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}},{{\"username\":\"u5erNam3_4\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}}]}}";
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.CreateAndAddNewUser($"{username}_{i}", avatar));
}
userList.ChangeCurrentUser(3);
userList.Save();
FileAssert.Exists(PATH);
string content = File.ReadAllText(PATH);
int id = avatar.GetInstanceID();
string expected = $"{{\"currentUserIndex\":3,\"storedUsers\":[{{\"username\":\"u5erNam3_0\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}},{{\"username\":\"u5erNam3_1\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}},{{\"username\":\"u5erNam3_2\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}},{{\"username\":\"u5erNam3_3\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}},{{\"username\":\"u5erNam3_4\",\"avatar\":{{\"instanceID\":{id}}},\"playtime\":0.0,\"courses\":[],\"minigames\":[]}}]}}";
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 = "{\"currentUserIndex\":-1,\"storedUsers\":[]}";
Assert.AreEqual(expected, content);
}
/// <summary>
/// Test whether a userlist (containing some users) is correrctly loaded
/// </summary>
[Test]
public void Test_UserList_Load()
{
List<User> u = new List<User>();
for (int i = 0; i < 5; i++)
{
u.Add(userList.CreateAndAddNewUser($"{username}_{i}", avatar));
}
userList.ChangeCurrentUser(3);
userList.Save();
userList.Load();
Assert.AreEqual(userList.GetUsers().Count, u.Count);
Assert.AreEqual(userList.GetCurrentUserIndex(), 3);
List<User> v = userList.GetUsers();
for (int i = 0; i < 5; i++)
{
Assert.AreEqual(u[i].username, v[i].username);
Assert.AreEqual(u[i].avatar, v[i].avatar);
}
}
/// <summary>
/// Test whether an empty userlist is correctly loaded
/// </summary>
[Test]
public void Test_UserList_Load_Empty()
{
userList.Save();
userList.Load();
Assert.Zero(userList.GetUsers().Count);
Assert.AreEqual(-1, userList.GetCurrentUserIndex());
}
/// <summary>
/// Test if the user save file is not found, a new one will be created and an empty userlist will be loaded
/// </summary>
[Test]
public void Test_UserList_Load_NotFound()
{
List<User> u = new List<User>();
for (int i = 0; i < 5; i++)
{
u.Add(userList.CreateAndAddNewUser($"{username}_{i}", avatar));
}
userList.ChangeCurrentUser(3);
userList.Save();
File.Delete(PATH);
userList.Load();
Assert.Zero(userList.GetUsers().Count);
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 298e34c71d9e68260a1e06d1d4e94be1
guid: cc44c73b32b9af7469b76bd6071f0cf5
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,150 +1,166 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Test the User class
/// </summary>
public class TestUser
[TestFixture]
public class UserTest
{
/// <summary>
/// Reference to the user to be tested
/// </summary>
private User user;
/// <summary>
/// Setup the tests
/// </summary>
[SetUp]
public void Setup_User()
{
user = new User();
}
/// <summary>
/// Test for the creation of a new user
/// </summary>
public void TestNewUser()
[Test]
public void Test_New_User()
{
User user = new User();
Debug.Assert(user != null);
Debug.Assert(user.courses.Count == 0);
Debug.Assert(user.minigames.Count == 0);
Assert.NotNull(user);
Assert.Zero(user.courses.Count);
Assert.Zero(user.minigames.Count);
}
/// <summary>
/// Test whether progress on a new course can be added
/// </summary>
public void TestUserAddCourse()
[Test]
public void Test_User_AddCourse()
{
User user = new User();
Progress p = new Progress();
user.courses.Add(p);
Debug.Assert(user.courses.Count == 1);
Debug.Assert(user.minigames.Count == 0);
Assert.AreEqual(user.courses.Count, 1);
Assert.Zero(user.minigames.Count);
}
/// <summary>
/// Test whether progress on a new minigame can be added
/// </summary>
public void TestUserAddMinigame()
[Test]
public void Test_User_AddMinigame()
{
User user = new User();
Progress p = new Progress();
user.minigames.Add(p);
Debug.Assert(user.courses.Count == 0);
Debug.Assert(user.minigames.Count == 1);
Assert.Zero(user.courses.Count);
Assert.AreEqual(user.minigames.Count, 1);
}
/// <summary>
/// Test GetRecentCourses will return empty when no progress is stored
/// </summary>
public void TestGetRecentCoursesEmpty()
[Test]
public void Test_User_GetRecentCourses_Empty()
{
User user = new User();
Debug.Assert(user.GetRecentCourses().Count == 0);
Assert.Zero(user.GetRecentCourses().Count);
}
/// <summary>
/// Temporary test for GetRecentCourses will return all progress that is stored
/// TEMPORARY test for GetRecentCourses will return all progress that is stored
/// </summary>
public void TestGetRecentCoursesAll()
[Test]
public void Test_User_GetRecentCourses_All()
{
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);
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>
public void TestGetRecommendedCoursesEmpty()
[Test]
public void Test_User_GetRecommendedCourses_Empty()
{
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);
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
/// TEMPORARY test for GetRecommenedCourses will return all progress that is stored
/// </summary>
public void TestGetRecommendedCoursesAll()
[Test]
public void Test_User_GetRecommendedCourses_All()
{
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);
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>
public void TestGetCourseProgressNull()
[Test]
public void Test_User_GetCourseProgress_Null()
{
User user = new User();
Debug.Assert(user.GetCourseProgress(CourseIndex.FINGERSPELLING) == null);
Debug.Assert(user.GetCourseProgress((CourseIndex)100) == null);
Assert.Null(user.GetCourseProgress(CourseIndex.FINGERSPELLING));
Assert.Null(user.GetCourseProgress((CourseIndex)100));
}
/// <summary>
/// Test GetCourseProgress returns correct progress object
/// </summary>
public void TestGetCourseProgressValid()
[Test]
public void Test_User_GetCourseProgress_Valid()
{
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);
Assert.AreEqual(q.Get<CourseIndex>("courseIndex"), CourseIndex.FINGERSPELLING);
Assert.AreEqual(q.Get<float>("courseProgress"), 3.14159265f);
}
/// <summary>
/// Test GetMinigameProgress returns null when minigame cannot be found
/// </summary>
public void TestGetMinigameProgressNull()
[Test]
public void Test_User_GetMinigameProgress_Null()
{
User user = new User();
Debug.Assert(user.GetMinigameProgress(MinigameIndex.SPELLING_BEE) == null);
Debug.Assert(user.GetMinigameProgress((MinigameIndex)100) == null);
Assert.Null(user.GetMinigameProgress(MinigameIndex.SPELLING_BEE));
Assert.Null(user.GetMinigameProgress((MinigameIndex)100));
Progress p = new Progress();
p.AddOrUpdate<MinigameIndex>("minigameIndex", MinigameIndex.SPELLING_BEE);
user.minigames.Add(p);
Debug.Assert(user.GetMinigameProgress(MinigameIndex.HANGMAN) == null);
Assert.Null(user.GetMinigameProgress(MinigameIndex.HANGMAN));
}
/// <summary>
/// Test GetMinigameProgress returns correct progress object
/// </summary>
public void TestGetMinigameProgressValid()
[Test]
public void Test_User_GetMinigameProgress_Valid()
{
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);
Assert.AreEqual(q.Get<CourseIndex>("minigameIndex"), CourseIndex.FINGERSPELLING);
}
}

View File

@@ -0,0 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// This enum is used to identify each of the SignLanguage models
/// </summary>
public enum ModelIndex
{
FINGERSPELLING,
NONE
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 44e682a32ee15cc489bf50f3a06f717b
guid: 6dbd5e1100bc81648b52206df369d0a1
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Barracuda;
/// <summary>
/// This scriptable will hold tupples of Courseindices and models
/// </summary>
[CreateAssetMenu(menuName = "Create new Scriptable/ModelList")]
public class ModelList : ScriptableObject
{
/// <summary>
/// Small class to link a model to a courseIndex irrespective of its position in a list
/// </summary>
[Serializable]
public class ModelTuple
{
/// <summary>
/// ModelIndex to which the model corresponds
/// </summary>
public ModelIndex index;
/// <summary>
/// The model itself
/// </summary>
public NNModel model;
}
/// <summary>
/// Index of the currently active model
/// </summary>
public int currentModelIndex = 0;
/// <summary>
/// A list of all the models
/// </summary>
public List<ModelTuple> models = new List<ModelTuple>();
/// <summary>
/// Get a model by modelindex
/// </summary>
/// <param name="modelIndex">ModelIndex of the model</param>
/// <returns>Model associated with this index, null if no model was found</returns>
public NNModel GetCurrentModel()
{
return models.Find(x => x.model == models[currentModelIndex].model)?.model;
}
/// <summary>
/// Function to find a model-index in the list based on its index
/// </summary>
/// <param name="title"></param>
public void SetCurrentModel(ModelIndex index)
{
currentModelIndex = models.FindIndex((m) => m.index == index);
}
}

View File

@@ -1,8 +1,7 @@
fileFormatVersion: 2
guid: 85997561a67b3e740be145c96c4a0b37
timeCreated: 1455294104
licenseType: Store
guid: 78a3f61c93a08c04496c49ffd10b9021
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using Unity.Barracuda;
using UnityEngine;
/// <summary>
@@ -24,9 +23,9 @@ public class Theme : ScriptableObject
public ThemeIndex index;
/// <summary>
/// Reference to the model used in the SignPredictor
/// The index of the model you want to use
/// </summary>
public NNModel model;
public ModelIndex modelIndex;
/// <summary>
/// List of all learnable words/letters

View File

@@ -15,7 +15,7 @@ public class BackButtonTests
[UnitySetUp]
public IEnumerator SetupFunction()
{
string path = Path.Combine("Assets", "users.json");
string path = $"{Application.persistentDataPath}/users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))

View File

@@ -8,7 +8,8 @@
"CommonScripts",
"InterfacesScripts",
"Unity.TextMeshPro",
"AccountsScripts"
"AccountsScripts",
"SignPredictor"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -15,7 +15,7 @@ public class CourseActivityTests
[UnitySetUp]
public IEnumerator SetupFunction()
{
string path = Path.Combine("Assets", "users.json");
string path = $"{Application.persistentDataPath}/users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))

View File

@@ -15,7 +15,7 @@ public class CourseMenuScreenTest
[UnitySetUp]
public IEnumerator SetupFunction()
{
string path = Path.Combine("Assets", "users.json");
string path = $"{Application.persistentDataPath}/users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))

View File

@@ -15,7 +15,7 @@ public class ListCoursesScreenTest
[UnitySetUp]
public IEnumerator SetupFunction()
{
string path = Path.Combine("Assets", "users.json");
string path = $"{Application.persistentDataPath}/users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))

View File

@@ -15,7 +15,7 @@ public class MainMenuScreenTests
[UnitySetUp]
public IEnumerator SetupFunction()
{
string path = Path.Combine("Assets", "users.json");
string path = $"{Application.persistentDataPath}/users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))

View File

@@ -15,7 +15,7 @@ public class MiniGameActivityScreenTests
[UnitySetUp]
public IEnumerator SetupFunction()
{
string path = Path.Combine("Assets", "users.json");
string path = $"{Application.persistentDataPath}/users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))

View File

@@ -16,7 +16,7 @@ public class StartGamesTests
[UnityTest]
public IEnumerator BootWithUsersTest()
{
string path = Path.Combine("Assets", "users.json");
string path = $"{Application.persistentDataPath}/users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))
@@ -62,7 +62,7 @@ public class StartGamesTests
public IEnumerator BootWithoutUsersTest()
{
// Arrange
string path = Path.Combine("Assets", "users.json");
string path = $"{Application.persistentDataPath}/users.json";
var oneUser = "{}";
using (StreamWriter writer = new StreamWriter(path))

View File

@@ -15,7 +15,7 @@ public class UserButtonTests
[UnitySetUp]
public IEnumerator SetupFunction()
{
string path = Path.Combine("Assets", "users.json");
string path = $"{Application.persistentDataPath}/users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))

View File

@@ -16,6 +16,7 @@ MonoBehaviour:
description: Van vis tot leeuw
index: 2
model: {fileID: 0}
modelIndex: 1
learnables:
- name: Walvis
image: {fileID: 21300000, guid: 2b01165a5836ab14593d7a5862bd6793, type: 3}

View File

@@ -15,6 +15,8 @@ MonoBehaviour:
title: Kleren en Kleuren
description: Van rok tot sok
index: 1
model: {fileID: 0}
modelIndex: 1
learnables:
- name: Blauw
image: {fileID: 21300000, guid: 182fb89eba9c64041bef31ca35c4bcd8, type: 3}

View File

@@ -15,6 +15,8 @@ MonoBehaviour:
title: Familie
description: Van generatie tot generatie
index: 6
model: {fileID: 0}
modelIndex: 1
learnables:
- name: Broer
image: {fileID: 21300000, guid: eecf67266f150f1489717049489cf16d, type: 3}

View File

@@ -15,6 +15,8 @@ MonoBehaviour:
title: Groenten en Fruit
description: Van kers tot pompoen
index: 3
model: {fileID: 0}
modelIndex: 1
learnables:
- name: Aardappel
image: {fileID: 21300000, guid: 2610cdbc24a125f43ada7fed67d8f51b, type: 3}
@@ -79,6 +81,6 @@ MonoBehaviour:
- name: Tomaat
image: {fileID: 21300000, guid: e16943962d961d948a0a6dc73ebf2b0b, type: 3}
clip: {fileID: 32900000, guid: 7ca3dbd42b3033341b3c5c51059643f6, type: 3}
- name: Wrotel
- name: Wortel
image: {fileID: 21300000, guid: 8962e03659a079546810c3ad9c8a211c, type: 3}
clip: {fileID: 32900000, guid: ef9554901c0c6784e9102d5c1fd28a9a, type: 3}

View File

@@ -15,6 +15,8 @@ MonoBehaviour:
title: Hobbies
description: Van schilderen tot reizen
index: 4
model: {fileID: 0}
modelIndex: 1
learnables:
- name: Dansen
image: {fileID: 21300000, guid: 6d405f607ae817744b49f921f0611088, type: 3}

View File

@@ -15,6 +15,8 @@ MonoBehaviour:
title: Huis beschrijven
description: Van zetel tot villa
index: 5
model: {fileID: 0}
modelIndex: 1
learnables:
- name: Keuken
image: {fileID: 21300000, guid: b17ce5bf59092b847b084d3400e7a1b4, type: 3}

View File

@@ -5,8 +5,8 @@
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:63c63e721f65ebb7d871cb9ef49f4752",
"GUID:1631ed2680c61245b8211d943c1639a8",
"GUID:5c2b5ba89f9e74e418232e154bc5cc7a",
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25"
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25",
"GUID:d0b6b39a21908f94fbbd9f2c196a9725"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -56,8 +56,8 @@ public class CourseActivityScreen : MonoBehaviour
Course course = courseList.courses[index];
// vvv TEMPORARY STUFF vvv
playButton.SetActive(course.theme.model != null);
previewButton.SetActive(course.theme.model == null);
playButton.SetActive(course.theme.modelIndex != ModelIndex.NONE);
previewButton.SetActive(course.theme.modelIndex == ModelIndex.NONE);
// ^^^ TEMPORARY STUFF ^^^
title.text = course.title;
@@ -66,6 +66,7 @@ public class CourseActivityScreen : MonoBehaviour
//slider.value = progressValue;
// Set progress
userList.Load();
progress = userList.GetCurrentUser().GetCourseProgress(course.index);
if (progress != null)
progressBar.value = progress.Get<float>("courseProgress");

View File

@@ -42,6 +42,7 @@ public class CourseMenuScreen : MonoBehaviour
/// </summary>
void Start()
{
userList.Load();
User user = userList.GetCurrentUser();
// Recent courses

View File

@@ -30,6 +30,7 @@ public class ListCoursesScreen : MonoBehaviour
/// </summary>
void Start()
{
userList.Load();
User user = userList.GetCurrentUser();
foreach (Course course in courseList.courses)

View File

@@ -17,6 +17,7 @@ public class MainMenuScreen : MonoBehaviour
/// </summary>
void Awake()
{
userList.Load();
if (!File.Exists(UserList.PATH) || userList.GetUsers().Count <= 0)
{
SystemController.GetInstance().LoadNextScene("Accounts/Scenes/UserCreationScreen");

View File

@@ -65,6 +65,7 @@ public class MinigameActivityScreen : MonoBehaviour
/// </summary>
void Start()
{
userList.Load();
GenerateContent();
GenerateHighScores();
}

View File

@@ -32,6 +32,7 @@ public class UserButton : MonoBehaviour
/// </summary>
void Start()
{
userList.Load();
User user = userList.GetCurrentUser();
avatar.sprite = user.avatar;
username.text = user.username;

View File

@@ -1,25 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public class BasicTests
{
// A Test behaves as an ordinary method
[Test]
public void BasicTestsSimplePasses()
{
// Use the Assert class to test conditions
}
// A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use
// `yield return null;` to skip a frame.
[UnityTest]
public IEnumerator BasicTestsWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
}
}

View File

@@ -4,7 +4,10 @@
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"CommonScripts"
"CommonScripts",
"InterfacesScripts",
"Unity.Barracuda",
"SignPredictor"
],
"includePlatforms": [
"Editor"

View File

@@ -0,0 +1,80 @@
using NUnit.Framework;
using UnityEngine;
/// <summary>
/// Test the CourseList class
/// </summary>
[TestFixture]
public class CourseListTest
{
private CourseList courseList;
/// <summary>
/// Setup a CourseList with all possible courses in the enum
/// </summary>
[SetUp]
public void Setup_Minigame()
{
courseList = ScriptableObject.CreateInstance<CourseList>();
// Add a course for each index in the enum
// Dumb way to access each index in the enum, couldn't find a different way to do it though
foreach (var field in typeof(CourseIndex).GetFields())
{
if (field.IsLiteral)
{
CourseIndex value = (CourseIndex)field.GetValue(null);
string name = field.Name;
Course course = ScriptableObject.CreateInstance<Course>();
// This is all we will need to distinguish
course.index = value;
course.title = name;
// Insert in front to guarantee that courseIndex will not line up with listIndex
courseList.courses.Insert(0, course);
}
}
}
/// <summary>
/// Check if all courses can be correctly fetched via GetCourseByIndex
/// </summary>
[Test]
public void TestGetMinigameByIndex()
{
foreach (var field in typeof(CourseIndex).GetFields())
{
if (field.IsLiteral)
{
CourseIndex value = (CourseIndex)field.GetValue(null);
string name = field.Name;
Course m = courseList.GetCourseByIndex(value);
Assert.AreEqual(m.title, name);
}
}
}
/// <summary>
/// Check if all courses can be correctly set as current via SetCurrentCourse
/// </summary>
[Test]
public void TestSetCurrentMinigame()
{
foreach (var field in typeof(CourseIndex).GetFields())
{
if (field.IsLiteral)
{
CourseIndex value = (CourseIndex)field.GetValue(null);
string name = field.Name;
courseList.SetCurrentCourse(value);
// Fetch the current course and check if its name is the same as the one we made into the current one
Course m = courseList.courses[courseList.currentCourseIndex];
Assert.AreEqual(m.title, name);
}
}
}
}

View File

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

View File

@@ -0,0 +1,80 @@
using NUnit.Framework;
using UnityEngine;
/// <summary>
/// Test the MinigameList class
/// </summary>
[TestFixture]
public class MinigameListTest
{
private MinigameList minigameList;
/// <summary>
/// Setup a minigameList with all possible minigames in the enum
/// </summary>
[SetUp]
public void Setup_Minigame()
{
minigameList = ScriptableObject.CreateInstance<MinigameList>();
// Add a minigame for each index in the enum
// Dumb way to access each index in the enum, couldn't find a different way to do it though
foreach (var field in typeof(MinigameIndex).GetFields())
{
if (field.IsLiteral)
{
MinigameIndex value = (MinigameIndex)field.GetValue(null);
string name = field.Name;
Minigame m = ScriptableObject.CreateInstance<Minigame>();
// This is all we will need to distinguish
m.index = value;
m.title = name;
// Insert in front to guarantee that MinigameIndex will not line up with listIndex
minigameList.minigames.Insert(0, m);
}
}
}
/// <summary>
/// Check if all minigames can be correctly fetched via GetMinigameByIndex
/// </summary>
[Test]
public void TestGetMinigameByIndex()
{
foreach (var field in typeof(MinigameIndex).GetFields())
{
if (field.IsLiteral)
{
MinigameIndex value = (MinigameIndex)field.GetValue(null);
string name = field.Name;
Minigame m = minigameList.GetMinigameByIndex(value);
Assert.AreEqual(m.title, name);
}
}
}
/// <summary>
/// Check if all minigames can be correctly set as current via SetCurrentMinigame
/// </summary>
[Test]
public void TestSetCurrentMinigame()
{
foreach (var field in typeof(MinigameIndex).GetFields())
{
if (field.IsLiteral)
{
MinigameIndex value = (MinigameIndex)field.GetValue(null);
string name = field.Name;
minigameList.SetCurrentMinigame(value);
// Fetch the current minigame and check if its name is the same as the one we made into the current one
Minigame m = minigameList.minigames[minigameList.currentMinigameIndex];
Assert.AreEqual(m.title, name);
}
}
}
}

View File

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

View File

@@ -0,0 +1,80 @@
using NUnit.Framework;
using Unity.Barracuda;
using UnityEngine;
/// <summary>
/// Test the ModelList class
/// </summary>
[TestFixture]
public class ModelListTest
{
private ModelList modelList;
/// <summary>
/// Setup a ModelList with all possible Models in the enum
/// </summary>
[SetUp]
public void Setup_Model()
{
modelList = ScriptableObject.CreateInstance<ModelList>();
// Add a Model for each index in the enum
// Dumb way to access each index in the enum, couldn't find a different way to do it though
foreach (var field in typeof(ModelIndex).GetFields())
{
if (field.IsLiteral)
{
ModelIndex value = (ModelIndex)field.GetValue(null);
string name = field.Name;
ModelList.ModelTuple model = new ModelList.ModelTuple();
// This is all we will need to distinguish
model.index = value;
// Insert in front to guarantee that ModelIndex will not line up with listIndex
modelList.models.Insert(0, model);
}
}
}
/// <summary>
/// Check if current model can be correctly gotten as current via GetCurrentModel
/// </summary>
[Test]
public void TestGetCurrentModel()
{
System.Random random = new System.Random();
ModelIndex value = (ModelIndex)random.Next(modelList.models.Count);
modelList.SetCurrentModel(value);
Assert.AreEqual(modelList.models[modelList.currentModelIndex].model, modelList.GetCurrentModel());
// Check if empty model fails gracefully (returns null)
Assert.IsNull(ScriptableObject.CreateInstance<ModelList>().GetCurrentModel());
}
/// <summary>
/// Check if all models can be correctly set as current via SetCurrentModel
/// </summary>
[Test]
public void TestSetCurrentModel()
{
foreach (var field in typeof(ModelIndex).GetFields())
{
if (field.IsLiteral)
{
ModelIndex value = (ModelIndex)field.GetValue(null);
string name = field.Name;
modelList.SetCurrentModel(value);
// Fetch the current model and check if its name is the same as the one we made into the current one
ModelList.ModelTuple m = modelList.models[modelList.currentModelIndex];
Assert.AreEqual(m.index, value);
Assert.IsTrue(m.model is NNModel || m.model is null);
}
}
ModelList emptyList = ScriptableObject.CreateInstance<ModelList>();
emptyList.SetCurrentModel(ModelIndex.FINGERSPELLING);
Assert.IsTrue(emptyList.currentModelIndex == -1);
}
}

View File

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

View File

@@ -0,0 +1,60 @@
using NUnit.Framework;
using UnityEngine;
/// <summary>
/// Test the ThemeList class
/// </summary>
[TestFixture]
public class ThemeListTest
{
private ThemeList themeList;
/// <summary>
/// Setup a ThemeList with all possible themes in the enum
/// </summary>
[SetUp]
public void Setup_Minigame()
{
themeList = ScriptableObject.CreateInstance<ThemeList>();
// Add a theme for each index in the enum
// Dumb way to access each index in the enum, couldn't find a different way to do it though
foreach (var field in typeof(ThemeIndex).GetFields())
{
if (field.IsLiteral)
{
ThemeIndex value = (ThemeIndex)field.GetValue(null);
string name = field.Name;
Theme theme = ScriptableObject.CreateInstance<Theme>();
// This is all we will need to distinguish
theme.index = value;
theme.title = name;
// Insert in front to guarantee that themeIndex will not line up with listIndex
themeList.themes.Insert(0, theme);
}
}
}
/// <summary>
/// Check if all themes can be correctly set as current via SetCurrentTheme
/// </summary>
[Test]
public void TestSetCurrentMinigame()
{
foreach (var field in typeof(ThemeIndex).GetFields())
{
if (field.IsLiteral)
{
ThemeIndex value = (ThemeIndex)field.GetValue(null);
string name = field.Name;
themeList.SetCurrentTheme(value);
// Fetch the current theme and check if its name is the same as the one we made into the current one
Theme m = themeList.themes[themeList.currentThemeIndex];
Assert.AreEqual(m.title, name);
}
}
}
}

View File

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

View File

@@ -0,0 +1,40 @@
using NUnit.Framework;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Test the Theme class
/// </summary>
[TestFixture]
public class ThemeTest
{
private Theme theme;
private List<string> names = new List<string>() { "appel", "peer", "banaan" };
/// <summary>
/// Setup a theme with some learnables in it
/// </summary>
[SetUp]
public void Setup()
{
theme = ScriptableObject.CreateInstance<Theme>();
foreach (string name in names)
{
Learnable learnable = new Learnable();
learnable.name = name;
theme.learnables.Add(learnable);
}
}
/// <summary>
/// Test if all the learnables are stored in the theme
/// </summary>
[Test]
public void TestThemeLearnables()
{
// Check if each of the learnables is kept
foreach (Learnable learnable in theme.learnables)
{
names.Remove(learnable.name);
}
// Assert that all items have been checked
Assert.IsTrue(names.Count == 0);
}
}

View File

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

View File

@@ -490,6 +490,10 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 6b3f784c065813a4a8364b1299284816, type: 3}
m_Name:
m_EditorClassIdentifier:
feedbackText: {fileID: 4318122121437849762}
feedbackProgress: {fileID: 4318122121437849761}
feedbackProgressImage: {fileID: 4318122121437849760}
signPredictor: {fileID: 883853268}
previewModel: {fileID: 5022602860645237092, guid: e6d85df707405ad4f97c23b07227ee99, type: 3}
feedbackProgressBar: {fileID: 4318122121437849759}
previewMessage: {fileID: 2070775951}
@@ -501,9 +505,10 @@ MonoBehaviour:
userList: {fileID: 11400000, guid: 072bec636a40f7e4e93b0ac624a3bda2, type: 2}
courselist: {fileID: 11400000, guid: a7ab583094b7897468bbca9243717608, type: 2}
ResultPanel: {fileID: 1383144366}
ResultsTitle: {fileID: 822960079}
ResultsDecription: {fileID: 100123246}
CoursesButton: {fileID: 839294691}
timeSpent: {fileID: 77614869}
feedback: {fileID: 1714882683}
--- !u!1 &361280475
GameObject:
m_ObjectHideFlags: 0
@@ -1220,8 +1225,8 @@ MonoBehaviour:
m_OnClick:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 301088551}
m_TargetAssemblyTypeName: TemplateCourse, Assembly-CSharp
- m_Target: {fileID: 1335886461}
m_TargetAssemblyTypeName: BackButton, CommonScripts
m_MethodName: Back
m_Mode: 1
m_Arguments:
@@ -1299,10 +1304,11 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 043ccd99cf82b3cc9bf2e00956ce2b93, type: 3}
m_Name:
m_EditorClassIdentifier:
model: {fileID: 5022602860645237092, guid: e6d85df707405ad4f97c23b07227ee99, type: 3}
modelList: {fileID: 11400000, guid: 39516e4e6e56f0f4f80647d9c4d8034c, type: 2}
modelInfoFile: {fileID: 4900000, guid: fb8b51022bdcd654a9f29c054832a1b5, type: 3}
configAsset: {fileID: 4900000, guid: 6288c43cdca97374782dac1ea87aa029, type: 3}
screen: {fileID: 378145456}
screen2: {fileID: 0}
--- !u!4 &883853269
Transform:
m_ObjectHideFlags: 0
@@ -1836,6 +1842,17 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 8299246693487308515, guid: 3bccdf365a4fbea4d8fa1aa461d3dc5c, type: 3}
m_PrefabInstance: {fileID: 1335886459}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1335886461 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4518652150503380115, guid: 3bccdf365a4fbea4d8fa1aa461d3dc5c, type: 3}
m_PrefabInstance: {fileID: 1335886459}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c3dd279b546423e4a8a1b28819a6c4a1, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1383144366
GameObject:
m_ObjectHideFlags: 0
@@ -2249,17 +2266,6 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 4318122119930585316, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 4318122121437849758}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1714882683 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4318122119930585317, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 4318122121437849758}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4318122121437849759}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 44e682a32ee15cc489bf50f3a06f717b, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1773033262
GameObject:
m_ObjectHideFlags: 0
@@ -2835,6 +2841,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4318122119930585317, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
propertyPath: m_Enabled
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4318122119930585317, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
propertyPath: signPredictor
value:
@@ -2862,3 +2872,36 @@ GameObject:
m_CorrespondingSourceObject: {fileID: 4318122119930585319, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 4318122121437849758}
m_PrefabAsset: {fileID: 0}
--- !u!114 &4318122121437849760 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4318122120334233319, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 4318122121437849758}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &4318122121437849761 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4318122119968934242, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 4318122121437849758}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 67db9e8f0e2ae9c40bc1e2b64352a6b4, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &4318122121437849762 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4318122120222767928, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 4318122121437849758}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:

View File

@@ -6,7 +6,9 @@
"AccountsScripts",
"InterfacesScripts",
"SignPredictor",
"Unity.Barracuda"
"Unity.Barracuda",
"Tween",
"SignPredictorInterfaces"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -1,14 +1,16 @@
using System;
using System.Collections;
using TMPro;
using Unity.Barracuda;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
using DigitalRuby.Tween;
/// <summary>
/// TemplateCourse scene manager
/// </summary>
public class TemplateCourse : MonoBehaviour
public class TemplateCourse : AbstractFeedback
{
// vvv TEMPORARY STUFF vvv
public NNModel previewModel;
@@ -87,6 +89,16 @@ public class TemplateCourse : MonoBehaviour
/// </summary>
public GameObject ResultPanel;
/// <summary>
/// Reference to the title on the results panel
/// </summary>
public TMP_Text ResultsTitle;
/// <summary>
/// Reference to the description on the results panel
/// </summary>
public TMP_Text ResultsDecription;
/// <summary>
/// Button to go back to courses list
/// </summary>
@@ -102,10 +114,37 @@ public class TemplateCourse : MonoBehaviour
/// </summary>
public TMP_Text timeSpent;
/// <summary>
/// Reference to the feedback script on the Feedback prefab
/// Reference to the feedback field
/// </summary>
public Feedback feedback;
public TMP_Text feedbackText;
/// <summary>
/// Reference to the progress bar
/// </summary>
public Slider feedbackProgress;
/// <summary>
/// Reference to the progress bar image, so we can add fancy colors
/// </summary>
public Image feedbackProgressImage;
/// <summary>
/// Timer to keep track of how long a incorrect sign is performed
/// </summary>
protected DateTime timer;
/// <summary>
/// Current predicted sign
/// </summary>
protected string predictedSign = null;
/// <summary>
/// Previous incorrect sign, so we can keep track whether the user is wrong or the user is still changing signs
/// </summary>
protected string previousIncorrectSign = null;
/// <summary>
/// This function is called when the script is initialised.
@@ -114,20 +153,27 @@ public class TemplateCourse : MonoBehaviour
/// Then it checks whether or not the User has started the course yet, to possibly create a new progress atribute for the course.
/// Then it sets up the course-screen to display relevant information from the course-scriptable.
/// </summary>
void Awake()
void Start()
{
StartGameController();
signPredictor.SetModel(course.theme.modelIndex);
AddSelfAsListener();
}
public void StartGameController()
{
// Setting up course
course = courselist.courses[courselist.currentCourseIndex];
feedback.signPredictor.model = course.theme.model;
maxWords = course.theme.learnables.Count;
// vvv TEMPORARY STUFF vvv
feedbackProgressBar.SetActive(course.theme.model != null);
previewMessage.SetActive(course.theme.model == null);
feedback.signPredictor.model = previewModel;
feedbackProgressBar.SetActive(course.theme.modelIndex != ModelIndex.NONE);
previewMessage.SetActive(course.theme.modelIndex == ModelIndex.NONE);
// ^^^ TEMPORARY STUFF ^^^
// Create entry in current user for keeping track of progress
userList.Load();
user = userList.GetCurrentUser();
progress = user.GetCourseProgress(course.index);
if (progress == null)
@@ -152,23 +198,6 @@ public class TemplateCourse : MonoBehaviour
ResultPanel.SetActive(false);
// Set the startTime
startMoment = DateTime.Now;
// Set callbacks
feedback.getSignCallback = () =>
{
if (currentWordIndex < course.theme.learnables.Count)
{
return course.theme.learnables[currentWordIndex].name;
}
return null;
};
feedback.predictSignCallback = (sign) =>
{
if (sign == course.theme.learnables[currentWordIndex].name)
{
NextSign();
}
};
}
/// <summary>
@@ -259,6 +288,12 @@ public class TemplateCourse : MonoBehaviour
// Show the "finished" screen
ResultPanel.SetActive(true);
// Set the correct title
ResultsTitle.text = course.title + " voltooid!";
// Set the correct description
ResultsDecription.text = "Goed gedaan! Je kan nu spelletjes spelen met " + course.title + " om verder te oefenen!";
// Set the total time spent UI
TimeSpan time = DateTime.Now - startMoment;
timeSpent.text = time.ToString(@"hh\:mm\:ss");
@@ -269,4 +304,98 @@ public class TemplateCourse : MonoBehaviour
progress.AddOrUpdate<float>("courseProgress", 1f);
userList.Save();
}
protected override IEnumerator UpdateFeedback()
{
// Get current sign
string currentSign = course.theme.learnables[currentWordIndex].name;
// Get the predicted sign
if (signPredictor != null && signPredictor.learnableProbabilities != null &&
currentSign != null && signPredictor.learnableProbabilities.ContainsKey(currentSign))
{
float accuracy = signPredictor.learnableProbabilities[currentSign];
if (feedbackText != null && feedbackProgressImage != null)
{
if (accuracy > 0.90)
{
feedbackText.text = "Goed";
feedbackText.color = Color.green;
feedbackProgressImage.color = Color.green;
}
else if (accuracy > 0.80)
{
feedbackText.text = "Bijna...";
Color col = new Color(0xff / 255.0f, 0x66 / 255.0f, 0x00 / 255.0f);
feedbackText.color = col;
feedbackProgressImage.color = col;
}
else
{
feedbackText.text = "Detecteren...";
feedbackText.color = Color.red;
feedbackProgressImage.color = Color.red;
}
float oldValue = feedbackProgress.value;
// use an exponential scale
float newValue = Mathf.Exp(4 * (accuracy - 1.0f));
feedbackProgress.gameObject.Tween("FeedbackUpdate", oldValue, newValue, 0.2f, TweenScaleFunctions.CubicEaseInOut, (t) =>
{
if (feedbackProgress != null)
{
feedbackProgress.value = t.CurrentValue;
}
});
}
// Check whether (in)correct sign has high accuracy
foreach (var kv in signPredictor.learnableProbabilities)
{
if (kv.Value > 0.90)
{
predictedSign = kv.Key;
// Correct sign
if (predictedSign == currentSign)
{
yield return new WaitForSeconds(1.0f);
CheckEquality(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
// Incorrect sign
else
{
if (previousIncorrectSign != predictedSign)
{
timer = DateTime.Now;
previousIncorrectSign = predictedSign;
}
else if (DateTime.Now - timer > TimeSpan.FromSeconds(2.0f))
{
CheckEquality(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
}
break;
}
}
}
else if (feedbackProgress != null)
{
feedbackProgress.value = 0.0f;
}
}
private void CheckEquality(string predicted)
{
if(predicted == course.theme.learnables[currentWordIndex].name)
{
NextSign();
}
}
}

View File

@@ -38,7 +38,7 @@ RenderSettings:
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1}
m_IndirectSpecularColor: {r: 0.44657826, g: 0.49641263, b: 0.57481676, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
@@ -2570,7 +2570,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 4
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &896343212
GameObject:
@@ -3507,7 +3507,7 @@ RectTransform:
- {fileID: 1892638588}
- {fileID: 56162990}
m_Father: {fileID: 0}
m_RootOrder: 2
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
@@ -3526,6 +3526,10 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 2db44635e0eb1e9429a2e6195785364d, type: 3}
m_Name:
m_EditorClassIdentifier:
feedbackText: {fileID: 5233312448025626833}
feedbackProgress: {fileID: 5233312447201393291}
feedbackProgressImage: {fileID: 5233312447919013134}
signPredictor: {fileID: 1991376311}
themelist: {fileID: 11400000, guid: a247e2ce790f0f746a3bc521e6ab7d58, type: 2}
letterPrefab: {fileID: 4639383499500021565, guid: c3e66e8957864914cb022af914df6a28, type: 3}
letterContainer: {fileID: 1870283439}
@@ -3545,11 +3549,11 @@ MonoBehaviour:
wordsText: {fileID: 1704021883}
scoreText: {fileID: 90551462}
minigame: {fileID: 11400000, guid: 165c1d9867275924d9720d409e935f95, type: 2}
minigamelist: {fileID: 11400000, guid: 51453f9b41bc72f468ba3e67ab622f8f, type: 2}
userList: {fileID: 11400000, guid: 072bec636a40f7e4e93b0ac624a3bda2, type: 2}
Scoreboard: {fileID: 1007532375}
EntriesGrid: {fileID: 1391137944}
scoreboardEntry: {fileID: 9154151134820372555, guid: d4a3a228b08d61847acc6da35b44e52c, type: 3}
feedback: {fileID: 5233312447513285388}
gottogamebutton: {fileID: 1581633295}
--- !u!1001 &1290865991
PrefabInstance:
@@ -6244,7 +6248,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 043ccd99cf82b3cc9bf2e00956ce2b93, type: 3}
m_Name:
m_EditorClassIdentifier:
model: {fileID: 5022602860645237092, guid: e6d85df707405ad4f97c23b07227ee99, type: 3}
modelList: {fileID: 11400000, guid: 39516e4e6e56f0f4f80647d9c4d8034c, type: 2}
modelInfoFile: {fileID: 4900000, guid: fb8b51022bdcd654a9f29c054832a1b5, type: 3}
configAsset: {fileID: 4900000, guid: 6288c43cdca97374782dac1ea87aa029, type: 3}
screen: {fileID: 1649505745}
@@ -6262,7 +6266,7 @@ Transform:
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 5
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2001212056
GameObject:
@@ -6565,22 +6569,6 @@ GameObject:
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &5233312447513285388
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5233312447513285390}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 44e682a32ee15cc489bf50f3a06f717b, type: 3}
m_Name:
m_EditorClassIdentifier:
feedbackText: {fileID: 0}
feedbackProgress: {fileID: 0}
feedbackProgressImage: {fileID: 0}
signPredictor: {fileID: 1991376311}
--- !u!224 &5233312447513285389
RectTransform:
m_ObjectHideFlags: 0
@@ -6596,7 +6584,7 @@ RectTransform:
- {fileID: 5233312448025626847}
- {fileID: 5233312447201393292}
m_Father: {fileID: 0}
m_RootOrder: 3
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0}
m_AnchorMax: {x: 0.5, y: 0}
@@ -6612,7 +6600,6 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 5233312447513285389}
- component: {fileID: 5233312447513285388}
m_Layer: 5
m_Name: Feedback
m_TagString: Untagged

View File

@@ -1,12 +1,13 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using DigitalRuby.Tween;
public class HangmanGameController : MonoBehaviour
public class HangmanGameController : AbstractFeedback
{
/// <summary>
/// The scriptable with all the themes, will be used to select a random word for hangman.
@@ -160,6 +161,12 @@ public class HangmanGameController : MonoBehaviour
/// </summary>
public Minigame minigame;
/// <summary>
/// We keep the minigamelist as well so that the minigame-index doesn't get reset
/// DO NOT REMOVE
/// </summary>
public MinigameList minigamelist;
/// <summary>
/// Reference to the user list to access the current user
/// </summary>
@@ -195,11 +202,6 @@ public class HangmanGameController : MonoBehaviour
/// </summary>
public GameObject scoreboardEntry;
/// <summary>
/// Accuracy feeback object
/// </summary>
public Feedback feedback;
/// <summary>
/// The button to go into the game
/// </summary>
@@ -210,8 +212,48 @@ public class HangmanGameController : MonoBehaviour
/// </summary>
private String currentsign = "";
/// <summary>
/// Reference to the feedback field
/// </summary>
public TMP_Text feedbackText;
/// <summary>
/// Reference to the progress bar
/// </summary>
public Slider feedbackProgress;
/// <summary>
/// Reference to the progress bar image, so we can add fancy colors
/// </summary>
public Image feedbackProgressImage;
/// <summary>
/// Timer to keep track of how long a incorrect sign is performed
/// </summary>
protected DateTime timer;
/// <summary>
/// Current predicted sign
/// </summary>
protected string predictedSign = null;
/// <summary>
/// Previous incorrect sign, so we can keep track whether the user is wrong or the user is still changing signs
/// </summary>
protected string previousIncorrectSign = null;
// Start is called before the first frame update
void Start()
{
StartController();
signPredictor.SetModel(ModelIndex.FINGERSPELLING);
AddSelfAsListener();
}
/// <summary>
/// Called at the start of the scene AND when the scene is replayed
/// </summary>
public void StartController()
{
// Make sure the mode starts at zero
mode = 0;
@@ -223,6 +265,7 @@ public class HangmanGameController : MonoBehaviour
playerPanel.SetActive(true);
// Create entry in current user for keeping track of progress
userList.Load();
user = userList.GetCurrentUser();
Progress progress = user.GetMinigameProgress(minigame.index);
if (progress == null)
@@ -234,16 +277,6 @@ public class HangmanGameController : MonoBehaviour
user.minigames.Add(progress);
}
userList.Save();
// Set calllbacks
feedback.getSignCallback = () =>
{
return "A";
};
feedback.predictSignCallback = (sign) =>
{
currentsign = sign;
};
}
/// <summary>
@@ -272,7 +305,7 @@ public class HangmanGameController : MonoBehaviour
DeleteWord();
DisplayWord(currentWord);
replayButton.onClick.AddListener(Start);
replayButton.onClick.AddListener(StartController);
// Call to display the first image, corresponding to a clean image.
ChangeSprite();
}
@@ -331,7 +364,7 @@ public class HangmanGameController : MonoBehaviour
public void TwoPlayer()
{
if (currentWord.Length >= 2)
{
{
StartGame();
}
}
@@ -341,9 +374,9 @@ public class HangmanGameController : MonoBehaviour
/// </summary>
public void Update()
{
if(mode == 1)
{
if (currentsign != "")
if (mode == 1)
{
if (currentsign != "" && currentsign != null)
{
char letter = currentsign.ToLower()[0];
currentsign = "";
@@ -359,7 +392,7 @@ public class HangmanGameController : MonoBehaviour
if (Input.GetKey(KeyCode.Backspace))
{
// Remove the last letter from the currentword
if(currentWord.Length > 0)
if (currentWord.Length > 0)
{
currentWord = currentWord.Substring(0, currentWord.Length - 1);
inputTextField.text = currentWord.ToString();
@@ -372,7 +405,7 @@ public class HangmanGameController : MonoBehaviour
inputTextField.text = currentWord.ToString();
}
}
gottogamebutton.SetActive(currentWord.Length >2);
gottogamebutton.SetActive(currentWord.Length > 2);
}
if (mode == 2)
@@ -384,7 +417,7 @@ public class HangmanGameController : MonoBehaviour
// For the first input char given by the user, check if the letter is in the word that needs to be spelled.
// Check to make sure the inputfield is not empty
if (currentsign != "")
if (currentsign != null && currentsign != "")
{
char firstLetter = currentsign.ToLower()[0];
currentsign = "";
@@ -534,21 +567,6 @@ public class HangmanGameController : MonoBehaviour
}
}
/// <summary>
/// Randomly shuffle the list of words
/// </summary>
private void ShuffleWords()
{
for (int i = words.Length - 1; i > 0; i--)
{
// Generate a random index between 0 and i (inclusive)
int j = UnityEngine.Random.Range(0, i + 1);
// Swap the values at indices i and j
(words[j], words[i]) = (words[i], words[j]);
}
}
/// <summary>
/// Update and save the scores
/// </summary>
@@ -722,4 +740,94 @@ public class HangmanGameController : MonoBehaviour
rank++;
}
}
protected override IEnumerator UpdateFeedback()
{
// Get current sign
string currentSign = "A";
// Get the predicted sign
if (signPredictor != null && signPredictor.learnableProbabilities != null &&
currentSign != null && signPredictor.learnableProbabilities.ContainsKey(currentSign))
{
float accuracy = signPredictor.learnableProbabilities[currentSign];
if (feedbackText != null && feedbackProgressImage != null)
{
if (accuracy > 0.90)
{
feedbackText.text = "Goed";
feedbackText.color = Color.green;
feedbackProgressImage.color = Color.green;
}
else if (accuracy > 0.80)
{
feedbackText.text = "Bijna...";
Color col = new Color(0xff / 255.0f, 0x66 / 255.0f, 0x00 / 255.0f);
feedbackText.color = col;
feedbackProgressImage.color = col;
}
else
{
feedbackText.text = "Detecteren...";
feedbackText.color = Color.red;
feedbackProgressImage.color = Color.red;
}
float oldValue = feedbackProgress.value;
// use an exponential scale
float newValue = Mathf.Exp(4 * (accuracy - 1.0f));
feedbackProgress.gameObject.Tween("FeedbackUpdate", oldValue, newValue, 0.2f, TweenScaleFunctions.CubicEaseInOut, (t) =>
{
if (feedbackProgress != null)
{
feedbackProgress.value = t.CurrentValue;
}
});
}
// Check whether (in)correct sign has high accuracy
foreach (var kv in signPredictor.learnableProbabilities)
{
if (kv.Value > 0.90)
{
predictedSign = kv.Key;
// Correct sign
if (predictedSign == currentSign)
{
yield return new WaitForSeconds(1.0f);
CheckEquality(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
// Incorrect sign
else
{
if (previousIncorrectSign != predictedSign)
{
timer = DateTime.Now;
previousIncorrectSign = predictedSign;
}
else if (DateTime.Now - timer > TimeSpan.FromSeconds(2.0f))
{
CheckEquality(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
}
break;
}
}
}
else if (feedbackProgress != null)
{
feedbackProgress.value = 0.0f;
}
}
private void CheckEquality(string sign)
{
currentsign = sign;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
%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: cd3d1c3a6cf7ef07abb343f8862a7435, type: 3}
m_Name: Graceland
m_EditorClassIdentifier:
firstSymbolTime: 0.5
spawnPeriod: 2
duration: 127
song: {fileID: 8300000, guid: e01ded5e221494bdbaab8674c0354f64, type: 3}

View File

@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 493623c166a735445b4283396018d38b
guid: 7a55c88547b9345fe87aecd1398550c9
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 4890085278179872738
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
%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: ceeae47a3efc2206299ddf1cc31043c2, type: 3}
m_Name: SongList
m_EditorClassIdentifier:
currentSongIndex: 0
songs:
- {fileID: 11400000, guid: 0f5b5e156c628bf189df5723ad2aab96, type: 2}
- {fileID: 11400000, guid: 7a55c88547b9345fe87aecd1398550c9, type: 2}

View File

@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 2d994b80f30361c449f5504b6ddb859a
timeCreated: 1455295548
licenseType: Store
guid: 4f0ce70309bb901feb28199a82a7d195
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
%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: cd3d1c3a6cf7ef07abb343f8862a7435, type: 3}
m_Name: SunnySafari
m_EditorClassIdentifier:
firstSymbolTime: 1.3
spawnPeriod: 1.99
duration: 164
song: {fileID: 8300000, guid: d7ca1b558b7b064cdb6f9d6c951b1522, type: 3}

View File

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

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -40,6 +41,23 @@ public class JustSignController : MonoBehaviour
/// </summary>
public Minigame minigame;
/// <summary>
/// We keep the minigamelist as well so that the minigame-index doesn't get reset
/// DO NOT REMOVE
/// </summary>
public MinigameList minigamelist;
/// </summary>
/// Reference to the list of available songs
/// </summary>
public SongList songList;
/// <summary>
/// Reference to the currently used song
/// </summary>
private Song currentSong;
/// <summary>
/// The zone that the player should be hitting with his or her inputs
/// </summary>
@@ -55,11 +73,6 @@ public class JustSignController : MonoBehaviour
/// </summary>
public Transform symbolContainer;
/// <summary>
/// All of the available themes
/// </summary>
private ThemeList themeList;
/// <summary>
/// The theme we are currently using
/// </summary>
@@ -80,6 +93,11 @@ public class JustSignController : MonoBehaviour
/// </summary>
private int score;
/// <summary>
/// Have the symbols started spawning or not
/// </summary>
private bool gameIsActive = false;
/// <summary>
/// Width and height of the symbols
/// </summary>
@@ -146,22 +164,150 @@ public class JustSignController : MonoBehaviour
private float lastSpawn;
/// <summary>
/// Determines every how many seconds a symbol should spawn (will become music-dependent later on)
/// Time at which the game started, needed to know when to stop
/// </summary>
private float spawnPeriod = 3.0f;
private float beginTime;
/// <summary>
/// Time at which the last symbol should spawn
/// </summary>
private float lastSymbolTime;
/// <summary>
/// Counter that keeps track of how many signs get you score "perfect"
/// </summary>
private int perfectSigns;
/// <summary>
/// Counter that keeps track of how many signs get you score "good"
/// </summary>
private int goodSigns;
/// <summary>
/// Counter that keeps track of how many signs get you score "meh"
/// </summary>
private int mehSigns;
/// <summary>
/// Counter that keeps track of how many signs get you score "perfect"
/// </summary>
private int terribleSigns;
/// <summary>
/// Counter that keeps track of how many signs done incorrectly
/// </summary>
private int incorrectSigns;
/// <summary>
/// Reference to the scoreboard entries container
/// </summary>
public Transform scoreboardEntriesContainer;
/// <summary>
/// The GameObjects representing the letters
/// </summary>
private List<GameObject> scoreboardEntries = new List<GameObject>();
/// <summary>
/// Reference to the ScoreboardEntry prefab
/// </summary>
public GameObject scoreboardEntry;
/// <summary>
/// Reference to the user list to access the current user
/// </summary>
public UserList userList;
/// <summary>
/// Reference to the current user
/// </summary>
private User user;
/// <summary>
/// LPM
/// </summary>
public TMP_Text lpmText;
/// <summary>
/// Perfect Signs Score
/// </summary>
public TMP_Text perfectSignsText;
/// <summary>
/// Good Signs Score
/// </summary>
public TMP_Text goodSignsText;
/// <summary>
/// Meh Signs Score
/// </summary>
public TMP_Text mehSignsText;
/// <summary>
/// Perfect Signs Score
/// </summary>
public TMP_Text terribleSignsText;
/// <summary>
/// Signs that were not found
/// </summary>
public TMP_Text notFoundSignsText;
/// <summary>
/// Score
/// </summary>
public TMP_Text scoreText;
/// <summary>
/// Reference to the gameEnded panel, so we can update its display
/// </summary>
public GameObject gameEndedPanel;
/// <summary>
/// Start is called before the first frame update
/// </summary>
void Start()
{
public void Start()
{
perfectSigns = 0;
goodSigns = 0;
mehSigns = 0;
terribleSigns = 0;
incorrectSigns = 0;
score = 0;
gameEndedPanel.SetActive(false);
// Create entry in current user for keeping track of progress
userList.Load();
user = userList.GetCurrentUser();
Progress progress = user.GetMinigameProgress(minigame.index);
if (progress == null)
{
progress = new Progress();
progress.AddOrUpdate<MinigameIndex>("minigameIndex", minigame.index);
progress.AddOrUpdate<List<Score>>("highestScores", new List<Score>());
progress.AddOrUpdate<List<Score>>("latestScores", new List<Score>());
user.minigames.Add(progress);
}
userList.Save();
scoreDisplay.text = "Score: " + score.ToString();
currentTheme = minigame.themeList.themes[minigame.themeList.currentThemeIndex];
words.AddRange(currentTheme.learnables);
//currentTheme = FindThemeByName(PlayerPrefs.GetString("themeName"));
//words = currentTheme.words;
lastSpawn = Time.time;
SpawnNewSymbol();
currentSong = songList.songs[songList.currentSongIndex];
AudioSource.PlayClipAtPoint(currentSong.song, Vector3.zero, 1.0f);
beginTime = Time.time;
lastSymbolTime = beginTime + currentSong.duration - 1920.0f / moveSpeed;
StartCoroutine(WaitThenStart(currentSong.firstSymbolTime));
}
/// <summary>
/// Wait for a given amount of time (specified in song) before spawning symbols
/// </summary>
IEnumerator WaitThenStart(float nrOfSeconds)
{
//yield on a new YieldInstruction that waits for nrOfSeconds seconds
yield return new WaitForSeconds(nrOfSeconds);
gameIsActive = true;
}
/// <summary>
@@ -169,56 +315,86 @@ public class JustSignController : MonoBehaviour
/// </summary>
void Update()
{
int matchedSymbolIndex = -1;
for (int i = 0; i < activeWords.Count; i++) {
if (activeWords[i].ToLower() == answerField.text.ToLower()) {
matchedSymbolIndex = i;
}
}
// Destroy the oldest symbol if the current input matches it
if (matchedSymbolIndex >= 0) {
int difference = Math.Abs((int) (activeSymbols[matchedSymbolIndex].transform.position.x - hitZone.transform.position.x));
if (difference < perfectBoundary) {
feedBack.text = "Perfect!";
score += perfectScore;
} else if (difference < goodBoundary) {
feedBack.text = "Good!";
score += goodScore;
} else if (difference < mehBoundary) {
feedBack.text = "Meh...";
score += mehScore;
} else {
feedBack.text = "Terrible!";
score += terribleScore;
if (gameIsActive) {
int matchedSymbolIndex = -1;
for (int i = 0; i < activeWords.Count; i++) {
if (activeWords[i].ToLower() == answerField.text.ToLower()) {
matchedSymbolIndex = i;
}
}
DestroySymbolAt(matchedSymbolIndex);
answerField.text = "";
}
// Destroy the oldest symbol if the current input matches it
if (matchedSymbolIndex >= 0) {
int difference = Math.Abs((int) (activeSymbols[matchedSymbolIndex].transform.position.x - hitZone.transform.position.x));
if (difference < perfectBoundary) {
feedBack.text = "Perfect!";
perfectSigns++;
score += perfectScore;
} else if (difference < goodBoundary) {
feedBack.text = "Good!";
goodSigns++;
score += goodScore;
} else if (difference < mehBoundary) {
feedBack.text = "Meh...";
mehSigns++;
score += mehScore;
} else {
feedBack.text = "Terrible!";
terribleSigns++;
score += terribleScore;
}
// Destroy the oldest symbol if it leaves the screen
if (activeSymbols.Count > 0) {
if (activeSymbols[0].GetComponent<RectTransform>().localPosition.x > -trackX) {
DestroySymbolAt(0);
score += offscreenScore;
DestroySymbolAt(matchedSymbolIndex);
answerField.text = "";
}
}
// Spawn new symbol every spawn period
float currentTime = Time.time;
if (currentTime - lastSpawn > spawnPeriod) {
lastSpawn = currentTime;
SpawnNewSymbol();
}
// Destroy the oldest symbol if it leaves the screen
if (activeSymbols.Count > 0) {
if (activeSymbols[0].GetComponent<RectTransform>().localPosition.x > -trackX) {
DestroySymbolAt(0);
incorrectSigns++;
feedBack.text = "Te laat!";
score += offscreenScore;
}
}
// Move all active symbols to the right
foreach (GameObject symbol in activeSymbols) {
RectTransform rectTransform = symbol.GetComponent<RectTransform>();
rectTransform.localPosition = new Vector3(rectTransform.localPosition.x + Time.deltaTime * moveSpeed, trackY, 0);
}
// Spawn new symbol every spawn period
float currentTime = Time.time;
if (currentTime - lastSpawn > currentSong.spawnPeriod && lastSymbolTime > currentTime) {
lastSpawn = currentTime;
SpawnNewSymbol();
}
scoreDisplay.text = "Score: " + score.ToString();
// Check if the song has ended and activate scorescreen if it has
if (currentTime - beginTime > currentSong.duration) {
ActivateEnd();
}
// Move all active symbols to the right
foreach (GameObject symbol in activeSymbols) {
RectTransform rectTransform = symbol.GetComponent<RectTransform>();
rectTransform.localPosition = new Vector3(rectTransform.localPosition.x + Time.deltaTime * moveSpeed, trackY, 0);
}
scoreDisplay.text = "Score: " + score.ToString();
}
}
/// <summary>
/// Display Scoreboard + Metrics
/// </summary>
public void ActivateEnd()
{
gameIsActive = false;
while (activeSymbols.Count > 0)
{
DestroySymbolAt(0);
}
// TODO: Scoreboard
SaveScores();
SetScoreMetrics();
SetScoreBoard();
gameEndedPanel.SetActive(true);
}
/// <summary>
@@ -264,4 +440,169 @@ public class JustSignController : MonoBehaviour
activeWords.Add(nextSymbol);
activeSymbols.Add(newSymbolObject);
}
/// <summary>
/// Update and save the scores
/// </summary>
public void SaveScores()
{
// Calculate new score
int newScore = this.score;
// Save the score as a tuple: < int score, string time ago>
Score score = new Score();
score.scoreValue = newScore;
score.time = DateTime.Now.ToString();
// Save the new score
user = userList.GetCurrentUser();
Progress progress = user.GetMinigameProgress(minigame.index);
// Get the current list of scores
List<Score> latestScores = progress.Get<List<Score>>("latestScores");
List<Score> highestScores = progress.Get<List<Score>>("highestScores");
// Add the new score
latestScores.Add(score);
highestScores.Add(score);
// Sort the scores
highestScores.Sort((a, b) => b.scoreValue.CompareTo(a.scoreValue));
// Only save the top 10 scores, so this list doesn't keep growing endlessly
progress.AddOrUpdate<List<Score>>("latestScores", latestScores.Take(10).ToList());
progress.AddOrUpdate<List<Score>>("highestScores", highestScores.Take(10).ToList());
userList.Save();
}
/// <summary>
/// Set score metrics
/// </summary>
private void SetScoreMetrics()
{
// In de zone
perfectSignsText.text = perfectSigns.ToString();
// Aanvaardbaar
goodSignsText.text = goodSigns.ToString();
// Nipt
mehSignsText.text = mehSigns.ToString();
// Slechte timing
terribleSignsText.text = terribleSigns.ToString();
// Niet Geraden
notFoundSignsText.text = incorrectSigns.ToString();
// LPM
int duration = songList.songs[songList.currentSongIndex].duration;
int correctSigns = goodSigns + perfectSigns + mehSigns + terribleSigns;
lpmText.text = (60f * correctSigns / duration).ToString("#") + " GPM";
// Score
scoreText.text = "Score: " + score.ToString();
}
/// <summary>
/// Sets the scoreboard
/// </summary>
private void SetScoreBoard()
{
// Clean the previous scoreboard entries
for (int i = 0; i < scoreboardEntries.Count; i++)
{
Destroy(scoreboardEntries[i]);
}
scoreboardEntries.Clear();
// Instantiate new entries
// Get all scores from all users
List<Tuple<string, Score>> allScores = new List<Tuple<string, Score>>();
foreach (User user in userList.GetUsers())
{
// Get user's progress for this minigame
Progress progress = user.GetMinigameProgress(minigame.index);
if (progress != null)
{
// Add scores to dictionary
List<Score> scores = progress.Get<List<Score>>("highestScores");
foreach (Score score in scores)
{
allScores.Add(new Tuple<string, Score>(user.username, score));
}
}
}
// Sort allScores based on Score.scoreValue
allScores.Sort((a, b) => b.Item2.scoreValue.CompareTo(a.Item2.scoreValue));
// Instantiate scoreboard entries
int rank = 1;
foreach (Tuple<string, Score> tup in allScores.Take(10))
{
string username = tup.Item1;
Score score = tup.Item2;
GameObject entry = Instantiate(scoreboardEntry, scoreboardEntriesContainer);
scoreboardEntries.Add(entry);
// Set the player icon
entry.transform.Find("Image").GetComponent<Image>().sprite = userList.GetUserByUsername(username).avatar;
// Set the player name
entry.transform.Find("PlayerName").GetComponent<TMP_Text>().text = username;
// Set the score
entry.transform.Find("Score").GetComponent<TMP_Text>().text = score.scoreValue.ToString();
// Set the rank
entry.transform.Find("Rank").GetComponent<TMP_Text>().text = rank.ToString();
// Set the ago
// Convert the score.time to Datetime
DateTime time = DateTime.Parse(score.time);
DateTime currentTime = DateTime.Now;
TimeSpan diff = currentTime.Subtract(time);
string formatted;
if (diff.Days > 0)
{
formatted = $"{diff.Days}d ";
}
else if (diff.Hours > 0)
{
formatted = $"{diff.Hours}h ";
}
else if (diff.Minutes > 0)
{
formatted = $"{diff.Minutes}m ";
}
else
{
formatted = "now";
}
entry.transform.Find("Ago").GetComponent<TMP_Text>().text = formatted;
// Alternating colors looks nice
if (rank % 2 == 0)
{
Image image = entry.transform.GetComponent<Image>();
image.color = new Color(image.color.r, image.color.g, image.color.b, 0f);
}
// Make new score stand out
if (diff.TotalSeconds < 1)
{
Image image = entry.transform.GetComponent<Image>();
image.color = new Color(0, 229, 255, 233);
}
rank++;
}
}
}

View File

@@ -1,13 +1,18 @@
fileFormatVersion: 2
<<<<<<<< HEAD:Assets/JustSign/Scripts/JustSignController.cs.meta
guid: 9ede962218eda88668cd8032b921aada
========
guid: 40ff941e1b34847bdb160c6950f35aec
>>>>>>>> development:Assets/MediaPipeUnity/Common/Scripts/KeypointManager.cs.meta
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
defaultReferences:
- canvas: {instanceID: 0}
- answerField: {instanceID: 0}
- feedBack: {instanceID: 0}
- scoreDisplay: {instanceID: 0}
- minigame: {instanceID: 0}
- songList: {fileID: 11400000, guid: 4f0ce70309bb901feb28199a82a7d195, type: 2}
- hitZone: {instanceID: 0}
- symbolPrefab: {instanceID: 0}
- symbolContainer: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,29 @@
using UnityEngine;
using UnityEngine.Audio;
/// <summary>
/// Class for holding all (static) data about a certain song
/// </summary>
[CreateAssetMenu(menuName = "Create new Scriptable/Song")]
public class Song : ScriptableObject
{
/// <summary>
/// Time at which the first symbol should enter the hit zone
/// </summary>
public float firstSymbolTime;
/// <summary>
/// Determines every how many seconds a symbol should enter the hit zone
/// </summary>
public float spawnPeriod;
/// <summary>
/// Duration of the song in seconds
/// </summary>
public int duration;
/// <summary>
/// The actual audio source
/// </summary>
public AudioClip song;
}

View File

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

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Keep track of all songs
/// </summary>
[CreateAssetMenu(menuName = "Create new Scriptable/SongList")]
public class SongList : ScriptableObject
{
/// <summary>
/// Index of the active/to be loaded/current song
/// </summary>
public int currentSongIndex = 0;
/// <summary>
/// List of all installed songs
/// </summary>
public List<Song> songs = new List<Song>();
}

View File

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

View File

@@ -1,9 +1,8 @@
fileFormatVersion: 2
guid: 9f5880b033929b7478e7b3a9ed6c5ee7
guid: a92b9838d20352bb8b984c2e361c7fba
folderAsset: yes
timeCreated: 1455295494
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: e01ded5e221494bdbaab8674c0354f64
AudioImporter:
externalObjects: {}
serializedVersion: 6
defaultSettings:
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
preloadAudioData: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: d7ca1b558b7b064cdb6f9d6c951b1522
AudioImporter:
externalObjects: {}
serializedVersion: 6
defaultSettings:
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
preloadAudioData: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: ad948b3082b546f4e8f3565bdfe0abf6
timeCreated: 1455295598
licenseType: Store
guid: 73c615986873dc246893879daf74c05d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface Listener
{
public IEnumerator ProcessIncomingCall();
}

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,6 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 4318122119930585316}
- component: {fileID: 4318122119930585317}
m_Layer: 5
m_Name: Feedback
m_TagString: Untagged
@@ -39,22 +38,6 @@ RectTransform:
m_AnchoredPosition: {x: 0, y: 200}
m_SizeDelta: {x: 500, y: 150}
m_Pivot: {x: 0.5, y: 0}
--- !u!114 &4318122119930585317
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4318122119930585319}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 44e682a32ee15cc489bf50f3a06f717b, type: 3}
m_Name:
m_EditorClassIdentifier:
feedbackText: {fileID: 4318122120222767928}
feedbackProgress: {fileID: 4318122119968934242}
feedbackProgressImage: {fileID: 4318122120334233319}
signPredictor: {fileID: 0}
--- !u!1 &4318122119968934244
GameObject:
m_ObjectHideFlags: 0

View File

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

View File

@@ -0,0 +1,20 @@
%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: 78a3f61c93a08c04496c49ffd10b9021, type: 3}
m_Name: ModelList
m_EditorClassIdentifier:
currentModelIndex: 0
models:
- index: 0
model: {fileID: 5022602860645237092, guid: e6d85df707405ad4f97c23b07227ee99, type: 3}
- index: 1
model: {fileID: 5022602860645237092, guid: e6d85df707405ad4f97c23b07227ee99, type: 3}

View File

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

View File

@@ -0,0 +1,32 @@
using DigitalRuby.Tween;
using Mediapipe.Unity.Tutorial;
using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
/// <summary>
/// Class to display feedback during a course
/// </summary>
public abstract class AbstractFeedback : MonoBehaviour, Listener
{
/// <summary>
/// Reference to the sign predictor
/// </summary>
public SignPredictor signPredictor;
public IEnumerator ProcessIncomingCall()
{
yield return StartCoroutine(UpdateFeedback());
}
public void AddSelfAsListener()
{
signPredictor.listeners.Add(this);
}
protected abstract IEnumerator UpdateFeedback();
}

View File

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

View File

@@ -1,182 +0,0 @@
using DigitalRuby.Tween;
using Mediapipe.Unity.Tutorial;
using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
/// <summary>
/// Class to display feedback during a course
/// </summary>
public class Feedback : MonoBehaviour
{
/// <summary>
/// Reference to the feedback field
/// </summary>
public TMP_Text feedbackText;
/// <summary>
/// Reference to the progress bar
/// </summary>
public Slider feedbackProgress;
/// <summary>
/// Reference to the progress bar image, so we can add fancy colors
/// </summary>
public Image feedbackProgressImage;
/// <summary>
/// Reference to the sign predictor
/// </summary>
public SignPredictor signPredictor;
/// <summary>
/// Callback for getting the correct sign
/// </summary>
public Func<string> getSignCallback;
/// <summary>
/// Callback to initiate the next sign
/// </summary>
public UnityAction<string> predictSignCallback;
/// <summary>
/// Timer to keep track of how long a incorrect sign is performed
/// </summary>
private DateTime timer;
/// <summary>
/// Current predicted sign
/// </summary>
private string predictedSign = null;
/// <summary>
/// Previous incorrect sign, so we can keep track whether the user is wrong or the user is still changing signs
/// </summary>
private string previousIncorrectSign = null;
/// <summary>
/// Start is called before the first frame update
/// </summary>
void Start()
{
// Start the coroutine to update the scale every 200 milliseconds
StartCoroutine(UpdateFeedback());
}
/// <summary>
/// UpdateScale updates the progress bar every 200ms, updated the feedback text, and progress bar color
/// If a high enough accuracy is detected, it will go to the next sign
/// </summary>
/// <returns></returns>
IEnumerator UpdateFeedback()
{
while (true)
{
if (getSignCallback != null && predictSignCallback != null)
{
// Get current sign
string currentSign = getSignCallback();
// Get the predicted sign
if (signPredictor != null && signPredictor.learnableProbabilities != null &&
currentSign != null && signPredictor.learnableProbabilities.ContainsKey(currentSign))
{
float accuracy = signPredictor.learnableProbabilities[currentSign];
if (feedbackText != null && feedbackProgressImage != null){
if (accuracy > 0.98)
{
// TODO: fix emojis
feedbackText.text = "✨ Perfect ✨";
Color col = new Color(0xff / 255.0f, 0xcc / 255.0f, 0x00 / 255.0f);
feedbackText.color = col;
feedbackProgressImage.color = col;
}
else if (accuracy > 0.95)
{
feedbackText.text = "Super!";
Color col = new Color(0x00 / 255.0f, 0xff / 255.0f, 0xcc / 255.0f);
feedbackText.color = col;
feedbackProgressImage.color = col;
}
else if (accuracy > 0.90)
{
feedbackText.text = "Goed";
feedbackText.color = Color.green;
feedbackProgressImage.color = Color.green;
}
else if (accuracy > 0.80)
{
feedbackText.text = "Bijna...";
Color col = new Color(0xff / 255.0f, 0x66 / 255.0f, 0x00 / 255.0f);
feedbackText.color = col;
feedbackProgressImage.color = col;
}
else
{
feedbackText.text = "Detecteren...";
feedbackText.color = Color.red;
feedbackProgressImage.color = Color.red;
}
float oldValue = feedbackProgress.value;
// use an exponential scale
float newValue = Mathf.Exp(4 * (accuracy - 1.0f));
feedbackProgress.gameObject.Tween("FeedbackUpdate", oldValue, newValue, 0.2f, TweenScaleFunctions.CubicEaseInOut, (t) =>
{
if (feedbackProgress != null)
{
feedbackProgress.value = t.CurrentValue;
}
});
}
// Check whether (in)correct sign has high accuracy
foreach (var kv in signPredictor.learnableProbabilities)
{
if (kv.Value > 0.90)
{
predictedSign = kv.Key;
// Correct sign
if (predictedSign == currentSign)
{
yield return new WaitForSeconds(1.0f);
predictSignCallback(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
// Incorrect sign
else
{
if (previousIncorrectSign != predictedSign)
{
timer = DateTime.Now;
previousIncorrectSign = predictedSign;
}
else if (DateTime.Now - timer > TimeSpan.FromSeconds(2.0f))
{
predictSignCallback(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
}
break;
}
}
}
else if(feedbackProgress != null)
{
feedbackProgress.value = 0.0f;
}
}
// Wait for 200 milliseconds before updating the scale again
yield return new WaitForSeconds(0.2f);
}
}
}

View File

@@ -6,7 +6,9 @@
"GUID:5c2b5ba89f9e74e418232e154bc5cc7a",
"GUID:04c4d86a70aa56c55a78c61f1ab1a56d",
"GUID:edc93f477bb73a743a97d6882ed330b3",
"GUID:58e104b97fb3752438ada2902a36dcbf"
"GUID:58e104b97fb3752438ada2902a36dcbf",
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25",
"GUID:f55a02e98b01bc849b30d9650ccd8f15"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -19,9 +19,9 @@ namespace Mediapipe.Unity.Tutorial
public class SignPredictor : MonoBehaviour
{
/// <summary>
/// Reference to the model used in the SignPredictor
/// ModelList, used to change model using ModelIndex
/// </summary>
public NNModel model;
public ModelList modelList;
/// <summary>
/// Reference to the model info file
@@ -141,6 +141,8 @@ namespace Mediapipe.Unity.Tutorial
/// </summary>
private Tensor inputTensor;
public List<Listener> listeners = new List<Listener>();
/// <summary>
/// Google Mediapipe setup & run
/// </summary>
@@ -207,14 +209,21 @@ namespace Mediapipe.Unity.Tutorial
keypointManager = new KeypointManager(modelInfoFile);
// check if model exists at path
//var model = ModelLoader.Load(Resources.Load<NNModel>("Models/Fingerspelling/model_A-L"));
worker = model.CreateWorker();
worker = modelList.GetCurrentModel().CreateWorker();
StartCoroutine(SignRecognitionCoroutine());
StartCoroutine(MediapipeCoroutine());
}
/// <summary>
/// Called at the start of course/Minigame, will set the model before the start of SIgnPredictor is called.
/// </summary>
/// <param name="index">The index of the model to be used</param>
public void SetModel(ModelIndex index)
{
this.modelList.SetCurrentModel(index);
}
/// <summary>
/// Coroutine which executes the mediapipe pipeline
@@ -315,6 +324,10 @@ namespace Mediapipe.Unity.Tutorial
learnableProbabilities.Add(((char)(i + 65)).ToString(), softmaxedOutput2[i]);
}
//Debug.Log($"prob = [{learnableProbabilities.Aggregate(" ", (t, kv) => $"{t}{kv.Key}:{kv.Value} ")}]");
foreach(Listener listener in listeners)
{
yield return listener.ProcessIncomingCall();
}
}
else
{

View File

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

View File

@@ -0,0 +1,78 @@
using System.Collections;
using System.Linq;
using System.IO;
using NUnit.Framework;
using TMPro;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.SceneManagement;
public class GameControllerTests
{
[UnitySetUp]
public IEnumerator SetupFunction()
{
string path = $"{Application.persistentDataPath}/unit_test_users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))
{
writer.Write(oneUser);
}
SystemController.GetInstance().LoadNextScene("SpellingBee/Scenes/Game");
yield return new WaitForSeconds(0.2f);
}
[UnityTest]
public IEnumerator CheckScoreTest()
{
GameController gameController = (GameController)GameObject.FindObjectOfType(typeof(GameController));
yield return new WaitForSeconds(0.2f);
Assert.AreEqual(0, gameController.CalculateScore());
gameController.NextWord();
Assert.AreEqual(5, gameController.CalculateScore());
gameController.NextLetter(true);
Assert.AreEqual(6, gameController.CalculateScore());
}
[UnityTest]
public IEnumerator ActivateGameOverTest()
{
GameController gameController = (GameController)GameObject.FindObjectOfType(typeof(GameController));
gameController.ActivateGameOver();
yield return new WaitForSeconds(0.2f);
GameEndedPanel gameEndedPanel = (GameEndedPanel)GameObject.FindObjectOfType(typeof(GameEndedPanel));
Assert.NotNull(gameEndedPanel);
Assert.AreEqual("VERLOREN", gameEndedPanel.endText.text);
}
[UnityTest]
public IEnumerator ActivateWinTests()
{
GameController gameController = (GameController)GameObject.FindObjectOfType(typeof(GameController));
gameController.ActivateWin();
yield return new WaitForSeconds(0.2f);
GameEndedPanel gameEndedPanel = (GameEndedPanel)GameObject.FindObjectOfType(typeof(GameEndedPanel));
Assert.NotNull(gameEndedPanel);
Assert.AreEqual("GEWONNEN", gameEndedPanel.endText.text);
}
[UnityTest]
public IEnumerator CheckGameOverTest()
{
GameController gameController = (GameController)GameObject.FindObjectOfType(typeof(GameController));
gameController.AddSeconds(-60);
yield return new WaitForSeconds(0.1f);
GameEndedPanel gameEndedPanel = (GameEndedPanel)GameObject.FindObjectOfType(typeof(GameEndedPanel));
Assert.NotNull(gameEndedPanel);
Assert.AreEqual("VERLOREN", gameEndedPanel.endText.text);
}
}

View File

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

View File

@@ -0,0 +1,52 @@
using System.Collections;
using System.Linq;
using System.IO;
using NUnit.Framework;
using TMPro;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.SceneManagement;
public class GameEndedPanelTests
{
[UnitySetUp]
public IEnumerator SetupFunction()
{
string path = $"{Application.persistentDataPath}/unit_test_users.json";
var oneUser = "{\"currentUserIndex\": 0,\"storedUsers\": [{\"username\": \"TEST\",\"avatar\": {\"instanceID\": 40848},\"playtime\": 0.0,\"courses\": [],\"minigames\": []}]}";
using (StreamWriter writer = new StreamWriter(path))
{
writer.Write(oneUser);
}
SystemController.GetInstance().LoadNextScene("SpellingBee/Scenes/Game");
yield return new WaitForSeconds(0.2f);
}
[UnityTest]
public IEnumerator ScoreTest()
{
GameController gameController = (GameController)GameObject.FindObjectOfType(typeof(GameController));
gameController.NextWord();
gameController.NextLetter(false);
gameController.NextLetter(true);
gameController.NextLetter(false);
yield return new WaitForSeconds(1f);
gameController.ActivateWin();
GameEndedPanel gameEndedPanel = (GameEndedPanel)GameObject.FindObjectOfType(typeof(GameEndedPanel));
Assert.NotNull(gameEndedPanel);
Assert.AreEqual("Score: 6", gameEndedPanel.scoreText.text);
Assert.AreEqual("1", gameEndedPanel.lettersRightText.text);
Assert.AreEqual("2", gameEndedPanel.lettersWrongText.text);
Assert.AreEqual("3", gameEndedPanel.lettersTotalText.text);
Assert.AreEqual("00:01", gameEndedPanel.timeText.text);
}
}

View File

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

View File

@@ -0,0 +1,26 @@
{
"name": "SpellingBeePlayModeTests",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"InterfacesScripts",
"Unity.TextMeshPro",
"SpellingBeeScripts",
"AccountsScripts",
"SignPredictor"
],
"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: 8024acb9574451c40ba558529a3ef51c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1720,6 +1720,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4318122119930585317, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
propertyPath: m_Enabled
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4318122119930585317, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
propertyPath: signPredictor
value:
@@ -1739,15 +1743,37 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 4318122119930585316, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 967164043}
m_PrefabAsset: {fileID: 0}
--- !u!114 &967164045 stripped
--- !u!114 &967164046 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4318122119930585317, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_CorrespondingSourceObject: {fileID: 4318122120334233319, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 967164043}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 44e682a32ee15cc489bf50f3a06f717b, type: 3}
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &967164047 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4318122119968934242, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 967164043}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 67db9e8f0e2ae9c40bc1e2b64352a6b4, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &967164048 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 4318122120222767928, guid: 7c71c65ecb5fe0449a8b0d178987f016, type: 3}
m_PrefabInstance: {fileID: 967164043}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &978093274
@@ -3144,7 +3170,7 @@ MonoBehaviour:
m_Calls:
- m_Target: {fileID: 1768150807}
m_TargetAssemblyTypeName: GameController, SpellingBeeScripts
m_MethodName: Start
m_MethodName: StartController
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
@@ -3900,10 +3926,11 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 043ccd99cf82b3cc9bf2e00956ce2b93, type: 3}
m_Name:
m_EditorClassIdentifier:
model: {fileID: 5022602860645237092, guid: e6d85df707405ad4f97c23b07227ee99, type: 3}
modelList: {fileID: 11400000, guid: 39516e4e6e56f0f4f80647d9c4d8034c, type: 2}
modelInfoFile: {fileID: 4900000, guid: fb8b51022bdcd654a9f29c054832a1b5, type: 3}
configAsset: {fileID: 4900000, guid: 6288c43cdca97374782dac1ea87aa029, type: 3}
screen: {fileID: 1743003084}
screen2: {fileID: 0}
--- !u!4 &1592592445
Transform:
m_ObjectHideFlags: 0
@@ -4551,16 +4578,20 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 44fbed5ae228de39b9f727def7578d06, type: 3}
m_Name:
m_EditorClassIdentifier:
themeList: {fileID: 0}
feedbackText: {fileID: 967164048}
feedbackProgress: {fileID: 967164047}
feedbackProgressImage: {fileID: 967164046}
signPredictor: {fileID: 1592592444}
themeList: {fileID: 11400000, guid: a247e2ce790f0f746a3bc521e6ab7d58, type: 2}
userList: {fileID: 11400000, guid: 072bec636a40f7e4e93b0ac624a3bda2, type: 2}
minigame: {fileID: 11400000, guid: 8a087d241d652634eb4f6352267ea7dc, type: 2}
minigamelist: {fileID: 11400000, guid: 51453f9b41bc72f468ba3e67ab622f8f, type: 2}
letterPrefab: {fileID: 4639383499500021565, guid: c3e66e8957864914cb022af914df6a28, type: 3}
letterContainer: {fileID: 1346005056}
wordImage: {fileID: 1338727891}
timerText: {fileID: 1843239267}
bonusTimeText: {fileID: 1812475780}
Scoreboard: {fileID: 862382568}
feedback: {fileID: 967164045}
gameEndedPanel: {fileID: 757133117}
--- !u!1 &1812475780
GameObject:

View File

@@ -5,8 +5,9 @@ using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using DigitalRuby.Tween;
public partial class GameController : MonoBehaviour
public partial class GameController : AbstractFeedback
{
/// <summary>
/// All of the words that can be used in this session
@@ -90,6 +91,12 @@ public partial class GameController : MonoBehaviour
/// </summary>
public Minigame minigame;
/// <summary>
/// We keep the minigamelist as well so that the minigame-index doesn't get reset
/// DO NOT REMOVE
/// </summary>
public MinigameList minigamelist;
/// <summary>
/// Letter prefab
/// </summary>
@@ -130,20 +137,53 @@ public partial class GameController : MonoBehaviour
/// </summary>
public Transform Scoreboard;
/// <summary>
/// Accuracy feeback object
/// </summary>
public Feedback feedback;
/// <summary>
/// Reference to the gameEnded panel, so we can update its display
/// </summary>
public GameObject gameEndedPanel;
/// <summary>
/// Reference to the feedback field
/// </summary>
public TMP_Text feedbackText;
/// <summary>
/// Reference to the progress bar
/// </summary>
public Slider feedbackProgress;
/// <summary>
/// Reference to the progress bar image, so we can add fancy colors
/// </summary>
public Image feedbackProgressImage;
/// <summary>
/// Timer to keep track of how long a incorrect sign is performed
/// </summary>
protected DateTime timer;
/// <summary>
/// Current predicted sign
/// </summary>
protected string predictedSign = null;
/// <summary>
/// Previous incorrect sign, so we can keep track whether the user is wrong or the user is still changing signs
/// </summary>
protected string previousIncorrectSign = null;
/// <summary>
/// Start is called before the first frame update
/// </summary>
public void Start()
{
StartController();
signPredictor.SetModel(currentTheme.modelIndex);
AddSelfAsListener();
}
public void StartController()
{
correctLetters = 0;
incorrectLetters = 0;
@@ -162,6 +202,7 @@ public partial class GameController : MonoBehaviour
bonusTimeText.SetActive(false);
// Create entry in current user for keeping track of progress
userList.Load();
user = userList.GetCurrentUser();
Progress progress = user.GetMinigameProgress(minigame.index);
if (progress == null)
@@ -175,29 +216,10 @@ public partial class GameController : MonoBehaviour
userList.Save();
currentTheme = minigame.themeList.themes[minigame.themeList.currentThemeIndex];
feedback.signPredictor.model = currentTheme.model;
//feedback.signPredictor.ChangeModel(currentTheme.modelIndex);
words.AddRange(currentTheme.learnables);
ShuffleWords();
NextWord();
// Set calllbacks
feedback.getSignCallback = () =>
{
if (letterIndex < currentWord.Length)
{
return currentWord[letterIndex].ToString().ToUpper();
}
return null;
};
feedback.predictSignCallback = (sign) =>
{
bool successful = sign.ToUpper() == currentWord[letterIndex].ToString().ToUpper();
if (successful)
{
AddSeconds(secondsPerLetter);
}
NextLetter(successful);
};
}
/// <summary>
@@ -227,14 +249,14 @@ public partial class GameController : MonoBehaviour
int seconds = Mathf.FloorToInt(timerValue % 60.0f);
timerText.text = string.Format("{0:00}:{1:00}", minutes, seconds);
}
}
/// <summary>
/// Randomly shuffle the list of words
/// </summary>
private void ShuffleWords()
public void ShuffleWords()
{
for (int i = words.Count - 1; i > 0; i--)
{
@@ -250,7 +272,7 @@ public partial class GameController : MonoBehaviour
/// Calculate the score
/// </summary>
/// <returns>The calculated score</returns>
private int CalculateScore()
public int CalculateScore()
{
return spelledWords * 5 + correctLetters;
}
@@ -258,7 +280,7 @@ public partial class GameController : MonoBehaviour
/// <summary>
/// Displays the game over panel and score values
/// </summary>
private void ActivateGameOver()
public void ActivateGameOver()
{
gameEnded = true;
DeleteWord();
@@ -280,7 +302,7 @@ public partial class GameController : MonoBehaviour
/// <summary>
/// Display win screen
/// </summary>
private void ActivateWin()
public void ActivateWin()
{
gameEnded = true;
DeleteWord();
@@ -302,7 +324,7 @@ public partial class GameController : MonoBehaviour
/// <summary>
/// Update and save the scores
/// </summary>
private void SaveScores()
public void SaveScores()
{
// Calculate new score
int newScore = CalculateScore();
@@ -337,7 +359,7 @@ public partial class GameController : MonoBehaviour
/// <summary>
/// Delete all letter objects
/// </summary>
private void DeleteWord()
public void DeleteWord()
{
for (int i = 0; i < letters.Count; i++)
{
@@ -350,7 +372,7 @@ public partial class GameController : MonoBehaviour
/// Adds seconds to timer
/// </summary>
/// <param name="seconds"></param>
private void AddSeconds(int seconds)
public void AddSeconds(int seconds)
{
timerValue += (float)seconds;
bonusTimeText.SetActive(true);
@@ -361,8 +383,10 @@ public partial class GameController : MonoBehaviour
/// Display the next letter
/// </summary>
/// <param name="successful">true if the letter was correctly signed, false otherwise</param>
private void NextLetter(bool successful)
public void NextLetter(bool successful)
{
if (gameEnded) { return; }
// Change color of current letter (skip spaces)
if (successful)
{
@@ -395,7 +419,7 @@ public partial class GameController : MonoBehaviour
/// <summary>
/// Display next word in the series
/// </summary>
private void NextWord()
public void NextWord()
{
DeleteWord();
spelledWords++;
@@ -418,7 +442,7 @@ public partial class GameController : MonoBehaviour
/// Displays the word that needs to be spelled
/// </summary>
/// <param name="word">The word to display</param>
private void DisplayWord(string word)
public void DisplayWord(string word)
{
for (int i = 0; i < word.Length; i++)
{
@@ -444,4 +468,106 @@ public partial class GameController : MonoBehaviour
{
yield return new WaitForSecondsRealtime(2);
}
protected override IEnumerator UpdateFeedback()
{
// Get current sign
string currentSign = GetSign();
// Get the predicted sign
if (signPredictor != null && signPredictor.learnableProbabilities != null &&
currentSign != null && signPredictor.learnableProbabilities.ContainsKey(currentSign))
{
float accuracy = signPredictor.learnableProbabilities[currentSign];
if (feedbackText != null && feedbackProgressImage != null)
{
if (accuracy > 0.90)
{
feedbackText.text = "Goed";
feedbackText.color = Color.green;
feedbackProgressImage.color = Color.green;
}
else if (accuracy > 0.80)
{
feedbackText.text = "Bijna...";
Color col = new Color(0xff / 255.0f, 0x66 / 255.0f, 0x00 / 255.0f);
feedbackText.color = col;
feedbackProgressImage.color = col;
}
else
{
feedbackText.text = "Detecteren...";
feedbackText.color = Color.red;
feedbackProgressImage.color = Color.red;
}
float oldValue = feedbackProgress.value;
// use an exponential scale
float newValue = Mathf.Exp(4 * (accuracy - 1.0f));
feedbackProgress.gameObject.Tween("FeedbackUpdate", oldValue, newValue, 0.2f, TweenScaleFunctions.CubicEaseInOut, (t) =>
{
if (feedbackProgress != null)
{
feedbackProgress.value = t.CurrentValue;
}
});
}
// Check whether (in)correct sign has high accuracy
foreach (var kv in signPredictor.learnableProbabilities)
{
if (kv.Value > 0.90)
{
predictedSign = kv.Key;
// Correct sign
if (predictedSign == currentSign)
{
yield return new WaitForSeconds(1.0f);
predictSign(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
// Incorrect sign
else
{
if (previousIncorrectSign != predictedSign)
{
timer = DateTime.Now;
previousIncorrectSign = predictedSign;
}
else if (DateTime.Now - timer > TimeSpan.FromSeconds(2.0f))
{
predictSign(predictedSign);
timer = DateTime.Now;
predictedSign = null;
previousIncorrectSign = null;
}
}
break;
}
}
}
else if (feedbackProgress != null)
{
feedbackProgress.value = 0.0f;
}
yield return null;
}
public string GetSign(){
if (letterIndex<currentWord.Length){
return currentWord[letterIndex].ToString().ToUpper();
}
return null;
}
public void predictSign(string sign) {
bool successful = sign.ToUpper() == currentWord[letterIndex].ToString().ToUpper();
if (successful)
{
AddSeconds(secondsPerLetter);
}
NextLetter(successful);
}
}

View File

@@ -7,7 +7,8 @@
"GUID:3444c67d5a3a93e5a95a48906078c372",
"GUID:d0b6b39a21908f94fbbd9f2c196a9725",
"GUID:5c2b5ba89f9e74e418232e154bc5cc7a",
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25"
"GUID:7f2d0ee6dd21e1d4eb25b71b7a749d25",
"GUID:58e104b97fb3752438ada2902a36dcbf"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -1,113 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2016 Digital Ruby, LLC
http://www.digitalruby.com
Created by Jeff Johnson
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using UnityEngine;
using System.Collections;
// for your own scripts make sure to add the following line:
using DigitalRuby.Tween;
using UnityEngine.SceneManagement;
namespace DigitalRuby.Tween
{
public class TweenDemo : MonoBehaviour
{
public GameObject Circle;
public Light Light;
private SpriteRenderer spriteRenderer;
private void TweenMove()
{
System.Action<ITween<Vector3>> updateCirclePos = (t) =>
{
Circle.gameObject.transform.position = t.CurrentValue;
};
System.Action<ITween<Vector3>> circleMoveCompleted = (t) =>
{
Debug.Log("Circle move completed");
};
Vector3 currentPos = Circle.transform.position;
Vector3 startPos = Camera.main.ViewportToWorldPoint(Vector3.zero);
Vector3 midPos = Camera.main.ViewportToWorldPoint(Vector3.one);
Vector3 endPos = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.5f));
currentPos.z = startPos.z = midPos.z = endPos.z = 0.0f;
// completion defaults to null if not passed in
Circle.gameObject.Tween("MoveCircle", currentPos, startPos, 1.75f, TweenScaleFunctions.CubicEaseIn, updateCirclePos)
.ContinueWith(new Vector3Tween().Setup(startPos, midPos, 1.75f, TweenScaleFunctions.Linear, updateCirclePos))
.ContinueWith(new Vector3Tween().Setup(midPos, endPos, 1.75f, TweenScaleFunctions.CubicEaseOut, updateCirclePos, circleMoveCompleted));
}
private void TweenColor()
{
System.Action<ITween<Color>> updateColor = (t) =>
{
spriteRenderer.color = t.CurrentValue;
};
Color endColor = UnityEngine.Random.ColorHSV(0.0f, 1.0f, 0.0f, 1.0f, 0.5f, 1.0f, 1.0f, 1.0f);
// completion defaults to null if not passed in
Circle.gameObject.Tween("ColorCircle", spriteRenderer.color, endColor, 1.0f, TweenScaleFunctions.QuadraticEaseOut, updateColor);
}
private void TweenRotate()
{
System.Action<ITween<float>> circleRotate = (t) =>
{
// start rotation from identity to ensure no stuttering
Circle.transform.rotation = Quaternion.identity;
Circle.transform.Rotate(Camera.main.transform.forward, t.CurrentValue);
};
float startAngle = Circle.transform.rotation.eulerAngles.z;
float endAngle = startAngle + 720.0f;
// completion defaults to null if not passed in
Circle.gameObject.Tween("RotateCircle", startAngle, endAngle, 2.0f, TweenScaleFunctions.CubicEaseInOut, circleRotate);
}
private void TweenReset()
{
SceneManager.LoadScene(0, LoadSceneMode.Single);
}
private void Start()
{
// for demo purposes, clear all tweens when new level loads, default is false
TweenFactory.ClearTweensOnLevelLoad = true;
spriteRenderer = Circle.GetComponent<SpriteRenderer>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Alpha1))
{
TweenMove();
}
if (Input.GetKeyDown(KeyCode.Alpha2))
{
TweenColor();
}
if (Input.GetKeyDown(KeyCode.Alpha3))
{
TweenRotate();
}
if (Input.GetKeyDown(KeyCode.R))
{
TweenReset();
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -1,135 +0,0 @@
fileFormatVersion: 2
guid: df831354d51eda74491b6ef6cfbbc4d0
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: -1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 16
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 32
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 1
cookieLightType: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

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