1369 lines
35 KiB
C#
1369 lines
35 KiB
C#
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.UI;
|
|||
|
|
using TMPro;
|
|||
|
|
using UnityEngine.SceneManagement;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Collections;
|
|||
|
|
|
|||
|
|
public class Pronunciation13Mod : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
[Header("TXT FILE")]
|
|||
|
|
public TextAsset pronunciationFile;
|
|||
|
|
|
|||
|
|
[Header("UI")]
|
|||
|
|
public TMP_Text questionText;
|
|||
|
|
public TMP_Text languageText;
|
|||
|
|
|
|||
|
|
public Button[] answerButtons;
|
|||
|
|
public TMP_Text[] answerTexts;
|
|||
|
|
|
|||
|
|
public TMP_Text feedbackText;
|
|||
|
|
public Button submitButton;
|
|||
|
|
|
|||
|
|
public TMP_Text scoreText;
|
|||
|
|
public TMP_Text comboText;
|
|||
|
|
public TMP_Text timerText;
|
|||
|
|
|
|||
|
|
[Header("QUESTION FONT")]
|
|||
|
|
public bool questionUppercase = true;
|
|||
|
|
public float questionFontSize = 86f;
|
|||
|
|
public Color questionColor = Color.white;
|
|||
|
|
public FontStyles questionFontStyle = FontStyles.Bold;
|
|||
|
|
public TextAlignmentOptions questionAlignment = TextAlignmentOptions.Center;
|
|||
|
|
public float questionCharacterSpacing = 4f;
|
|||
|
|
|
|||
|
|
[Header("EXTRA BUTTONS")]
|
|||
|
|
public Button extraTimeButton;
|
|||
|
|
public Button extraHintButton;
|
|||
|
|
public Button extraSkipButton;
|
|||
|
|
|
|||
|
|
public TMP_Text extraTimeCooldownText;
|
|||
|
|
public TMP_Text extraHintCooldownText;
|
|||
|
|
public TMP_Text extraSkipCooldownText;
|
|||
|
|
|
|||
|
|
[Header("EXTRA SETTINGS")]
|
|||
|
|
public float extraTimeAmount = 5f;
|
|||
|
|
public float cooldownDuration = 5f;
|
|||
|
|
|
|||
|
|
[Header("SCENES")]
|
|||
|
|
public Button backButton;
|
|||
|
|
public string backSceneName = "modsec 13";
|
|||
|
|
public string resultSceneName = "modres 13";
|
|||
|
|
|
|||
|
|
[Header("LANGUAGE")]
|
|||
|
|
public string forcedLanguageCode = "";
|
|||
|
|
|
|||
|
|
[Header("GAME SETTINGS")]
|
|||
|
|
public float startTime = 60f;
|
|||
|
|
public int basePoints = 50;
|
|||
|
|
public int wrongPenalty = 50;
|
|||
|
|
public float correctTimeBonus = 2f;
|
|||
|
|
public float nextQuestionDelay = 1.15f;
|
|||
|
|
|
|||
|
|
[Header("COMBO")]
|
|||
|
|
public float mult_1to2 = 1f;
|
|||
|
|
public float mult_3to4 = 1.15f;
|
|||
|
|
public float mult_5to7 = 1.3f;
|
|||
|
|
public float mult_8plus = 1.5f;
|
|||
|
|
|
|||
|
|
[Header("COLORS")]
|
|||
|
|
public Color normalButtonColor = new Color32(120, 70, 220, 255);
|
|||
|
|
public Color selectedButtonColor = new Color32(155, 105, 255, 255);
|
|||
|
|
public Color correctButtonColor = new Color32(70, 240, 130, 255);
|
|||
|
|
public Color wrongButtonColor = new Color32(255, 80, 95, 255);
|
|||
|
|
public Color disabledButtonColor = new Color32(90, 75, 120, 255);
|
|||
|
|
|
|||
|
|
[Header("ANIMATION")]
|
|||
|
|
[Range(0.80f, 1f)]
|
|||
|
|
public float selectedScale = 0.94f;
|
|||
|
|
|
|||
|
|
public float selectAnimTime = 0.12f;
|
|||
|
|
public float enterAnimTime = 0.30f;
|
|||
|
|
public float enterStagger = 0.07f;
|
|||
|
|
|
|||
|
|
const string modKey = "Mod13";
|
|||
|
|
|
|||
|
|
readonly string[] langCodes = { "tr", "en", "es", "de", "fr", "pt" };
|
|||
|
|
readonly string[] langNames = { "TR", "EN", "ES", "DE", "FR", "PT" };
|
|||
|
|
|
|||
|
|
class PronunciationEntry
|
|||
|
|
{
|
|||
|
|
public string[] words = new string[6];
|
|||
|
|
|
|||
|
|
// Unity string[,] serialize edemez.
|
|||
|
|
// 6 dil x 4 seçenek = 24 alan olarak tutulur.
|
|||
|
|
public string[] options = new string[24];
|
|||
|
|
|
|||
|
|
public void SetOption(int langIndex, int optionIndex, string value)
|
|||
|
|
{
|
|||
|
|
options[langIndex * 4 + optionIndex] = value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string GetOption(int langIndex, int optionIndex)
|
|||
|
|
{
|
|||
|
|
return options[langIndex * 4 + optionIndex];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
readonly List<PronunciationEntry> questionPool = new List<PronunciationEntry>();
|
|||
|
|
|
|||
|
|
int selectedLangIndex = 1;
|
|||
|
|
int questionIndex = 0;
|
|||
|
|
int correctAnswerIndex = -1;
|
|||
|
|
int selectedAnswer = -1;
|
|||
|
|
int lastQuestionShown = -1;
|
|||
|
|
|
|||
|
|
int score = 0;
|
|||
|
|
int combo = 0;
|
|||
|
|
int correctCount = 0;
|
|||
|
|
int wrongCount = 0;
|
|||
|
|
int bestComboThisRun = 0;
|
|||
|
|
|
|||
|
|
float timeLeft;
|
|||
|
|
|
|||
|
|
bool gameOver = false;
|
|||
|
|
bool waitingNextQuestion = false;
|
|||
|
|
|
|||
|
|
bool extraTimeReady = true;
|
|||
|
|
bool extraHintReady = true;
|
|||
|
|
bool extraSkipReady = true;
|
|||
|
|
|
|||
|
|
Vector3[] originalButtonScales;
|
|||
|
|
Vector3[] originalButtonPositions;
|
|||
|
|
|
|||
|
|
Coroutine[] buttonScaleCoroutines;
|
|||
|
|
Coroutine[] buttonEnterCoroutines;
|
|||
|
|
|
|||
|
|
readonly Color timerNormalColor = Color.white;
|
|||
|
|
readonly Color timerWarnColor = new Color32(255, 80, 80, 255);
|
|||
|
|
|
|||
|
|
void Awake()
|
|||
|
|
{
|
|||
|
|
LoadQuestions();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Start()
|
|||
|
|
{
|
|||
|
|
selectedLangIndex = ResolveLanguageIndex();
|
|||
|
|
|
|||
|
|
timeLeft = PlayerPrefs.GetFloat("SelectedGameTime", startTime);
|
|||
|
|
|
|||
|
|
if (timeLeft <= 0)
|
|||
|
|
timeLeft = startTime;
|
|||
|
|
|
|||
|
|
if (questionPool.Count < 1)
|
|||
|
|
{
|
|||
|
|
Debug.LogError("Mod 13 TXT boş veya hatalı!");
|
|||
|
|
enabled = false;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (answerButtons == null || answerButtons.Length < 4 || answerTexts == null || answerTexts.Length < 4)
|
|||
|
|
{
|
|||
|
|
Debug.LogError("Mod13 için 4 answerButtons ve 4 answerTexts gerekli!");
|
|||
|
|
enabled = false;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
HideCooldownTexts();
|
|||
|
|
CacheButtonData();
|
|||
|
|
ForceButtonTransitionNone();
|
|||
|
|
PrepareQuestionFont();
|
|||
|
|
PrepareFeedbackText();
|
|||
|
|
BindMainButtons();
|
|||
|
|
|
|||
|
|
Shuffle(questionPool);
|
|||
|
|
|
|||
|
|
UpdateScore();
|
|||
|
|
UpdateCombo();
|
|||
|
|
UpdateTimerUI();
|
|||
|
|
UpdateLanguageUI();
|
|||
|
|
|
|||
|
|
NextQuestion();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Update()
|
|||
|
|
{
|
|||
|
|
if (gameOver)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
timeLeft -= Time.deltaTime;
|
|||
|
|
|
|||
|
|
if (timeLeft <= 0)
|
|||
|
|
{
|
|||
|
|
timeLeft = 0;
|
|||
|
|
UpdateTimerUI();
|
|||
|
|
GameOver();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
UpdateTimerUI();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void LoadQuestions()
|
|||
|
|
{
|
|||
|
|
questionPool.Clear();
|
|||
|
|
|
|||
|
|
if (pronunciationFile == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogError("Pronunciation13 TXT atanmadı!");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
string[] lines = pronunciationFile.text.Split(
|
|||
|
|
new[] { '\n', '\r' },
|
|||
|
|
System.StringSplitOptions.RemoveEmptyEntries
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
|
|||
|
|
{
|
|||
|
|
string line = lines[lineIndex].Trim();
|
|||
|
|
|
|||
|
|
if (string.IsNullOrWhiteSpace(line))
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
string lower = line.ToLower();
|
|||
|
|
|
|||
|
|
if (lower.StartsWith("tr,en,es,de,fr,pt"))
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
if (lower.StartsWith("tr|en|es|de|fr|pt"))
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
string[] p = SplitLineSmart(line);
|
|||
|
|
|
|||
|
|
if (p.Length < 30)
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning("Mod13 eksik satır atlandı. Satır: " + (lineIndex + 1) + " Alan: " + p.Length);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
PronunciationEntry e = new PronunciationEntry();
|
|||
|
|
|
|||
|
|
bool valid = true;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < 6; i++)
|
|||
|
|
{
|
|||
|
|
e.words[i] = CleanField(p[i]);
|
|||
|
|
|
|||
|
|
if (string.IsNullOrWhiteSpace(e.words[i]))
|
|||
|
|
valid = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int cursor = 6;
|
|||
|
|
|
|||
|
|
for (int lang = 0; lang < 6; lang++)
|
|||
|
|
{
|
|||
|
|
for (int opt = 0; opt < 4; opt++)
|
|||
|
|
{
|
|||
|
|
string value = CleanField(p[cursor]);
|
|||
|
|
e.SetOption(lang, opt, value);
|
|||
|
|
|
|||
|
|
if (string.IsNullOrWhiteSpace(value))
|
|||
|
|
valid = false;
|
|||
|
|
|
|||
|
|
cursor++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (valid)
|
|||
|
|
questionPool.Add(e);
|
|||
|
|
else
|
|||
|
|
Debug.LogWarning("Mod13 boş alanlı satır atlandı. Satır: " + (lineIndex + 1));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
string[] SplitLineSmart(string line)
|
|||
|
|
{
|
|||
|
|
if (line.Contains("|"))
|
|||
|
|
return line.Split('|');
|
|||
|
|
|
|||
|
|
if (line.Contains("\t"))
|
|||
|
|
return line.Split('\t');
|
|||
|
|
|
|||
|
|
return SplitCsvLine(line).ToArray();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<string> SplitCsvLine(string line)
|
|||
|
|
{
|
|||
|
|
List<string> result = new List<string>();
|
|||
|
|
bool inQuotes = false;
|
|||
|
|
string current = "";
|
|||
|
|
|
|||
|
|
for (int i = 0; i < line.Length; i++)
|
|||
|
|
{
|
|||
|
|
char ch = line[i];
|
|||
|
|
|
|||
|
|
if (ch == '"')
|
|||
|
|
{
|
|||
|
|
inQuotes = !inQuotes;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (ch == ',' && !inQuotes)
|
|||
|
|
{
|
|||
|
|
result.Add(current);
|
|||
|
|
current = "";
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
current += ch;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
result.Add(current);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
string CleanField(string s)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(s))
|
|||
|
|
return "";
|
|||
|
|
|
|||
|
|
return s.Trim().Trim('"').Trim();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int ResolveLanguageIndex()
|
|||
|
|
{
|
|||
|
|
string code = forcedLanguageCode.Trim().ToLower();
|
|||
|
|
|
|||
|
|
if (string.IsNullOrEmpty(code))
|
|||
|
|
code = PlayerPrefs.GetString("PronunciationLangCode", "").Trim().ToLower();
|
|||
|
|
|
|||
|
|
if (string.IsNullOrEmpty(code))
|
|||
|
|
code = PlayerPrefs.GetString("QuestionLangCode", "").Trim().ToLower();
|
|||
|
|
|
|||
|
|
if (!string.IsNullOrEmpty(code))
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < langCodes.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (code == langCodes[i])
|
|||
|
|
return i;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int appLang = PlayerPrefs.GetInt("AppLanguage", 1);
|
|||
|
|
|
|||
|
|
if (appLang < 0 || appLang > 5)
|
|||
|
|
appLang = 1;
|
|||
|
|
|
|||
|
|
return appLang;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void PrepareQuestionFont()
|
|||
|
|
{
|
|||
|
|
if (questionText == null)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
questionText.fontSize = questionFontSize;
|
|||
|
|
questionText.color = FullAlpha(questionColor);
|
|||
|
|
questionText.fontStyle = questionFontStyle;
|
|||
|
|
questionText.alignment = questionAlignment;
|
|||
|
|
questionText.characterSpacing = questionCharacterSpacing;
|
|||
|
|
|
|||
|
|
// Unity 6 uyumlu yeni kullanım
|
|||
|
|
questionText.textWrappingMode = TextWrappingModes.NoWrap;
|
|||
|
|
|
|||
|
|
questionText.overflowMode = TextOverflowModes.Overflow;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void UpdateLanguageUI()
|
|||
|
|
{
|
|||
|
|
if (languageText != null)
|
|||
|
|
languageText.text = "LANG: " + langNames[selectedLangIndex];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void BindMainButtons()
|
|||
|
|
{
|
|||
|
|
if (submitButton != null)
|
|||
|
|
{
|
|||
|
|
submitButton.onClick.RemoveAllListeners();
|
|||
|
|
submitButton.onClick.AddListener(OnSubmit);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (extraTimeButton != null)
|
|||
|
|
{
|
|||
|
|
extraTimeButton.onClick.RemoveAllListeners();
|
|||
|
|
extraTimeButton.onClick.AddListener(OnExtraTime);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (extraHintButton != null)
|
|||
|
|
{
|
|||
|
|
extraHintButton.onClick.RemoveAllListeners();
|
|||
|
|
extraHintButton.onClick.AddListener(OnExtraHint);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (extraSkipButton != null)
|
|||
|
|
{
|
|||
|
|
extraSkipButton.onClick.RemoveAllListeners();
|
|||
|
|
extraSkipButton.onClick.AddListener(OnExtraSkip);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (backButton != null)
|
|||
|
|
{
|
|||
|
|
backButton.onClick.RemoveAllListeners();
|
|||
|
|
backButton.onClick.AddListener(() =>
|
|||
|
|
{
|
|||
|
|
SceneManager.LoadScene(backSceneName);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void BindAnswerButtons()
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < answerButtons.Length; i++)
|
|||
|
|
{
|
|||
|
|
int index = i;
|
|||
|
|
|
|||
|
|
if (answerButtons[i] == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
answerButtons[i].onClick.RemoveAllListeners();
|
|||
|
|
answerButtons[i].onClick.AddListener(() =>
|
|||
|
|
{
|
|||
|
|
SelectAnswer(index);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void HideCooldownTexts()
|
|||
|
|
{
|
|||
|
|
if (extraTimeCooldownText != null)
|
|||
|
|
extraTimeCooldownText.gameObject.SetActive(false);
|
|||
|
|
|
|||
|
|
if (extraHintCooldownText != null)
|
|||
|
|
extraHintCooldownText.gameObject.SetActive(false);
|
|||
|
|
|
|||
|
|
if (extraSkipCooldownText != null)
|
|||
|
|
extraSkipCooldownText.gameObject.SetActive(false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void CacheButtonData()
|
|||
|
|
{
|
|||
|
|
int len = answerButtons.Length;
|
|||
|
|
|
|||
|
|
originalButtonScales = new Vector3[len];
|
|||
|
|
originalButtonPositions = new Vector3[len];
|
|||
|
|
buttonScaleCoroutines = new Coroutine[len];
|
|||
|
|
buttonEnterCoroutines = new Coroutine[len];
|
|||
|
|
|
|||
|
|
for (int i = 0; i < len; i++)
|
|||
|
|
{
|
|||
|
|
if (answerButtons[i] == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
originalButtonScales[i] = answerButtons[i].transform.localScale;
|
|||
|
|
originalButtonPositions[i] = answerButtons[i].transform.localPosition;
|
|||
|
|
|
|||
|
|
if (answerButtons[i].GetComponent<CanvasGroup>() == null)
|
|||
|
|
answerButtons[i].gameObject.AddComponent<CanvasGroup>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void ForceButtonTransitionNone()
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < answerButtons.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (answerButtons[i] == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
answerButtons[i].transition = Selectable.Transition.None;
|
|||
|
|
|
|||
|
|
Image img = answerButtons[i].image;
|
|||
|
|
|
|||
|
|
if (img != null)
|
|||
|
|
img.color = FullAlpha(normalButtonColor);
|
|||
|
|
|
|||
|
|
CanvasGroup cg = answerButtons[i].GetComponent<CanvasGroup>();
|
|||
|
|
|
|||
|
|
if (cg != null)
|
|||
|
|
{
|
|||
|
|
cg.alpha = 1f;
|
|||
|
|
cg.interactable = true;
|
|||
|
|
cg.blocksRaycasts = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void PrepareFeedbackText()
|
|||
|
|
{
|
|||
|
|
if (feedbackText == null)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
feedbackText.alignment = TextAlignmentOptions.Center;
|
|||
|
|
feedbackText.fontSize = Mathf.Max(feedbackText.fontSize, 72f);
|
|||
|
|
feedbackText.text = "";
|
|||
|
|
feedbackText.color = FullAlpha(feedbackText.color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void NextQuestion()
|
|||
|
|
{
|
|||
|
|
if (gameOver)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
waitingNextQuestion = false;
|
|||
|
|
selectedAnswer = -1;
|
|||
|
|
|
|||
|
|
ResetButtons();
|
|||
|
|
|
|||
|
|
if (questionIndex >= questionPool.Count)
|
|||
|
|
{
|
|||
|
|
questionIndex = 0;
|
|||
|
|
Shuffle(questionPool);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (questionPool.Count > 1 && questionIndex == lastQuestionShown)
|
|||
|
|
{
|
|||
|
|
questionIndex++;
|
|||
|
|
|
|||
|
|
if (questionIndex >= questionPool.Count)
|
|||
|
|
{
|
|||
|
|
questionIndex = 0;
|
|||
|
|
Shuffle(questionPool);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
lastQuestionShown = questionIndex;
|
|||
|
|
PronunciationEntry chosen = questionPool[questionIndex++];
|
|||
|
|
|
|||
|
|
string word = chosen.words[selectedLangIndex];
|
|||
|
|
|
|||
|
|
if (questionUppercase)
|
|||
|
|
word = word.ToUpper();
|
|||
|
|
|
|||
|
|
if (questionText != null)
|
|||
|
|
{
|
|||
|
|
questionText.text = word;
|
|||
|
|
StartCoroutine(QuestionEnterAnim(questionText));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<string> options = new List<string>();
|
|||
|
|
|
|||
|
|
for (int i = 0; i < 4; i++)
|
|||
|
|
options.Add(chosen.GetOption(selectedLangIndex, i));
|
|||
|
|
|
|||
|
|
string correctText = chosen.GetOption(selectedLangIndex, 0);
|
|||
|
|
|
|||
|
|
Shuffle(options);
|
|||
|
|
|
|||
|
|
correctAnswerIndex = options.IndexOf(correctText);
|
|||
|
|
|
|||
|
|
for (int i = 0; i < 4; i++)
|
|||
|
|
{
|
|||
|
|
if (answerTexts[i] != null)
|
|||
|
|
answerTexts[i].text = options[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
BindAnswerButtons();
|
|||
|
|
|
|||
|
|
for (int i = 0; i < answerButtons.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (answerButtons[i] == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
if (buttonEnterCoroutines[i] != null)
|
|||
|
|
StopCoroutine(buttonEnterCoroutines[i]);
|
|||
|
|
|
|||
|
|
buttonEnterCoroutines[i] = StartCoroutine(ButtonEnterAnim(answerButtons[i], i));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void ResetButtons()
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < answerButtons.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (answerButtons[i] == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
StopButtonCoroutines(i);
|
|||
|
|
|
|||
|
|
answerButtons[i].interactable = true;
|
|||
|
|
answerButtons[i].transition = Selectable.Transition.None;
|
|||
|
|
answerButtons[i].transform.localRotation = Quaternion.identity;
|
|||
|
|
|
|||
|
|
Image img = answerButtons[i].image;
|
|||
|
|
|
|||
|
|
if (img != null)
|
|||
|
|
img.color = FullAlpha(normalButtonColor);
|
|||
|
|
|
|||
|
|
answerButtons[i].transform.localScale = originalButtonScales[i];
|
|||
|
|
answerButtons[i].transform.localPosition = originalButtonPositions[i];
|
|||
|
|
|
|||
|
|
CanvasGroup cg = answerButtons[i].GetComponent<CanvasGroup>();
|
|||
|
|
|
|||
|
|
if (cg != null)
|
|||
|
|
{
|
|||
|
|
cg.alpha = 1f;
|
|||
|
|
cg.interactable = true;
|
|||
|
|
cg.blocksRaycasts = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (feedbackText != null)
|
|||
|
|
feedbackText.text = "";
|
|||
|
|
|
|||
|
|
if (submitButton != null)
|
|||
|
|
submitButton.interactable = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void SelectAnswer(int index)
|
|||
|
|
{
|
|||
|
|
if (gameOver || waitingNextQuestion)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (index < 0 || index >= answerButtons.Length)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (answerButtons[index] == null)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (!answerButtons[index].interactable)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (selectedAnswer == index)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (selectedAnswer >= 0)
|
|||
|
|
{
|
|||
|
|
Image oldImg = answerButtons[selectedAnswer].image;
|
|||
|
|
|
|||
|
|
if (oldImg != null)
|
|||
|
|
oldImg.color = FullAlpha(normalButtonColor);
|
|||
|
|
|
|||
|
|
StartScaleAnim(selectedAnswer, originalButtonScales[selectedAnswer]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
selectedAnswer = index;
|
|||
|
|
|
|||
|
|
Image img = answerButtons[index].image;
|
|||
|
|
|
|||
|
|
if (img != null)
|
|||
|
|
img.color = FullAlpha(selectedButtonColor);
|
|||
|
|
|
|||
|
|
StartSelectPopAnim(index);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void OnSubmit()
|
|||
|
|
{
|
|||
|
|
if (gameOver || waitingNextQuestion)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (selectedAnswer == -1)
|
|||
|
|
{
|
|||
|
|
if (feedbackText != null)
|
|||
|
|
{
|
|||
|
|
feedbackText.color = FullAlpha(wrongButtonColor);
|
|||
|
|
feedbackText.text = "CHOOSE AN ANSWER";
|
|||
|
|
StartCoroutine(FeedbackAnim(feedbackText));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
waitingNextQuestion = true;
|
|||
|
|
|
|||
|
|
if (submitButton != null)
|
|||
|
|
submitButton.interactable = false;
|
|||
|
|
|
|||
|
|
LockAnswerButtons();
|
|||
|
|
|
|||
|
|
if (selectedAnswer == correctAnswerIndex)
|
|||
|
|
HandleCorrectAnswer();
|
|||
|
|
else
|
|||
|
|
HandleWrongAnswer();
|
|||
|
|
|
|||
|
|
UpdateScore();
|
|||
|
|
UpdateCombo();
|
|||
|
|
|
|||
|
|
StartCoroutine(DelayNext());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void LockAnswerButtons()
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < answerButtons.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (answerButtons[i] == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
answerButtons[i].interactable = false;
|
|||
|
|
|
|||
|
|
if (i != selectedAnswer && i != correctAnswerIndex)
|
|||
|
|
{
|
|||
|
|
Image img = answerButtons[i].image;
|
|||
|
|
|
|||
|
|
if (img != null)
|
|||
|
|
img.color = FullAlpha(normalButtonColor);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void HandleCorrectAnswer()
|
|||
|
|
{
|
|||
|
|
Image img = answerButtons[selectedAnswer].image;
|
|||
|
|
|
|||
|
|
if (img != null)
|
|||
|
|
img.color = FullAlpha(correctButtonColor);
|
|||
|
|
|
|||
|
|
StartCoroutine(CorrectPulse(answerButtons[selectedAnswer], selectedAnswer));
|
|||
|
|
|
|||
|
|
combo++;
|
|||
|
|
correctCount++;
|
|||
|
|
|
|||
|
|
if (combo > bestComboThisRun)
|
|||
|
|
bestComboThisRun = combo;
|
|||
|
|
|
|||
|
|
score += Mathf.RoundToInt(basePoints * GetComboMultiplier(combo));
|
|||
|
|
timeLeft += correctTimeBonus;
|
|||
|
|
|
|||
|
|
if (feedbackText != null)
|
|||
|
|
{
|
|||
|
|
feedbackText.color = FullAlpha(correctButtonColor);
|
|||
|
|
feedbackText.text = combo >= 3 ? "CORRECT " + combo + "x COMBO!" : "CORRECT";
|
|||
|
|
StartCoroutine(FeedbackAnim(feedbackText));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (comboText != null)
|
|||
|
|
StartCoroutine(ComboAnim(comboText));
|
|||
|
|
|
|||
|
|
if (scoreText != null)
|
|||
|
|
StartCoroutine(ScorePopAnim(scoreText));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void HandleWrongAnswer()
|
|||
|
|
{
|
|||
|
|
Image wrongImg = answerButtons[selectedAnswer].image;
|
|||
|
|
|
|||
|
|
if (wrongImg != null)
|
|||
|
|
wrongImg.color = FullAlpha(wrongButtonColor);
|
|||
|
|
|
|||
|
|
StartCoroutine(ShakeButton(answerButtons[selectedAnswer], selectedAnswer));
|
|||
|
|
|
|||
|
|
if (correctAnswerIndex >= 0 && correctAnswerIndex < answerButtons.Length)
|
|||
|
|
{
|
|||
|
|
Image correctImg = answerButtons[correctAnswerIndex].image;
|
|||
|
|
|
|||
|
|
if (correctImg != null)
|
|||
|
|
correctImg.color = FullAlpha(correctButtonColor);
|
|||
|
|
|
|||
|
|
StartCoroutine(CorrectReveal(answerButtons[correctAnswerIndex], correctAnswerIndex));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wrongCount++;
|
|||
|
|
combo = 0;
|
|||
|
|
|
|||
|
|
score -= wrongPenalty;
|
|||
|
|
|
|||
|
|
if (score < 0)
|
|||
|
|
score = 0;
|
|||
|
|
|
|||
|
|
if (feedbackText != null)
|
|||
|
|
{
|
|||
|
|
feedbackText.color = FullAlpha(wrongButtonColor);
|
|||
|
|
|
|||
|
|
if (correctAnswerIndex >= 0 && correctAnswerIndex < answerTexts.Length && answerTexts[correctAnswerIndex] != null)
|
|||
|
|
feedbackText.text = "WRONG\nCorrect: " + answerTexts[correctAnswerIndex].text;
|
|||
|
|
else
|
|||
|
|
feedbackText.text = "WRONG";
|
|||
|
|
|
|||
|
|
StartCoroutine(FeedbackAnim(feedbackText));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator DelayNext()
|
|||
|
|
{
|
|||
|
|
yield return new WaitForSeconds(nextQuestionDelay);
|
|||
|
|
|
|||
|
|
if (!gameOver)
|
|||
|
|
NextQuestion();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void OnExtraTime()
|
|||
|
|
{
|
|||
|
|
if (!extraTimeReady || gameOver)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
timeLeft += extraTimeAmount;
|
|||
|
|
extraTimeReady = false;
|
|||
|
|
|
|||
|
|
if (extraTimeButton != null)
|
|||
|
|
extraTimeButton.interactable = false;
|
|||
|
|
|
|||
|
|
StartCoroutine(Cooldown(extraTimeCooldownText, extraTimeButton, () => extraTimeReady = true));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void OnExtraHint()
|
|||
|
|
{
|
|||
|
|
if (!extraHintReady || gameOver || waitingNextQuestion)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
int removed = 0;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < answerButtons.Length && removed < 2; i++)
|
|||
|
|
{
|
|||
|
|
if (i == correctAnswerIndex)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
if (selectedAnswer >= 0 && i == selectedAnswer)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
if (answerButtons[i] == null)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
if (!answerButtons[i].interactable)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
answerButtons[i].interactable = false;
|
|||
|
|
|
|||
|
|
Image img = answerButtons[i].image;
|
|||
|
|
|
|||
|
|
if (img != null)
|
|||
|
|
img.color = FullAlpha(disabledButtonColor);
|
|||
|
|
|
|||
|
|
if (answerTexts[i] != null)
|
|||
|
|
answerTexts[i].text = "—";
|
|||
|
|
|
|||
|
|
StartCoroutine(FadeOutButton(answerButtons[i]));
|
|||
|
|
|
|||
|
|
removed++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
extraHintReady = false;
|
|||
|
|
|
|||
|
|
if (extraHintButton != null)
|
|||
|
|
extraHintButton.interactable = false;
|
|||
|
|
|
|||
|
|
StartCoroutine(Cooldown(extraHintCooldownText, extraHintButton, () => extraHintReady = true));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void OnExtraSkip()
|
|||
|
|
{
|
|||
|
|
if (!extraSkipReady || gameOver || waitingNextQuestion)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
NextQuestion();
|
|||
|
|
|
|||
|
|
extraSkipReady = false;
|
|||
|
|
|
|||
|
|
if (extraSkipButton != null)
|
|||
|
|
extraSkipButton.interactable = false;
|
|||
|
|
|
|||
|
|
StartCoroutine(Cooldown(extraSkipCooldownText, extraSkipButton, () => extraSkipReady = true));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator Cooldown(TMP_Text label, Button button, System.Action done)
|
|||
|
|
{
|
|||
|
|
float t = cooldownDuration;
|
|||
|
|
|
|||
|
|
if (label != null)
|
|||
|
|
label.gameObject.SetActive(true);
|
|||
|
|
|
|||
|
|
while (t > 0f)
|
|||
|
|
{
|
|||
|
|
if (label != null)
|
|||
|
|
label.text = Mathf.CeilToInt(t).ToString();
|
|||
|
|
|
|||
|
|
t -= Time.deltaTime;
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (label != null)
|
|||
|
|
{
|
|||
|
|
label.text = "";
|
|||
|
|
label.gameObject.SetActive(false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (button != null)
|
|||
|
|
button.interactable = true;
|
|||
|
|
|
|||
|
|
done?.Invoke();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void GameOver()
|
|||
|
|
{
|
|||
|
|
if (gameOver)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
gameOver = true;
|
|||
|
|
|
|||
|
|
PlayerPrefs.SetString("LastPlayedMod", modKey);
|
|||
|
|
|
|||
|
|
PlayerPrefs.SetInt("Mod13Skor", score);
|
|||
|
|
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_LastScore", score);
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_Correct", correctCount);
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_Wrong", wrongCount);
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_LastCombo", combo);
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_BestComboRun", bestComboThisRun);
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_LastTimeLeft", Mathf.CeilToInt(timeLeft));
|
|||
|
|
|
|||
|
|
int totalAnswers = correctCount + wrongCount;
|
|||
|
|
int accuracy = totalAnswers > 0 ? Mathf.RoundToInt((correctCount / (float)totalAnswers) * 100f) : 0;
|
|||
|
|
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_LastAccuracy", accuracy);
|
|||
|
|
|
|||
|
|
if (score > PlayerPrefs.GetInt(modKey + "_MaxScore", 0))
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_MaxScore", score);
|
|||
|
|
|
|||
|
|
if (bestComboThisRun > PlayerPrefs.GetInt(modKey + "_BestCombo", 0))
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_BestCombo", bestComboThisRun);
|
|||
|
|
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_TotalCorrect", PlayerPrefs.GetInt(modKey + "_TotalCorrect", 0) + correctCount);
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_TotalWrong", PlayerPrefs.GetInt(modKey + "_TotalWrong", 0) + wrongCount);
|
|||
|
|
PlayerPrefs.SetInt(modKey + "_PlayCount", PlayerPrefs.GetInt(modKey + "_PlayCount", 0) + 1);
|
|||
|
|
|
|||
|
|
PlayerPrefs.SetInt("TotalScore", PlayerPrefs.GetInt("TotalScore", 0) + score);
|
|||
|
|
|
|||
|
|
int prevBest = PlayerPrefs.GetInt("Mod13Score", 0);
|
|||
|
|
|
|||
|
|
if (score > prevBest)
|
|||
|
|
PlayerPrefs.SetInt("Mod13Score", score);
|
|||
|
|
|
|||
|
|
PlayerPrefs.SetInt("Mod13LastScore", score);
|
|||
|
|
PlayerPrefs.SetInt("Mod13Correct", correctCount);
|
|||
|
|
PlayerPrefs.SetInt("Mod13Wrong", wrongCount);
|
|||
|
|
|
|||
|
|
PlayerPrefs.Save();
|
|||
|
|
|
|||
|
|
SceneManager.LoadScene(resultSceneName);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void UpdateScore()
|
|||
|
|
{
|
|||
|
|
if (scoreText != null)
|
|||
|
|
scoreText.text = score.ToString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void UpdateCombo()
|
|||
|
|
{
|
|||
|
|
if (comboText != null)
|
|||
|
|
comboText.text = combo + "x";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void UpdateTimerUI()
|
|||
|
|
{
|
|||
|
|
if (timerText == null)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
timerText.text = Mathf.CeilToInt(timeLeft).ToString();
|
|||
|
|
|
|||
|
|
if (timeLeft <= 10f)
|
|||
|
|
{
|
|||
|
|
timerText.color = FullAlpha(Color.Lerp(timerWarnColor, Color.white, Mathf.PingPong(Time.time * 3f, 1f)));
|
|||
|
|
timerText.transform.localScale = Vector3.one * (1f + Mathf.Sin(Time.time * 10f) * 0.05f);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
timerText.color = FullAlpha(timerNormalColor);
|
|||
|
|
timerText.transform.localScale = Vector3.one;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float GetComboMultiplier(int c)
|
|||
|
|
{
|
|||
|
|
if (c >= 8)
|
|||
|
|
return mult_8plus;
|
|||
|
|
|
|||
|
|
if (c >= 5)
|
|||
|
|
return mult_5to7;
|
|||
|
|
|
|||
|
|
if (c >= 3)
|
|||
|
|
return mult_3to4;
|
|||
|
|
|
|||
|
|
return mult_1to2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void StartScaleAnim(int index, Vector3 targetScale)
|
|||
|
|
{
|
|||
|
|
if (index < 0 || index >= answerButtons.Length)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (answerButtons[index] == null)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (buttonScaleCoroutines[index] != null)
|
|||
|
|
StopCoroutine(buttonScaleCoroutines[index]);
|
|||
|
|
|
|||
|
|
buttonScaleCoroutines[index] = StartCoroutine(ScaleButton(answerButtons[index], targetScale));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void StartSelectPopAnim(int index)
|
|||
|
|
{
|
|||
|
|
if (index < 0 || index >= answerButtons.Length)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (answerButtons[index] == null)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
if (buttonScaleCoroutines[index] != null)
|
|||
|
|
StopCoroutine(buttonScaleCoroutines[index]);
|
|||
|
|
|
|||
|
|
buttonScaleCoroutines[index] = StartCoroutine(SelectPop(answerButtons[index], index));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void StopButtonCoroutines(int index)
|
|||
|
|
{
|
|||
|
|
if (buttonScaleCoroutines != null && index < buttonScaleCoroutines.Length && buttonScaleCoroutines[index] != null)
|
|||
|
|
{
|
|||
|
|
StopCoroutine(buttonScaleCoroutines[index]);
|
|||
|
|
buttonScaleCoroutines[index] = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (buttonEnterCoroutines != null && index < buttonEnterCoroutines.Length && buttonEnterCoroutines[index] != null)
|
|||
|
|
{
|
|||
|
|
StopCoroutine(buttonEnterCoroutines[index]);
|
|||
|
|
buttonEnterCoroutines[index] = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator ScaleButton(Button btn, Vector3 targetScale)
|
|||
|
|
{
|
|||
|
|
Vector3 startScale = btn.transform.localScale;
|
|||
|
|
float t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / Mathf.Max(0.01f, selectAnimTime);
|
|||
|
|
btn.transform.localScale = Vector3.Lerp(startScale, targetScale, EaseOutCubic(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
btn.transform.localScale = targetScale;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator SelectPop(Button btn, int idx)
|
|||
|
|
{
|
|||
|
|
Vector3 baseScale = originalButtonScales[idx];
|
|||
|
|
Vector3 target = baseScale * selectedScale;
|
|||
|
|
Vector3 pop = baseScale * 1.06f;
|
|||
|
|
|
|||
|
|
Vector3 start = btn.transform.localScale;
|
|||
|
|
float t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.09f;
|
|||
|
|
btn.transform.localScale = Vector3.Lerp(start, pop, EaseOutCubic(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.10f;
|
|||
|
|
btn.transform.localScale = Vector3.Lerp(pop, target, EaseOutBack(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
btn.transform.localScale = target;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator ButtonEnterAnim(Button btn, int idx)
|
|||
|
|
{
|
|||
|
|
CanvasGroup cg = btn.GetComponent<CanvasGroup>();
|
|||
|
|
|
|||
|
|
Vector3 targetPos = originalButtonPositions[idx];
|
|||
|
|
Vector3 startPos = targetPos + Vector3.up * 45f;
|
|||
|
|
|
|||
|
|
Vector3 targetScale = originalButtonScales[idx];
|
|||
|
|
Vector3 startScale = targetScale * 0.80f;
|
|||
|
|
|
|||
|
|
btn.transform.localPosition = startPos;
|
|||
|
|
btn.transform.localScale = startScale;
|
|||
|
|
|
|||
|
|
if (cg != null)
|
|||
|
|
cg.alpha = 0f;
|
|||
|
|
|
|||
|
|
yield return new WaitForSeconds(idx * enterStagger);
|
|||
|
|
|
|||
|
|
float t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / Mathf.Max(0.01f, enterAnimTime);
|
|||
|
|
|
|||
|
|
float easePos = EaseOutBack(t);
|
|||
|
|
float easeFade = EaseOutCubic(Mathf.Clamp01(t * 1.4f));
|
|||
|
|
|
|||
|
|
btn.transform.localPosition = Vector3.Lerp(startPos, targetPos, easePos);
|
|||
|
|
btn.transform.localScale = Vector3.Lerp(startScale, targetScale, easePos);
|
|||
|
|
|
|||
|
|
if (cg != null)
|
|||
|
|
cg.alpha = easeFade;
|
|||
|
|
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
btn.transform.localPosition = targetPos;
|
|||
|
|
btn.transform.localScale = targetScale;
|
|||
|
|
|
|||
|
|
if (cg != null)
|
|||
|
|
cg.alpha = 1f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator CorrectPulse(Button btn, int idx)
|
|||
|
|
{
|
|||
|
|
Vector3 normal = originalButtonScales[idx];
|
|||
|
|
Vector3 big = normal * 1.16f;
|
|||
|
|
Vector3 mid = normal * 1.05f;
|
|||
|
|
|
|||
|
|
float t = 0f;
|
|||
|
|
Vector3 start = btn.transform.localScale;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.11f;
|
|||
|
|
btn.transform.localScale = Vector3.Lerp(start, big, EaseOutBack(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.10f;
|
|||
|
|
btn.transform.localScale = Vector3.Lerp(big, mid, EaseOutCubic(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.10f;
|
|||
|
|
btn.transform.localScale = Vector3.Lerp(mid, normal, EaseOutCubic(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
btn.transform.localScale = normal;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator CorrectReveal(Button btn, int idx)
|
|||
|
|
{
|
|||
|
|
Vector3 normal = originalButtonScales[idx];
|
|||
|
|
Vector3 big = normal * 1.10f;
|
|||
|
|
|
|||
|
|
float t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.18f;
|
|||
|
|
btn.transform.localScale = Vector3.Lerp(normal, big, EaseOutBack(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.16f;
|
|||
|
|
btn.transform.localScale = Vector3.Lerp(big, normal, EaseOutCubic(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
btn.transform.localScale = normal;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator ShakeButton(Button btn, int idx)
|
|||
|
|
{
|
|||
|
|
Vector3 origin = originalButtonPositions[idx];
|
|||
|
|
Quaternion originRot = btn.transform.localRotation;
|
|||
|
|
|
|||
|
|
float duration = 0.40f;
|
|||
|
|
float magnitude = 16f;
|
|||
|
|
float elapsed = 0f;
|
|||
|
|
|
|||
|
|
while (elapsed < duration)
|
|||
|
|
{
|
|||
|
|
float power = 1f - (elapsed / duration);
|
|||
|
|
power *= power;
|
|||
|
|
|
|||
|
|
float wave = Mathf.Sin(elapsed * 55f);
|
|||
|
|
float x = wave * magnitude * power;
|
|||
|
|
float rot = wave * 4f * power;
|
|||
|
|
|
|||
|
|
btn.transform.localPosition = origin + new Vector3(x, 0f, 0f);
|
|||
|
|
btn.transform.localRotation = Quaternion.Euler(0f, 0f, rot);
|
|||
|
|
|
|||
|
|
elapsed += Time.deltaTime;
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
btn.transform.localPosition = origin;
|
|||
|
|
btn.transform.localRotation = originRot;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator FadeOutButton(Button btn)
|
|||
|
|
{
|
|||
|
|
CanvasGroup cg = btn.GetComponent<CanvasGroup>();
|
|||
|
|
|
|||
|
|
if (cg == null)
|
|||
|
|
yield break;
|
|||
|
|
|
|||
|
|
float start = cg.alpha;
|
|||
|
|
float t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.20f;
|
|||
|
|
cg.alpha = Mathf.Lerp(start, 0.35f, EaseOutCubic(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
cg.alpha = 0.35f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator QuestionEnterAnim(TMP_Text txt)
|
|||
|
|
{
|
|||
|
|
Color c = FullAlpha(txt.color);
|
|||
|
|
|
|||
|
|
Vector3 baseScale = Vector3.one;
|
|||
|
|
Vector3 startScale = baseScale * 0.85f;
|
|||
|
|
|
|||
|
|
c.a = 0f;
|
|||
|
|
txt.color = c;
|
|||
|
|
txt.transform.localScale = startScale;
|
|||
|
|
|
|||
|
|
float t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.22f;
|
|||
|
|
c.a = EaseOutCubic(t);
|
|||
|
|
txt.color = c;
|
|||
|
|
txt.transform.localScale = Vector3.Lerp(startScale, baseScale, EaseOutBack(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
c.a = 1f;
|
|||
|
|
txt.color = c;
|
|||
|
|
txt.transform.localScale = baseScale;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator FeedbackAnim(TMP_Text txt)
|
|||
|
|
{
|
|||
|
|
Vector3 originalScale = Vector3.one;
|
|||
|
|
Vector3 smallScale = originalScale * 0.8f;
|
|||
|
|
Vector3 bigScale = originalScale * 1.20f;
|
|||
|
|
|
|||
|
|
Color c = FullAlpha(txt.color);
|
|||
|
|
|
|||
|
|
c.a = 0f;
|
|||
|
|
txt.color = c;
|
|||
|
|
txt.transform.localScale = smallScale;
|
|||
|
|
|
|||
|
|
float t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.16f;
|
|||
|
|
|
|||
|
|
txt.transform.localScale = Vector3.Lerp(smallScale, bigScale, EaseOutBack(t));
|
|||
|
|
|
|||
|
|
c.a = Mathf.Clamp01(t * 1.5f);
|
|||
|
|
txt.color = c;
|
|||
|
|
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
c.a = 1f;
|
|||
|
|
txt.color = c;
|
|||
|
|
|
|||
|
|
t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.12f;
|
|||
|
|
txt.transform.localScale = Vector3.Lerp(bigScale, originalScale, EaseOutCubic(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
txt.transform.localScale = originalScale;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator ComboAnim(TMP_Text txt)
|
|||
|
|
{
|
|||
|
|
Vector3 original = Vector3.one;
|
|||
|
|
Vector3 big = original * 1.25f;
|
|||
|
|
|
|||
|
|
float t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.12f;
|
|||
|
|
txt.transform.localScale = Vector3.Lerp(original, big, EaseOutBack(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.14f;
|
|||
|
|
txt.transform.localScale = Vector3.Lerp(big, original, EaseOutCubic(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
txt.transform.localScale = original;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
IEnumerator ScorePopAnim(TMP_Text txt)
|
|||
|
|
{
|
|||
|
|
Vector3 original = Vector3.one;
|
|||
|
|
Vector3 big = original * 1.18f;
|
|||
|
|
|
|||
|
|
float t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.10f;
|
|||
|
|
txt.transform.localScale = Vector3.Lerp(original, big, EaseOutBack(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
t = 0f;
|
|||
|
|
|
|||
|
|
while (t < 1f)
|
|||
|
|
{
|
|||
|
|
t += Time.deltaTime / 0.12f;
|
|||
|
|
txt.transform.localScale = Vector3.Lerp(big, original, EaseOutCubic(t));
|
|||
|
|
yield return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
txt.transform.localScale = original;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Color FullAlpha(Color c)
|
|||
|
|
{
|
|||
|
|
c.a = 1f;
|
|||
|
|
return c;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Shuffle<T>(List<T> list)
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < list.Count; i++)
|
|||
|
|
{
|
|||
|
|
int rnd = Random.Range(i, list.Count);
|
|||
|
|
|
|||
|
|
T temp = list[i];
|
|||
|
|
list[i] = list[rnd];
|
|||
|
|
list[rnd] = temp;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float EaseOutBack(float t)
|
|||
|
|
{
|
|||
|
|
t = Mathf.Clamp01(t);
|
|||
|
|
|
|||
|
|
float c1 = 1.70158f;
|
|||
|
|
float c3 = c1 + 1f;
|
|||
|
|
|
|||
|
|
return 1f + c3 * Mathf.Pow(t - 1f, 3f) + c1 * Mathf.Pow(t - 1f, 2f);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
float EaseOutCubic(float t)
|
|||
|
|
{
|
|||
|
|
t = Mathf.Clamp01(t);
|
|||
|
|
return 1f - Mathf.Pow(1f - t, 3f);
|
|||
|
|
}
|
|||
|
|
}
|