using System; using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; /// /// Contains all game logic for the JustSign game /// public class JustSignController : MonoBehaviour { /// /// All of the words that can be used in this session /// private List words = new List(); /// /// The canvas containing all components /// public Canvas canvas; /// /// The input field where the user can type his or her answer /// public TMP_InputField answerField; /// /// The feedback on the timing /// public TMP_Text feedBack; /// /// The current score /// public TMP_Text scoreDisplay; /// /// Reference to the minigame ScriptableObject /// public Minigame minigame; /// /// The zone that the player should be hitting with his or her inputs /// public GameObject hitZone; /// /// Symbol prefab /// public GameObject symbolPrefab; /// /// Reference to symbol prefab /// public Transform symbolContainer; /// /// All of the available themes /// private ThemeList themeList; /// /// The theme we are currently using /// private Theme currentTheme; /// /// List of strings representing all words on the track /// private List activeWords = new List(); /// /// List of objects representing all symbols on the track /// private List activeSymbols = new List(); /// /// The current score /// private int score; /// /// Width and height of the symbols /// private int symbolSize = 280; /// /// Controls movement speed of symbols (higher -> faster) /// private int moveSpeed = 200; /// /// Starting X-coordinate of a symbol = (-1920 - symbolsize) / 2 /// private int trackX = -1100; /// /// Starting Y-coordinate of a symbol /// private int trackY = -200; /// /// Max distance from hit zone to get perfect score /// private int perfectBoundary = 10; /// /// Score obtained when getting a perfect hit /// private int perfectScore = 5; /// /// Max distance from hit zone to get good score /// private int goodBoundary = 120; /// /// Score obtained when getting a good hit /// private int goodScore = 3; /// /// Max distance from hit zone to get meh score /// private int mehBoundary = 200; /// /// Score obtained when getting a meh hit /// private int mehScore = 1; /// /// Score obtained when getting a terrible hit /// private int terribleScore = -3; /// /// Score obtained when symbol goes offscreen /// private int offscreenScore = -5; /// /// Time at which the last symbol was spawned /// private float lastSpawn; /// /// Determines every how many seconds a symbol should spawn (will become music-dependent later on) /// private float spawnPeriod = 3.0f; /// /// Start is called before the first frame update /// void Start() { 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(); } /// /// Update is called once per frame /// 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; } DestroySymbolAt(matchedSymbolIndex); answerField.text = ""; } // Destroy the oldest symbol if it leaves the screen if (activeSymbols.Count > 0) { if (activeSymbols[0].GetComponent().localPosition.x > -trackX) { DestroySymbolAt(0); score += offscreenScore; } } // Spawn new symbol every spawn period float currentTime = Time.time; if (currentTime - lastSpawn > spawnPeriod) { lastSpawn = currentTime; SpawnNewSymbol(); } // Move all active symbols to the right foreach (GameObject symbol in activeSymbols) { RectTransform rectTransform = symbol.GetComponent(); rectTransform.localPosition = new Vector3(rectTransform.localPosition.x + Time.deltaTime * moveSpeed, trackY, 0); } scoreDisplay.text = "Score: " + score.ToString(); } /// /// Destroy the symbol at the given index /// /// The index of the symbol to destroy void DestroySymbolAt(int index) { activeWords.RemoveAt(index); GameObject symbol = activeSymbols[index]; activeSymbols.RemoveAt(index); Destroy(symbol); } /// /// Create a new symbol at the start of the track /// void SpawnNewSymbol() { // Pick a word that isn't in use yet List unusedWordIndices = new List(); for (int i = 0; i < words.Count; i++) { if (!activeWords.Contains(words[i].name)) { unusedWordIndices.Add(i); } } Learnable newLearnable = words[unusedWordIndices[UnityEngine.Random.Range(0, unusedWordIndices.Count)]]; string nextSymbol = newLearnable.name; GameObject newSymbolObject = GameObject.Instantiate(symbolPrefab, symbolContainer); // Dynamically load appearance Image image = newSymbolObject.GetComponent(); image.sprite = newLearnable.image; image.rectTransform.sizeDelta = new Vector2(symbolSize, symbolSize); // Place the word that the symbol represents under the image TMP_Text text = newSymbolObject.GetComponentInChildren(); text.text = nextSymbol; text.color = Color.black; text.rectTransform.localPosition = new Vector3(0, -160, 0); activeWords.Add(nextSymbol); activeSymbols.Add(newSymbolObject); } }