Resolve WES-43 "Minigame framework"

This commit is contained in:
Dries Van Schuylenbergh
2023-03-06 12:14:25 +00:00
parent f670c0cd0a
commit 9eebf4e4f2
34 changed files with 3092 additions and 2354 deletions

View File

@@ -1,286 +1,296 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class SpellingBeeController : MonoBehaviour {
// All of the words that can be used in this session
private string[] words;
// Where we currently are in the word
private int letterIndex;
// Where we currently are in the word list
private int wordIndex;
// The word that is currently being spelled
private string currentWord;
// All of the available themes
private ThemeList themeList;
// The theme we are currently using
private Theme currentTheme;
// The GameObjects representing the letters
private GameObject[] letters;
// The Image component for displaying the appropriate sprite
public Image image;
// The input field for testing purposes
public TMP_InputField input;
// Current value of timer in seconds
private float timerValue;
// Timer display
public TMP_Text timerText;
// "Game over" or "You win!"
public TMP_Text endText;
// First score display
public TMP_Text correctWordsText;
// Second score display
public TMP_Text correctLettersText;
// The game over panel
public GameObject gameEndedPanel;
// Button for restarting the game
public Button replayButton;
// Indicates if the game is still going
private bool gameEnded;
// Amount of seconds user gets per letter of the current word
// Set to 1 for testing; should be increased later
private int secondsPerLetter = 1;
// Counter that keeps track of how many words have been spelled correctly
private int spelledLetters;
// Counter that keeps track of how many letters have been spelled correctly
private int spelledWords;
// Start is called before the first frame update
void Start() {
spelledLetters = 0;
// We use -1 instead of 0 so SetNextWord can simply increment it each time
spelledWords = -1;
gameEnded = false;
wordIndex = 0;
input.text = "";
timerValue = 0.0f;
gameEndedPanel.SetActive(false);
replayButton.onClick.AddListener(() => Start());
themeList = ThemeLoader.loadJson();
currentTheme = FindThemeByName(PlayerPrefs.GetString("themeName"));
words = currentTheme.words;
ShuffleWords();
SetNextWord();
}
// Update is called once per frame
void Update() {
if (!gameEnded) {
if (input.text.Length > 0) {
CheckChar(input.text[0]);
}
timerValue -= Time.deltaTime;
if (timerValue <= 0.0f) {
timerValue = 0.0f;
ActivateGameOver();
}
int minutes = Mathf.FloorToInt(timerValue / 60.0f);
int seconds = Mathf.FloorToInt(timerValue % 60.0f);
timerText.text = string.Format("{0:00}:{1:00}", minutes, seconds);
}
}
// Randomly shuffle the list of words
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
string temp = words[i];
words[i] = words[j];
words[j] = temp;
}
}
// Displays the game over panel and score values
void ActivateGameOver() {
DeleteWord();
endText.text = "GAME OVER";
correctLettersText.text = "Correctly spelled letters: " + spelledLetters.ToString();
correctWordsText.text = "Correctly spelled words: " + spelledWords.ToString();
gameEndedPanel.SetActive(true);
gameEndedPanel.transform.SetAsLastSibling();
gameEnded = true;
}
// Display win screen
void ActivateWin() {
DeleteWord();
endText.text = "YOU WIN!";
correctLettersText.text = "Your time: " + spelledLetters.ToString();
int totalWordsDuration = 0;
foreach (string word in words) {
totalWordsDuration += word.Length * secondsPerLetter + 1;
}
// How much time was spent by the player
int spentTime = totalWordsDuration - (int) timerValue;
int seconds = spentTime % 60;
int minutes = spentTime / 60;
if (minutes == 0) {
correctLettersText.text = "Your time: " + seconds + " seconds";
} else {
correctLettersText.text = "Your time: " + minutes + " minutes and " + seconds + " seconds";
}
correctWordsText.text = "";
gameEndedPanel.SetActive(true);
gameEndedPanel.transform.SetAsLastSibling();
gameEnded = true;
}
// Check if the correct char has been given as input
void CheckChar(char letter) {
if (Char.ToUpper(letter) == Char.ToUpper(currentWord[letterIndex]) && input.text.Length == 1) {
letters[letterIndex].GetComponent<Image>().color = Color.green;
input.text = "";
spelledLetters++;
letterIndex++;
if (letterIndex >= currentWord.Length) {
DeleteWord();
StartCoroutine(Wait());
SetNextWord();
}
}
}
// Delete all letter objects
void DeleteWord() {
for (int i = 0; i < currentWord.Length; i++) {
Destroy(letters[i]);
}
}
// Adds seconds to timer
void AddSeconds(int seconds) {
timerValue += (float) seconds;
}
// Find the chosen theme by its name
Theme FindThemeByName(string themeName) {
int themeIndex = 0;
while (themeIndex < themeList.themes.Length) {
Theme theme = themeList.themes[themeIndex];
if (theme.name == themeName) {
return theme;
}
themeIndex++;
}
Debug.Log("Requested theme not found");
return null;
}
// Display next word in the series
void SetNextWord() {
spelledWords++;
if (wordIndex < words.Length) {
currentWord = words[wordIndex];
ChangeSprite(currentWord);
DisplayWord(currentWord);
AddSeconds(currentWord.Length * secondsPerLetter + 1);
letterIndex = 0;
wordIndex++;
} else {
ActivateWin();
}
}
// Displays the word that needs to be spelled
void DisplayWord(string word) {
int length = word.Length;
int canvasWidth = 1920;
int maxWidth = 1080;
int spacing = maxWidth / (length - 1);
letters = new GameObject[length];
int[] xvalues = new int[length];
for (int i = 0; i < length; i++) {
xvalues[i] = (canvasWidth - maxWidth) / 2 + i * spacing;
}
for (int i = 0; i < length; i++) {
GameObject colorBox = new GameObject("Letter box");
colorBox.transform.SetParent(gameObject.transform);
letters[i] = colorBox;
// Add an Image component to the new GameObject
Image background = colorBox.AddComponent<Image>();
background.color = Color.red;
background.sprite = UnityEditor.AssetDatabase.GetBuiltinExtraResource<Sprite>("UI/Skin/UISprite.psd");
// Create a new child GameObject to hold the Text component
GameObject letterObject = new GameObject("Text");
letterObject.transform.SetParent(colorBox.transform);
// Add a Text component to the new child GameObject
Text letterComponent = letterObject.AddComponent<Text>();
letterComponent.text = Char.ToString(Char.ToUpper(word[i]));
letterComponent.alignment = TextAnchor.MiddleCenter;
letterComponent.fontSize = 60;
// Set the font of the Text component
letterComponent.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
// Set the position of the parent GameObject
colorBox.transform.position = new Vector2(xvalues[i], 450);
}
}
// Change the image that is being displayed
void ChangeSprite(string spriteName) {
Image imageComponent = image.GetComponent<Image>();
// Load the new sprite from the Resources folder
Sprite sprite = Resources.Load<Sprite>("SpellingBee/images/" + spriteName);
// Set the new sprite as the Image component's source image
image.sprite = sprite;
}
IEnumerator Wait() {
yield return new WaitForSecondsRealtime(2);
}
}
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class GameController : MonoBehaviour
{
// All of the words that can be used in this session
private string[] words;
// Where we currently are in the word
private int letterIndex;
// Where we currently are in the word list
private int wordIndex;
// The word that is currently being spelled
private string currentWord;
// All of the available themes
private ThemeList themeList;
// The theme we are currently using
private Theme currentTheme;
// The input field for testing purposes
public TMP_InputField input;
// Current value of timer in seconds
private float timerValue;
// "Game over" or "You win!"
public TMP_Text endText;
// First score display
public TMP_Text correctWordsText;
// Second score display
public TMP_Text correctLettersText;
// The game over panel
public GameObject gameEndedPanel;
// Button for restarting the game
public Button replayButton;
// Indicates if the game is still going
private bool gameEnded;
// Amount of seconds user gets per letter of the current word
// Set to 1 for testing; should be increased later
private int secondsPerLetter = 1;
// Counter that keeps track of how many words have been spelled correctly
private int spelledLetters;
// Counter that keeps track of how many letters have been spelled correctly
private int spelledWords;
[Header("Letter prefab")]
// Letter prefab
public GameObject letterPrefab;
[Header("UI References")]
// Reference to letter prefab
public Transform letterContainer;
// The Image component for displaying the appropriate sprite
public Image wordImage;
// Timer display
public TMP_Text timerText;
[Header("private variables")]
// The GameObjects representing the letters
private List<GameObject> letters = new List<GameObject>();
// Start is called before the first frame update
public void Start()
{
spelledLetters = 0;
// We use -1 instead of 0 so SetNextWord can simply increment it each time
spelledWords = -1;
gameEnded = false;
wordIndex = 0;
input.text = "";
timerValue = 0.0f;
gameEndedPanel.SetActive(false);
replayButton.onClick.AddListener(Start);
// TODO: change to ScriptableObject
themeList = ThemeLoader.LoadJson();
currentTheme = FindThemeByName(PlayerPrefs.GetString("themeName"));
words = currentTheme.words;
ShuffleWords();
SetNextWord();
}
// Update is called once per frame
public void Update()
{
if (!gameEnded)
{
if (input.text.Length > 0)
{
CheckChar(input.text[0]);
}
timerValue -= Time.deltaTime;
if (timerValue <= 0.0f)
{
timerValue = 0.0f;
ActivateGameOver();
}
int minutes = Mathf.FloorToInt(timerValue / 60.0f);
int seconds = Mathf.FloorToInt(timerValue % 60.0f);
timerText.text = string.Format("{0:00}:{1:00}", minutes, seconds);
}
}
// Randomly shuffle the list of words
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]);
}
}
// Displays the game over panel and score values
private void ActivateGameOver()
{
DeleteWord();
endText.text = "GAME OVER";
correctLettersText.text = "Correctly spelled letters: " + spelledLetters.ToString();
correctWordsText.text = "Correctly spelled words: " + spelledWords.ToString();
gameEndedPanel.SetActive(true);
gameEndedPanel.transform.SetAsLastSibling();
gameEnded = true;
}
// Display win screen
private void ActivateWin()
{
DeleteWord();
endText.text = "YOU WIN!";
correctLettersText.text = "Your time: " + spelledLetters.ToString();
int totalWordsDuration = 0;
foreach (string word in words)
{
totalWordsDuration += word.Length * secondsPerLetter + 1;
}
// How much time was spent by the player
int spentTime = totalWordsDuration - (int)timerValue;
int seconds = spentTime % 60;
int minutes = spentTime / 60;
if (minutes == 0)
{
correctLettersText.text = "Your time: " + seconds + " seconds";
}
else
{
correctLettersText.text = "Your time: " + minutes + " minutes and " + seconds + " seconds";
}
correctWordsText.text = "";
gameEndedPanel.SetActive(true);
gameEndedPanel.transform.SetAsLastSibling();
gameEnded = true;
}
// Check if the correct char has been given as input
private void CheckChar(char letter)
{
if (Char.ToUpper(letter) == Char.ToUpper(currentWord[letterIndex]) && input.text.Length == 1)
{
letters[letterIndex].GetComponent<Image>().color = Color.green;
input.text = "";
spelledLetters++;
letterIndex++;
if (letterIndex >= currentWord.Length)
{
DeleteWord();
StartCoroutine(Wait());
SetNextWord();
}
}
}
// Delete all letter objects
private void DeleteWord()
{
for (int i = 0; i < currentWord.Length; i++)
{
Destroy(letters[i]);
}
letters.Clear();
}
// Adds seconds to timer
private void AddSeconds(int seconds)
{
timerValue += (float)seconds;
}
// Find the chosen theme by its name
private Theme FindThemeByName(string themeName)
{
int themeIndex = 0;
while (themeIndex < themeList.themes.Length)
{
Theme theme = themeList.themes[themeIndex];
if (theme.name == themeName)
{
return theme;
}
themeIndex++;
}
Debug.Log("Requested theme not found");
return null;
}
// Display next word in the series
private void SetNextWord()
{
spelledWords++;
if (wordIndex < words.Length)
{
currentWord = words[wordIndex];
ChangeSprite(currentWord);
DisplayWord(currentWord);
AddSeconds(currentWord.Length * secondsPerLetter + 1);
letterIndex = 0;
wordIndex++;
}
else
{
ActivateWin();
}
}
// Displays the word that needs to be spelled
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.red;
TMP_Text txt = instance.GetComponentInChildren<TMP_Text>();
txt.text = Char.ToString(Char.ToUpper(word[i]));
}
}
// Change the image that is being displayed
private void ChangeSprite(string spriteName)
{
// Load the new sprite from the Resources folder
Sprite sprite = Resources.Load<Sprite>("SpellingBee/images/" + spriteName);
// Set the new sprite as the Image component's source image
wordImage.sprite = sprite;
}
private IEnumerator Wait()
{
yield return new WaitForSecondsRealtime(2);
}
}

