830 lines
25 KiB
C#
830 lines
25 KiB
C#
using DigitalRuby.Tween;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using Random = UnityEngine.Random;
|
|
|
|
public class HangmanController : AbstractFeedback
|
|
{
|
|
/// <summary>
|
|
/// The scriptable with all the themes, will be used to select a random word for hangman.
|
|
/// The spellingthemeList will be used for the words.
|
|
/// </summary>
|
|
public ThemeList themeList;
|
|
|
|
/// <summary>
|
|
/// reference to the fingerspelling-theme to reach the letter-thresholds
|
|
/// </summary>
|
|
public Theme fingerSpelling;
|
|
|
|
/// <summary>
|
|
/// The word that is currently being spelled
|
|
/// </summary>
|
|
private string currentWord;
|
|
|
|
/// <summary>
|
|
/// This integer holds the total amount of wrong guesses the player has made
|
|
/// </summary>
|
|
private int wrongs;
|
|
|
|
/// <summary>
|
|
/// This integer holds the amount of correct letters of the word that the user has guessed
|
|
/// </summary>
|
|
private int corrects;
|
|
|
|
/// <summary>
|
|
/// Letter prefab
|
|
/// </summary>
|
|
public GameObject letterPrefab;
|
|
|
|
/// <summary>
|
|
/// Reference to letter prefab
|
|
/// </summary>
|
|
public Transform letterContainer;
|
|
|
|
/// <summary>
|
|
/// The Image component for displaying the appropriate sprite
|
|
/// </summary>
|
|
public Image hangmanImage;
|
|
|
|
/// <summary>
|
|
/// The GameObjects representing the letters
|
|
/// </summary>
|
|
private List<GameObject> letters = new List<GameObject>();
|
|
|
|
/// <summary>
|
|
/// This scriptable holds all the images for the different stages of hangman
|
|
/// </summary>
|
|
public List<Sprite> images = new List<Sprite>();
|
|
|
|
/// <summary>
|
|
/// This initially empty list holds all the previous guesses that the user has made, as to not allow repeated msitakes
|
|
/// </summary>
|
|
private List<string> guesses = new List<string>();
|
|
|
|
/// <summary>
|
|
/// Holds a string of all the used letters, used to display them to the player
|
|
/// </summary>
|
|
public TMP_Text usedLettersText;
|
|
|
|
/// <summary>
|
|
/// The panel holding the actual game
|
|
/// </summary>
|
|
public GameObject gamePanel;
|
|
|
|
/// <summary>
|
|
/// The panel holding the player-selection screen
|
|
/// </summary>
|
|
public GameObject playerPanel;
|
|
|
|
/// <summary>
|
|
/// The panel holding the screen where player 1 can input their word
|
|
/// </summary>
|
|
public GameObject inputPanel;
|
|
|
|
/// <summary>
|
|
/// Reference to display the score
|
|
/// </summary>
|
|
public TMP_Text scoreDisplay;
|
|
|
|
/// <summary>
|
|
/// Reference to display the points lost/won
|
|
/// </summary>
|
|
public TMP_Text scoreBonus;
|
|
|
|
///// <summary>
|
|
///// This panel holds the panels for input and playing the game, sharing webcam and feedback
|
|
///// </summary>
|
|
//public GameObject inputGamePanel;
|
|
|
|
/// <summary>
|
|
/// This int shows what mode we are in, used in update: <br></br>
|
|
/// 0 : single or multiplayer?<br></br>
|
|
/// 1 : multiplayer word input<br></br>
|
|
/// 2 : signing letter<br></br>
|
|
/// 3 : confirming choice gameplay
|
|
/// 4 : confirming choice multiplayer input
|
|
/// </summary>
|
|
private int mode;
|
|
|
|
/// <summary>
|
|
/// The game over panel
|
|
/// </summary>
|
|
public GameObject gameEndedPanel;
|
|
|
|
/// <summary>
|
|
/// Reference to the minigame ScriptableObject
|
|
/// </summary>
|
|
public Minigame minigame;
|
|
|
|
/// <summary>
|
|
/// We keep the minigamelist as well so that the minigame-index doesn't get reset
|
|
/// DO NOT REMOVE
|
|
/// </summary>
|
|
public MinigameList minigamelist;
|
|
|
|
/// <summary>
|
|
/// Reference to the current user
|
|
/// </summary>
|
|
private User user;
|
|
|
|
/// <summary>
|
|
/// The button to go into the game
|
|
/// </summary>
|
|
public GameObject gotoGameButton;
|
|
|
|
/// <summary>
|
|
/// This textfield holds the word that player 1 is typing
|
|
/// </summary>
|
|
public TMP_Text inputTextField;
|
|
|
|
/// <summary>
|
|
/// Current sign out of the predictor
|
|
/// </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>
|
|
/// reference to the webcam background
|
|
/// </summary>
|
|
public RawImage webcamScreen;
|
|
|
|
/// <summary>
|
|
/// Previous incorrect sign, so we can keep track whether the user is wrong or the user is still changing signs
|
|
/// </summary>
|
|
protected string previousSign = null;
|
|
|
|
/// <summary>
|
|
/// variable to remember whether or not the timer needs to be updates.
|
|
/// Only update the timer if the model is confident for a certain letter
|
|
/// </summary>
|
|
private bool runTime = false;
|
|
|
|
/// <summary>
|
|
/// Holds the time which the user needs to hold the sign for, for it to be accepted.
|
|
/// </summary>
|
|
private float maxTime = 0.3f;
|
|
|
|
/// <summary>
|
|
/// Holds the current amount of time the user has held their sign for
|
|
/// </summary>
|
|
private float currentTime = 0f;
|
|
|
|
/// <summary>
|
|
/// Holds a reference to the TimerCircle to update its fill
|
|
/// </summary>
|
|
public Image timerCircle;
|
|
|
|
/// <summary>
|
|
/// Hold a reference to the confirmPanel to toggle its activity
|
|
/// </summary>
|
|
public GameObject confirmPanel;
|
|
|
|
/// <summary>
|
|
/// Hold a reference to the confirmPanel to toggle its activity
|
|
/// </summary>
|
|
public TMP_Text confirmText;
|
|
|
|
///// <summary>
|
|
///// Temporary reference to timer to turn it off
|
|
///// </summary>
|
|
//public GameObject timer;
|
|
|
|
/// <summary>
|
|
/// Maximum length of the words
|
|
/// </summary>
|
|
public const int MIN_INC_WORD_LENGHT = 3;
|
|
|
|
/// <summary>
|
|
/// Maximum length of the words
|
|
/// </summary>
|
|
public const int MAX_EXC_WORD_LENGHT = 17;
|
|
|
|
/// <summary>
|
|
/// Number of fails before the game ends
|
|
/// </summary>
|
|
public const int NUMBER_OF_FAILS_BEFORE_GAMEOVER = 7;
|
|
|
|
/// <summary>
|
|
/// Score obtained when guessing a correct letter
|
|
/// </summary>
|
|
private int correctLetterScore = 10;
|
|
|
|
/// <summary>
|
|
/// Score obtained when guessing an incorrect letter
|
|
/// </summary>
|
|
private int incorrectLetterScore = -5;
|
|
|
|
/// <summary>
|
|
/// Score obtained when guessing the entire word
|
|
/// </summary>
|
|
private int winScore = 25;
|
|
|
|
/// <summary>
|
|
/// Start is called before the first frame update
|
|
/// </summary>
|
|
void Start()
|
|
{
|
|
signPredictor.SwapScreen(webcamScreen);
|
|
signPredictor.SetModel(ModelIndex.FINGERSPELLING);
|
|
AddSelfAsListener();
|
|
|
|
StartController();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called at the start of the scene AND when the scene is replayed
|
|
/// </summary>
|
|
public void StartController()
|
|
{
|
|
// Make sure the mode starts at zero
|
|
mode = 0;
|
|
|
|
|
|
// Make sure that only the player-selection panel is the one shown
|
|
gamePanel.SetActive(false);
|
|
inputPanel.SetActive(false);
|
|
playerPanel.SetActive(true);
|
|
|
|
// Make sure that unneeded panels are inactive
|
|
gameEndedPanel.SetActive(false);
|
|
confirmPanel.SetActive(false);
|
|
|
|
// Create entry in current user for keeping track of progress
|
|
user = UserList.GetCurrentUser();
|
|
var progress = user.GetMinigameProgress(minigame.index);
|
|
if (progress == null)
|
|
{
|
|
progress = new PersistentDataController.SavedMinigameProgress();
|
|
progress.minigameIndex = minigame.index;
|
|
user.AddMinigameProgress(progress);
|
|
}
|
|
UserList.Save();
|
|
|
|
// Guesses needs to be created instantly because it is used in the FeedbackLoop
|
|
//guesses = new List<string>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hangman starts by asking the amount of players, so this function holds all the info needed to start the actual game
|
|
/// </summary>
|
|
public void StartGame()
|
|
{
|
|
// Change the mode
|
|
mode = 2;
|
|
|
|
// Activate the right panel
|
|
gamePanel.SetActive(true);
|
|
//inputGamePanel.SetActive(true);
|
|
inputPanel.SetActive(false);
|
|
playerPanel.SetActive(false);
|
|
|
|
PanelHangmanGame script = gamePanel.GetComponent<PanelHangmanGame>();
|
|
inputTextField = script.guessesTextField;
|
|
feedbackText = script.feedbackText;
|
|
feedbackProgress = script.feedbackProgressBar;
|
|
feedbackProgressImage = script.feedbackProgressImage;
|
|
webcamScreen = script.webcamScreen;
|
|
timerCircle = script.timerCircle;
|
|
confirmPanel = script.confirmPanel;
|
|
confirmText = script.confirmText;
|
|
|
|
confirmPanel.SetActive(false);
|
|
signPredictor.SwapScreen(webcamScreen);
|
|
|
|
// Reset values of parameters, so that the replaybutton works properly
|
|
corrects = 0;
|
|
wrongs = 0;
|
|
guesses.Clear();
|
|
usedLettersText.text = "";
|
|
|
|
// Delete first, to make sure that the letters are empty
|
|
DeleteWord();
|
|
DisplayWord(currentWord);
|
|
ChangeSprite();
|
|
|
|
scoreDisplay.text = $"Score: {CalculateScore()}";
|
|
scoreBonus.text = "";
|
|
|
|
// Temporary
|
|
//timer.SetActive(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function is called when the "two player"-button is clicked, it goed to the input-screen
|
|
/// </summary>
|
|
public void GoToInput()
|
|
{
|
|
// Change the mode
|
|
mode = 1;
|
|
|
|
// Initialise the word to an empty String
|
|
currentWord = "";
|
|
inputTextField.text = currentWord.ToUpper();
|
|
|
|
// Activate the right panel
|
|
gamePanel.SetActive(false);
|
|
//inputGamePanel.SetActive(true);
|
|
inputPanel.SetActive(true);
|
|
playerPanel.SetActive(false);
|
|
|
|
PanelMultiplayerInput script = inputPanel.GetComponent<PanelMultiplayerInput>();
|
|
|
|
gotoGameButton = script.gotoGameButton;
|
|
inputTextField = script.inputTextField;
|
|
feedbackText = script.feedbackText;
|
|
feedbackProgress = script.feedbackProgressBar;
|
|
feedbackProgressImage = script.feedbackProgressImage;
|
|
webcamScreen = script.webcamScreen;
|
|
timerCircle = script.timerCircle;
|
|
confirmPanel = script.confirmPanel;
|
|
confirmText = script.confirmText;
|
|
|
|
confirmPanel.SetActive(false);
|
|
signPredictor.SwapScreen(script.webcamScreen);
|
|
|
|
//temporarily turn off timer in input-mode
|
|
//timer.SetActive(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function is called if singleplayer is selected, we generate a random word for the player and start the game.
|
|
/// </summary>
|
|
public void SinglePlayer()
|
|
{
|
|
// This word is used for testing before dynamic word-fetching is added
|
|
PickRandomWord();
|
|
StartGame();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Randomly select a word from a randomly selected theme, use this word for the hangman game for singleplayer.
|
|
/// </summary>
|
|
private void PickRandomWord()
|
|
{
|
|
// Get a random index for the themes
|
|
// Then get a random index for a word to pull
|
|
|
|
// First get random index for the themes
|
|
int amountThemes = themeList.themes.Count;
|
|
int themeIndex = Random.Range(0, amountThemes);
|
|
|
|
// Check how many words are in this theme
|
|
int amountWords = themeList.themes[themeIndex].learnables.Count;
|
|
int wordIndex = Random.Range(0, amountWords);
|
|
|
|
// Take the word, but lowercase it.
|
|
currentWord = themeList.themes[themeIndex].learnables[wordIndex].name.ToUpper();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function starts the game after player 1 has entered their word, but only if its length >= 2.
|
|
/// </summary>
|
|
public void TwoPlayer()
|
|
{
|
|
if (MIN_INC_WORD_LENGHT <= currentWord.Length)
|
|
{
|
|
// Reset the model-parameters
|
|
previousSign = null;
|
|
currentTime = 0;
|
|
|
|
// Start the game
|
|
StartGame();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update is called once per frame
|
|
/// </summary>
|
|
public void Update()
|
|
{
|
|
if (mode == 1)
|
|
{
|
|
if (Input.GetKey(KeyCode.Backspace))
|
|
{
|
|
// Remove the last letter from the currentword
|
|
if (0 < currentWord.Length)
|
|
{
|
|
currentWord = currentWord[0..^1];
|
|
inputTextField.text = currentWord;
|
|
}
|
|
Input.ResetInputAxes();
|
|
}
|
|
|
|
gotoGameButton.SetActive(MIN_INC_WORD_LENGHT <= currentWord.Length);
|
|
}
|
|
// The following logic is used to fill the timer
|
|
if ((mode == 1 || mode == 2) &&
|
|
runTime)
|
|
{
|
|
currentTime += Time.deltaTime; // subtract the time since last frame
|
|
if (currentTime > maxTime)
|
|
{
|
|
currentTime = maxTime;
|
|
}
|
|
|
|
float oldValue = timerCircle.fillAmount;
|
|
float newValue = currentTime / maxTime;
|
|
|
|
timerCircle.gameObject.Tween("TimerUpdate", oldValue, newValue, 1f / 60f, TweenScaleFunctions.CubicEaseInOut, (t) =>
|
|
{
|
|
if (timerCircle != null)
|
|
{
|
|
timerCircle.fillAmount = t.CurrentValue;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles sign logic, so that it does not have to run every frame
|
|
/// This function is called when the UpdateFeedback has accepted a letter
|
|
/// </summary>
|
|
public void UpdateSign()
|
|
{
|
|
switch (mode)
|
|
{
|
|
case 0: // Singleplayer or multiplayer?
|
|
break;
|
|
case 1: // Multiplayer word
|
|
{
|
|
if (currentSign != null && currentSign != "" && currentWord.Length < MAX_EXC_WORD_LENGHT)
|
|
{
|
|
confirmPanel.SetActive(true);
|
|
confirmText.text = $"Letter '{currentSign.ToUpper()}' ?";
|
|
mode = 4;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2: // Sign your letter
|
|
if (!guesses.Contains(currentSign))
|
|
{
|
|
mode = 3;
|
|
ConfirmAccept();
|
|
}
|
|
break;
|
|
|
|
case 3: // Confirm signed letter
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void ConfirmAccept()
|
|
{
|
|
string letter = currentSign;
|
|
currentSign = "";
|
|
confirmPanel.SetActive(false);
|
|
|
|
if (mode == 3)
|
|
{
|
|
|
|
if (currentWord.Contains(letter))
|
|
{
|
|
// The guess was correct, we can display all the letters that correspond to the guess
|
|
UpdateWord(letter);
|
|
}
|
|
else
|
|
{
|
|
// The guess was wrong, the wrongs integer needs to be incremented
|
|
wrongs++;
|
|
|
|
// Afterwards, the next stage needs to be displayed
|
|
ChangeSprite();
|
|
}
|
|
|
|
guesses.Add(letter);
|
|
if (usedLettersText.text != "")
|
|
usedLettersText.text += ", ";
|
|
usedLettersText.text += letter.ToString().ToUpper();
|
|
|
|
// The current sign was accepted, return to the game
|
|
mode = 2;
|
|
|
|
if (corrects == currentWord.Length)
|
|
{
|
|
// Victory, deactivate the model and show the scoreboard
|
|
ActivateWin();
|
|
}
|
|
else if (NUMBER_OF_FAILS_BEFORE_GAMEOVER < wrongs)
|
|
{
|
|
// You lost, deactivate the model and show the scoreboard
|
|
ActivateGameOver();
|
|
}
|
|
}
|
|
else if (mode == 4)
|
|
{
|
|
currentWord += letter;
|
|
inputTextField.text = currentWord.ToUpper();
|
|
mode = 1;
|
|
}
|
|
}
|
|
|
|
public void ConfirmDeny()
|
|
{
|
|
confirmPanel.SetActive(false);
|
|
|
|
// The current sign was rejected, return to the game-mode
|
|
if (mode == 3)
|
|
mode = 2;
|
|
else if (mode == 4)
|
|
mode = 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change the image that is being displayed
|
|
/// </summary>
|
|
private void ChangeSprite()
|
|
{
|
|
|
|
// Load the new sprite from the HangmanImages scriptable
|
|
Sprite sprite = images[wrongs];
|
|
|
|
// Set the new sprite as the Image component's source image
|
|
hangmanImage.sprite = sprite;
|
|
|
|
scoreDisplay.text = $"Score: {CalculateScore()}";
|
|
scoreBonus.text = $"{incorrectLetterScore}";
|
|
scoreBonus.color = new Color(0xf5 / 255.0f, 0x49 / 255.0f, 0x3d / 255.0f);
|
|
}
|
|
|
|
/// <summary>
|
|
/// In this function, the letters of the word selected in DisplayWord are updated after a correct guess.
|
|
/// </summary>
|
|
/// <param name="c">The letter that needs to be updated</param>
|
|
private void UpdateWord(string c)
|
|
{
|
|
int hits = 0;
|
|
|
|
for (int i = 0; i < currentWord.Length; i++)
|
|
{
|
|
if (currentWord[i] == c[0])
|
|
{
|
|
// Display the letter and change its background to green
|
|
Image background = letters[i].GetComponent<Image>();
|
|
background.color = new Color(139f / 255f, 212f / 255f, 94f / 255f);
|
|
TMP_Text txt = letters[i].GetComponentInChildren<TMP_Text>();
|
|
txt.text = c;
|
|
|
|
// You correctly guessed a letter
|
|
corrects++;
|
|
hits++;
|
|
}
|
|
}
|
|
|
|
scoreDisplay.text = $"Score: {CalculateScore()}";
|
|
scoreBonus.text = $"+{hits * correctLetterScore}";
|
|
scoreBonus.color = new Color(0x8b / 255.0f, 0xd4 / 255.0f, 0x5e / 255.0f);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function returns the score that the user currently has
|
|
/// </summary>
|
|
/// <returns>The current score of the user</returns>
|
|
private int CalculateScore()
|
|
{
|
|
int won = corrects == currentWord.Length ? 1 : 0;
|
|
return corrects * correctLetterScore + wrongs * incorrectLetterScore + winScore * won;
|
|
}
|
|
|
|
// The following functions originate from Spellingbee
|
|
|
|
/// <summary>
|
|
/// Delete all letter objects
|
|
/// </summary>
|
|
private void DeleteWord()
|
|
{
|
|
for (int i = 0; i < letters.Count; i++)
|
|
{
|
|
Destroy(letters[i]);
|
|
}
|
|
letters.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays the word that needs to be spelled
|
|
/// </summary>
|
|
/// <param name="word">The word to display</param>
|
|
private void DisplayWord(string word)
|
|
{
|
|
for (int i = 0; i < word.Length; i++)
|
|
{
|
|
// Create instance of prefab
|
|
GameObject instance = GameObject.Instantiate(letterPrefab, letterContainer);
|
|
letters.Add(instance);
|
|
|
|
// Dynamically load appearance
|
|
Image background = instance.GetComponent<Image>();
|
|
background.color = Color.clear;
|
|
TMP_Text txt = instance.GetComponentInChildren<TMP_Text>();
|
|
txt.text = Char.ToString('_');
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update and save the scores
|
|
/// </summary>
|
|
private void SaveScores()
|
|
{
|
|
// Calculate new score
|
|
int newScore = CalculateScore();
|
|
// Save the score as a tuple: < int score, string time ago>
|
|
Score score = new Score();
|
|
score.scoreValue = newScore;
|
|
score.time = DateTime.Now.ToString();
|
|
|
|
// Save the new score
|
|
var progress = user.GetMinigameProgress(minigame.index);
|
|
|
|
// Get the current list of scores
|
|
List<Score> latestScores = progress.latestScores;
|
|
List<Score> highestScores = progress.highestScores;
|
|
|
|
// Add the new score
|
|
latestScores.Add(score);
|
|
highestScores.Add(score);
|
|
|
|
// Sort the scores
|
|
highestScores.Sort((a, b) => b.scoreValue.CompareTo(a.scoreValue));
|
|
|
|
// Only save the top 10 scores, so this list doesn't keep growing endlessly
|
|
progress.latestScores = latestScores.Take(10).ToList();
|
|
progress.highestScores = highestScores.Take(10).ToList();
|
|
|
|
UserList.Save();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display win screen
|
|
/// </summary>
|
|
private void ActivateWin()
|
|
{
|
|
// Deactivate the model
|
|
mode = 0;
|
|
|
|
// Save the scores and show the scoreboard
|
|
SaveScores();
|
|
gameEndedPanel.GetComponent<HangmanGameEndedPanel>().GenerateContent(
|
|
guessWord: currentWord.ToLower(),
|
|
correctLetters: corrects,
|
|
incorrectLetters: wrongs,
|
|
sprite: hangmanImage.sprite,
|
|
result: "GEWONNEN",
|
|
score: CalculateScore()
|
|
);
|
|
|
|
gameEndedPanel.SetActive(true);
|
|
|
|
// @lukas stuff
|
|
DeleteWord();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays the game over panel and score values
|
|
/// </summary>
|
|
private void ActivateGameOver()
|
|
{
|
|
// Deactivate the model
|
|
mode = 0;
|
|
|
|
// Save the scores and show the scoreboard
|
|
SaveScores();
|
|
gameEndedPanel.GetComponent<HangmanGameEndedPanel>().GenerateContent(
|
|
guessWord: currentWord.ToLower(),
|
|
correctLetters: corrects,
|
|
incorrectLetters: wrongs,
|
|
sprite: hangmanImage.sprite,
|
|
result: "VERLOREN",
|
|
score: CalculateScore()
|
|
);
|
|
|
|
gameEndedPanel.SetActive(true);
|
|
|
|
DeleteWord();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The updateFunction that is called when new probabilities become available
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected override IEnumerator UpdateFeedback()
|
|
{
|
|
// Get the sign with the highest prediction
|
|
if ((mode == 1 || mode == 2) &&
|
|
signPredictor != null &&
|
|
signPredictor.learnableProbabilities != null)
|
|
{
|
|
KeyValuePair<string, float> highestPrediction = signPredictor.learnableProbabilities.Aggregate((x, y) => x.Value > y.Value ? x : y);
|
|
float accuracy = highestPrediction.Value;
|
|
string predictedSign = highestPrediction.Key;
|
|
|
|
// vvv TEMPORARY STUFF vvv
|
|
if (predictedSign == "J" && accuracy <= 0.965f)
|
|
{
|
|
highestPrediction = signPredictor.learnableProbabilities.Aggregate((x, y) => x.Value > y.Value && x.Key != "J" ? x : y);
|
|
}
|
|
accuracy = highestPrediction.Value;
|
|
predictedSign = highestPrediction.Key;
|
|
// ^^^ TEMPORARY STUFF ^^^
|
|
|
|
// Grab the threshold for the most probable letter
|
|
Learnable letter = fingerSpelling.learnables.Find((l) => l.name == predictedSign);
|
|
float threshold = letter.thresholdPercentage;
|
|
|
|
float oldValue = feedbackProgress.value;
|
|
// use an exponential scale
|
|
float newValue = Mathf.Exp(4 * (Mathf.Clamp(accuracy / threshold, 0.0f, 1.0f) - 1.0f));
|
|
feedbackProgress.gameObject.Tween("FeedbackUpdate", oldValue, newValue, 0.2f, TweenScaleFunctions.CubicEaseInOut, (t) =>
|
|
{
|
|
if (feedbackProgress != null)
|
|
{
|
|
feedbackProgress.value = t.CurrentValue;
|
|
}
|
|
});
|
|
|
|
if (accuracy > threshold)
|
|
{
|
|
feedbackText.text = $"Herkent '{predictedSign}'";
|
|
Color green = new Color(139.0f / 255.0f, 212.0f / 255.0f, 94.0f / 255.0f);
|
|
feedbackText.color = green;
|
|
feedbackProgressImage.color = green;
|
|
}
|
|
else if (accuracy > threshold * 0.9)
|
|
{
|
|
feedbackText.text = $"Lijkt op '{predictedSign}'";
|
|
Color orange = new Color(242.0f / 255.0f, 127.0f / 255.0f, 12.0f / 255.0f);
|
|
feedbackText.color = orange;
|
|
feedbackProgressImage.color = orange;
|
|
}
|
|
else
|
|
{
|
|
feedbackText.text = "Detecteren...";
|
|
Color red = new Color(245.0f / 255.0f, 73.0f / 255.0f, 61.0f / 255.0f);
|
|
feedbackText.color = red;
|
|
feedbackProgressImage.color = red;
|
|
}
|
|
|
|
if (accuracy > threshold)
|
|
{
|
|
if (previousSign != predictedSign)
|
|
{
|
|
// Reset the timer
|
|
previousSign = predictedSign;
|
|
currentTime = 0;
|
|
if ((mode == 1) ||
|
|
(mode == 2 && !guesses.Contains(previousSign.ToUpper())))
|
|
{
|
|
runTime = true;
|
|
}
|
|
timerCircle.fillAmount = currentTime;
|
|
}
|
|
else if (currentTime == maxTime)
|
|
{
|
|
// Set the predictedSign as your guess and update the Hangman
|
|
currentSign = predictedSign;
|
|
UpdateSign();
|
|
// reset the timer and look for a new prediction
|
|
previousSign = null;
|
|
currentTime = 0;
|
|
runTime = false;
|
|
timerCircle.fillAmount = currentTime;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// The sign was dropped, reset the timer
|
|
previousSign = null;
|
|
currentTime = 0;
|
|
runTime = false;
|
|
timerCircle.fillAmount = currentTime;
|
|
}
|
|
|
|
}
|
|
else if (feedbackProgress != null)
|
|
{
|
|
|
|
feedbackProgress.value = 0.0f;
|
|
}
|
|
yield return null;
|
|
}
|
|
}
|