using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine.SceneManagement; public class QuizManager : MonoBehaviour { public static QuizManager instance; [Header("Daftar Soal")] public List daftarSoal; private int soalSekarangIndex = 0; [Header("UI Layout Kiri")] public GameObject panelKiri; public Image imageSayurKiri; public TextMeshProUGUI textSoalKiri; public TextMeshProUGUI[] textTombolKiri; [Header("UI Layout Tengah")] public GameObject panelTengah; public TextMeshProUGUI textSoalTengah; public Image[] imageTombolTengah; [Header("Warna Feedback")] public Color warnaBenar = Color.green; public Color warnaSalah = Color.red; public Color warnaNormal = Color.white; [Header("Animasi & Feedback")] public string namaTriggerShake = "Shake"; [Header("Sistem Skor & Progress")] public Slider sliderSkor; public TextMeshProUGUI textSkor; public Image bintang1, bintang2, bintang3; public Sprite bintangNyala; private float skorVisual = 0; private int skorTarget = 0; [Header("Sistem Timer")] public TextMeshProUGUI textTimer; public float durasiMenit = 2f; private float waktuTersisa; private bool gameAktif = false; [Header("Sistem Suara")] public AudioClip suaraBintang; // Tarik file suara bintang ke sini di Inspector [Header("UI PopUp & Board Hasil Akhir")] public GameObject popupWahKeren; public GameObject popupSalah; public GameObject panelBoardHasil; public GameObject latarHitam; public TextMeshProUGUI textNilaiAkhir; public Image hasilBintang1, hasilBintang2, hasilBintang3; void Awake() { instance = this; } void Start() { // 1. ACAK DAFTAR SOAL (Fisher-Yates Shuffle) // Dilakukan paling awal agar urutan soal berbeda setiap main AcakSoal(); // 2. RESET DATA PERMAINAN soalSekarangIndex = 0; skorTarget = 0; skorVisual = 0; waktuTersisa = durasiMenit * 60f; gameAktif = true; // 3. RESET UI & SLIDER if(sliderSkor != null) { sliderSkor.maxValue = 100; sliderSkor.value = 0; } // 4. PASTIKAN SEMUA POPUP MATI if(popupWahKeren != null) popupWahKeren.SetActive(false); if(popupSalah != null) popupSalah.SetActive(false); if(panelBoardHasil != null) panelBoardHasil.SetActive(false); if(latarHitam != null) latarHitam.SetActive(false); // 5. MULAI TAMPILKAN SOAL PERTAMA TampilkanSoal(); } void Update() { if (!gameAktif) return; // Logika Timer if (waktuTersisa > 0) { waktuTersisa -= Time.deltaTime; UpdateDisplayTimer(); } else { MunculkanBoardAkhir(); } // Logika Animasi Skor & Progress Bintang if (skorVisual < skorTarget) { skorVisual = Mathf.MoveTowards(skorVisual, skorTarget, 50f * Time.deltaTime); if(sliderSkor != null) sliderSkor.value = skorVisual; if(textSkor != null) textSkor.text = Mathf.FloorToInt(skorVisual).ToString(); CheckBintangNyala(skorVisual, bintang1, bintang2, bintang3); } } // FUNGSI UNTUK MENGACAK LIST SOAL void AcakSoal() { for (int i = 0; i < daftarSoal.Count; i++) { QuizData temp = daftarSoal[i]; int randomIndex = Random.Range(i, daftarSoal.Count); daftarSoal[i] = daftarSoal[randomIndex]; daftarSoal[randomIndex] = temp; } } public void TampilkanSoal() { if (soalSekarangIndex >= daftarSoal.Count) { Invoke("MunculkanBoardAkhir", 0.5f); return; } QuizData data = daftarSoal[soalSekarangIndex]; ResetWarnaTombol(); if (data.jenisLayout == QuizData.LayoutType.LayoutKiri) { panelKiri.SetActive(true); panelTengah.SetActive(false); textSoalKiri.text = data.pertanyaan; imageSayurKiri.sprite = data.gambarUtama; for (int i = 0; i < textTombolKiri.Length; i++) { if (i < data.pilihanJawabanTeks.Length) textTombolKiri[i].text = data.pilihanJawabanTeks[i]; } } else { panelKiri.SetActive(false); panelTengah.SetActive(true); textSoalTengah.text = data.pertanyaan; for (int i = 0; i < imageTombolTengah.Length; i++) { if (i < data.pilihanJawabanGambar.Length) { imageTombolTengah[i].gameObject.SetActive(true); imageTombolTengah[i].sprite = data.pilihanJawabanGambar[i]; } else { imageTombolTengah[i].gameObject.SetActive(false); } } } } void ResetWarnaTombol() { foreach(var t in textTombolKiri) if(t != null) t.transform.parent.GetComponent().color = warnaNormal; foreach(var img in imageTombolTengah) if(img != null) img.transform.parent.GetComponent().color = warnaNormal; } public void PilihJawaban(int indexDipilih) { if (!gameAktif) return; int jawabanBenar = daftarSoal[soalSekarangIndex].indexJawabanBenar; GameObject tombolKlik = UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject; if (tombolKlik == null) return; Image imageTombolKlik = tombolKlik.GetComponent(); if (indexDipilih == jawabanBenar) { skorTarget += 10; StartCoroutine(EfekJawaban(imageTombolKlik, null, true)); } else { Image imageTombolBenar = null; if (daftarSoal[soalSekarangIndex].jenisLayout == QuizData.LayoutType.LayoutKiri) imageTombolBenar = textTombolKiri[jawabanBenar].transform.parent.GetComponent(); else imageTombolBenar = imageTombolTengah[jawabanBenar].transform.parent.GetComponent(); StartCoroutine(EfekJawaban(imageTombolKlik, imageTombolBenar, false)); } } IEnumerator EfekJawaban(Image imgKlik, Image imgBenar, bool isBenar) { gameAktif = false; // 1. Kasih warna dulu ke tombolnya if (isBenar) { if(imgKlik != null) imgKlik.color = warnaBenar; } else { if(imgKlik != null) { imgKlik.color = warnaSalah; StartCoroutine(Shake(imgKlik.rectTransform)); } if(imgBenar != null) imgBenar.color = warnaBenar; } // 2. NAH INI DIA! Kasih jeda sedikit sebelum pop-up muncul // Ganti 0.2f sesuai keinginan kamu (makin besar makin lama jedanya) yield return new WaitForSeconds(0.3f); // 3. Baru munculin pop-upnya if (isBenar) { if(popupWahKeren != null) popupWahKeren.SetActive(true); } else { if(popupSalah != null) popupSalah.SetActive(true); } // 4. Durasi berapa lama pop-up itu nongol di layar yield return new WaitForSeconds(1.5f); // 5. Bersihkan semuanya if(popupWahKeren != null) popupWahKeren.SetActive(false); if(popupSalah != null) popupSalah.SetActive(false); soalSekarangIndex++; TampilkanSoal(); gameAktif = true; } // FUNGSI GETAR MANUAL VIA SCRIPT IEnumerator Shake(RectTransform rt) { Vector3 posisiAsli = rt.anchoredPosition; float durasi = 0.4f; float kekuatan = 15f; float elapsed = 0f; while (elapsed < durasi) { float x = Random.Range(-1f, 1f) * kekuatan; rt.anchoredPosition = new Vector3(posisiAsli.x + x, posisiAsli.y, posisiAsli.z); elapsed += Time.deltaTime; yield return null; } rt.anchoredPosition = posisiAsli; } void UpdateDisplayTimer() { float menit = Mathf.FloorToInt(waktuTersisa / 60); float detik = Mathf.FloorToInt(waktuTersisa % 60); textTimer.text = string.Format("{0:00}:{1:00}", menit, detik); } void CheckBintangNyala(float skor, Image b1, Image b2, Image b3) { if (skor >= 40 && b1 != null) b1.sprite = bintangNyala; if (skor >= 80 && b2 != null) b2.sprite = bintangNyala; if (skor >= 100 && b3 != null) b3.sprite = bintangNyala; } public void MunculkanBoardAkhir() { // 1. Cek apakah game masih aktif agar data tidak tersimpan dua kali if (!gameAktif) return; gameAktif = false; // --- PROSES SIMPAN DATA KE DATABASE JSON --- float durasiMain = 0; TimeTracker tt = FindObjectOfType(); if (tt != null) durasiMain = tt.GetDurasiBermain(); if (SaveSystem.instance != null) { // Index 4 adalah khusus untuk Game Quiz SaveSystem.instance.SimpanHasilGame(4, skorTarget, durasiMain); } // -------------------------------------------- if(panelBoardHasil != null) { if(latarHitam != null) latarHitam.SetActive(true); panelBoardHasil.SetActive(true); StartCoroutine(AnimasiHasilAkhir()); } } IEnumerator AnimasiHasilAkhir() { float durasiAcak = 0.1f; float timerAcak = 0; // Ambil komponen AudioSource untuk memutar suara AudioSource audio = GetComponent(); // Efek angka acak sebelum skor asli muncul while (timerAcak < durasiAcak) { timerAcak += Time.deltaTime; int angkaTampil = Random.Range(0, skorTarget + 1); if(textNilaiAkhir != null) textNilaiAkhir.text = angkaTampil.ToString(); yield return new WaitForSeconds(0.05f); } if(textNilaiAkhir != null) textNilaiAkhir.text = skorTarget.ToString(); yield return new WaitForSeconds(0.3f); // --- PROSES MUNCUL BINTANG DENGAN SUARA --- // Bintang 1 (Skor minimal 40) if (skorTarget >= 40 && hasilBintang1 != null) { hasilBintang1.sprite = bintangNyala; // Mainkan Suara if(audio != null && suaraBintang != null) audio.PlayOneShot(suaraBintang); if(hasilBintang1.GetComponent()) hasilBintang1.GetComponent().enabled = true; yield return new WaitForSeconds(0.5f); // Jeda sebelum bintang berikutnya } // Bintang 2 (Skor minimal 80) if (skorTarget >= 80 && hasilBintang2 != null) { hasilBintang2.sprite = bintangNyala; // Mainkan Suara (Pitch dinaikkan dikit biar suaranya lebih tinggi/ceriam) if(audio != null && suaraBintang != null) { audio.pitch = 1.1f; audio.PlayOneShot(suaraBintang); } if(hasilBintang2.GetComponent()) hasilBintang2.GetComponent().enabled = true; yield return new WaitForSeconds(0.5f); } // Bintang 3 (Skor sempurna 100) if (skorTarget >= 100 && hasilBintang3 != null) { hasilBintang3.sprite = bintangNyala; // Mainkan Suara (Pitch dinaikkan lagi) if(audio != null && suaraBintang != null) { audio.pitch = 1.2f; audio.PlayOneShot(suaraBintang); } if(hasilBintang3.GetComponent()) hasilBintang3.GetComponent().enabled = true; // Kembalikan pitch ke normal setelah selesai if(audio != null) audio.pitch = 1f; } } public void TombolReplay() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); } public void TombolMenu() { SceneManager.LoadScene("MenuBermain"); } }