View File

@@ -1,67 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.SceneManagement;
public class SpellingBeeThemeSelectionController : MonoBehaviour
{
private int buttonHeight = 140;
private int buttonWidth = 600;
private int canvasWidth = 1920;
// Start is called before the first frame update
void Start()
{
ThemeList themeList = ThemeLoader.loadJson();
for (int i = 0; i < themeList.themes.Length; i++)
{
Theme theme = themeList.themes[i];
// First, you need to create a new button game object
GameObject newButton = new GameObject("Button - " + theme.name);
newButton.transform.SetParent(gameObject.transform);
// Then, add the button component to the game object
Button buttonComponent = newButton.AddComponent<Button>();
buttonComponent.onClick.AddListener(() => OnButtonClick(theme.name));
GameObject colorBox = new GameObject("Letter box");
colorBox.transform.SetParent(newButton.transform);
Image background = colorBox.AddComponent<Image>();
background.rectTransform.sizeDelta = new Vector2(buttonWidth, buttonHeight);
background.color = Color.black;
background.sprite = UnityEditor.AssetDatabase.GetBuiltinExtraResource<Sprite>("UI/Skin/UIMask.psd");
// Create the text game object
GameObject textObject = new GameObject("Text - " + theme.name);
textObject.transform.SetParent(newButton.transform);
// Set the position of the button game object
int xPos = (int)(i % 2 == 0 ? canvasWidth * 0.25f : canvasWidth * 0.75f);
int yPos = (1 + i / 2) * 180;
newButton.transform.position = new Vector2(xPos, yPos);
// Add the text component to the game object
Text textComponent = textObject.AddComponent<Text>();
textComponent.alignment = TextAnchor.MiddleCenter;
textComponent.color = new Color(50, 50, 50);
textComponent.text = theme.name;
textComponent.rectTransform.sizeDelta = new Vector2(buttonWidth, buttonHeight);
textComponent.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
textComponent.fontSize = 36;
}
}
void OnButtonClick(string clickedTheme)
{
PlayerPrefs.SetString("themeName", clickedTheme);
SceneManager.LoadScene("SpellingBee");
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class ThemeItem : MonoBehaviour
{
// TODO: change to ScriptableObject Theme;
[Header("ScriptableObject Theme")]
public string themeTitle;
public string themeDescription;
public UnityAction startGameCallback;
[Header("UI references")]
// Reference to thumbnail object
public TMP_Text title;
// Reference to description object
public TMP_Text description;
// Refetence to object so correct callback can be trigger on click
public Button button;
void Start()
{
// Use public function so that this component can get Instantiated
GenerateContent();
}
public void GenerateContent()
{
// Set appearance
title.text = themeTitle;
// TODO: make description only visible when hovering
description.text = themeDescription;
// Add click functionality
button.onClick.AddListener(startGameCallback);
}
}

View File

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

View File

@@ -4,13 +4,15 @@ using UnityEngine;
// JSON structure containing all themes/words
[System.Serializable]
public class ThemeList {
public class ThemeList
{
public Theme[] themes;
}
// Object representing part of the JSON containing word data
[System.Serializable]
public class Theme {
public class Theme
{
public string name;
public string description;
public string[] words;
@@ -19,7 +21,8 @@ public class Theme {
public class ThemeLoader : MonoBehaviour
{
// Loads the JSON file containing all of the themes
public static ThemeList loadJson() {
public static ThemeList LoadJson()
{
TextAsset themeJson = Resources.Load<TextAsset>("SpellingBee/words");
return JsonUtility.FromJson<ThemeList>(themeJson.text);
}

View File

@@ -0,0 +1,42 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.SceneManagement;
public class ThemeSelectionController : MonoBehaviour
{
[Header("Theme Selection")]
// Theme prefab
public GameObject themePrefab;
// Reference to container holding all theme-buttons
public Transform themesContainer;
public void Start()
{
// TODO: change to ScriptableObject
ThemeList themeList = ThemeLoader.LoadJson();
for (int i = 0; i < themeList.themes.Length; i++)
{
Theme theme = themeList.themes[i];
// First, you need to create a new button game object
GameObject instance = GameObject.Instantiate(themePrefab, themesContainer);
// Dynamically load appearance
ThemeItem item = instance.GetComponent<ThemeItem>();
item.themeTitle = theme.name;
item.themeDescription = theme.description;
item.startGameCallback = () => OnButtonClick(theme.name);
}
}
public void OnButtonClick(string clickedTheme)
{
PlayerPrefs.SetString("themeName", clickedTheme);
SceneManager.LoadScene("Game");
}
}