using UnityEngine; using UnityEngine.UI; using TMPro; using System.IO; using System.Collections; using System.Collections.Generic; using UnityEngine.SceneManagement; [RequireComponent(typeof(AudioSource))] public class Quiz : MonoBehaviour { // ======================================================== // 1. REFERENSI UI // ======================================================== [Header("UI Teks Gameplay")] public GameObject panelGameplay; public TextMeshProUGUI textSoal; public TextMeshProUGUI textNomorSoal; public TextMeshProUGUI[] textPilihan; [Header("UI Timer")] public TextMeshProUGUI textTimer; public Image imageFillTimer; [Header("UI Gambar Tombol")] public Image[] imageOval; public Image[] imageHuruf; public RectTransform[] tombolRects; [Header("UI Hasil")] public GameObject panelHasil; public GameObject[] bintangEmas; public TextMeshProUGUI textSkorAkhir; public TextMeshProUGUI textJudul; [Header("Settings")] public string namaFile = "soal_unsur"; public float waktuPerSoal = 20f; public int jumlahSoalPerSesi = 10; // JUMLAH SOAL YANG AKAN DIMAINKAN DARI BANK SOAL [Header("Warna Feedback Jawaban")] public Color warnaBenar = new Color(0f, 1f, 0f, 0.9f); public Color warnaSalah = new Color(1f, 0f, 0f, 0.9f); [Header("Audio Effects")] public AudioClip sfxBenar; public AudioClip sfxSalah; public AudioClip sfxBintangBiasa; public AudioClip sfxBintangSempurna; // ======================================================== // 2. VARIABEL LOGIKA // ======================================================== private QuizCollection dataKuis; private int currentSoal = 0; private int score = 0; private bool isAnswering = false; private AudioSource audioSource; private float sisaWaktu; private float totalWaktuAwal; private bool kuisBerjalan = false; // ======================================================== // 3. FUNGSI UTAMA // ======================================================== void Start() { Random.InitState((int)System.DateTime.Now.Ticks); audioSource = GetComponent(); // Mengasumsikan ada script Audio.instance untuk musik latar if (Audio.instance != null) Audio.instance.PlayQuizMusic(); if(panelGameplay) panelGameplay.SetActive(true); if(panelHasil) panelHasil.SetActive(false); LoadSoal(); TampilSoal(); } void Update() { if (kuisBerjalan) { UpdateTimer(); } } // ======================================================== // 4. SISTEM TIMER & BANK SOAL (RANDOMIZER) // ======================================================== void MulaiTimer() { if (dataKuis != null && dataKuis.daftarSoal.Count > 0) { // Menghitung total waktu berdasarkan jumlah soal yang sudah difilter totalWaktuAwal = dataKuis.daftarSoal.Count * waktuPerSoal; sisaWaktu = totalWaktuAwal; kuisBerjalan = true; } } void UpdateTimer() { if (sisaWaktu > 0) { sisaWaktu -= Time.deltaTime; int menit = Mathf.FloorToInt(sisaWaktu / 60); int detik = Mathf.FloorToInt(sisaWaktu % 60); if (textTimer) textTimer.text = string.Format("{0:00}:{1:00}", menit, detik); if (imageFillTimer) imageFillTimer.fillAmount = sisaWaktu / totalWaktuAwal; if (sisaWaktu < 5f && textTimer) textTimer.color = Color.red; } else { sisaWaktu = 0; kuisBerjalan = false; SelesaiKuis(); } } // Fungsi baru untuk mengacak seluruh bank soal void AcakSemuaSoal(List listSoal) { for (int i = 0; i < listSoal.Count; i++) { QuestionData temp = listSoal[i]; int randomIndex = Random.Range(i, listSoal.Count); listSoal[i] = listSoal[randomIndex]; listSoal[randomIndex] = temp; } } void LoadSoal() { string namaTarget = namaFile; if (!namaTarget.EndsWith(".json")) namaTarget += ".json"; string targetPath = Path.Combine(Application.persistentDataPath, namaTarget); // --- BARIS PEMBERSIH (Tambahkan ini) --- // Gunakan ini sekali saja untuk menghapus file lama di memori HP // Jika sudah berhasil, baris ini bisa dihapus atau di-comment // if (File.Exists(targetPath)) { // File.Delete(targetPath); // Debug.Log("File lama di HP berhasil dihapus. Sekarang menggunakan file baru dari Resources."); // } // --------------------------------------- QuizCollection fullData = null; // Mencoba memuat dari storage internal (Update) atau Resources (Bawaan) if (File.Exists(targetPath)) { string json = File.ReadAllText(targetPath); fullData = JsonUtility.FromJson(json); } else { TextAsset fileBawaan = Resources.Load(namaFile.Replace(".json", "")); if (fileBawaan != null) fullData = JsonUtility.FromJson(fileBawaan.text); } if (fullData != null && fullData.daftarSoal != null) { // LOGIKA BANK SOAL // 1. Acak seluruh urutan soal dalam koleksi AcakSemuaSoal(fullData.daftarSoal); // 2. Tentukan berapa banyak soal yang diambil (tidak melebihi total soal yang ada) int jumlahAmbil = Mathf.Min(jumlahSoalPerSesi, fullData.daftarSoal.Count); // 3. Masukkan subset soal yang sudah diacak ke data kuis utama dataKuis = new QuizCollection(); dataKuis.daftarSoal = fullData.daftarSoal.GetRange(0, jumlahAmbil); MulaiTimer(); } } // ======================================================== // 5. LOGIKA GAMEPLAY & FEEDBACK // ======================================================== void TampilSoal() { if (dataKuis != null && currentSoal < dataKuis.daftarSoal.Count) { isAnswering = false; QuestionData q = dataKuis.daftarSoal[currentSoal]; textSoal.text = q.pertanyaan; if(textNomorSoal) textNomorSoal.text = "SOAL " + (currentSoal + 1); for(int i=0; i<4; i++) { textPilihan[i].text = q.pilihanJawaban[i]; imageOval[i].color = Color.white; imageHuruf[i].color = Color.white; tombolRects[i].anchoredPosition = new Vector2(0, tombolRects[i].anchoredPosition.y); } } else { kuisBerjalan = false; SelesaiKuis(); } } public void Jawab(int indexPilihan) { if (isAnswering || !kuisBerjalan) return; isAnswering = true; StartCoroutine(CekJawaban(indexPilihan)); } IEnumerator CekJawaban(int indexDipilih) { int kunci = dataKuis.daftarSoal[currentSoal].kunciJawaban; if (indexDipilih == kunci) { score++; MainkanSuara(sfxBenar); StartCoroutine(AnimasiKedip(indexDipilih)); } else { MainkanSuara(sfxSalah); StartCoroutine(AnimasiGetar(tombolRects[indexDipilih])); imageOval[indexDipilih].color = warnaSalah; imageHuruf[indexDipilih].color = warnaSalah; StartCoroutine(AnimasiKedip(kunci)); } yield return new WaitForSeconds(0.8f); currentSoal++; TampilSoal(); } void MainkanSuara(AudioClip clip) { if (PlayerPrefs.GetInt("SFXState", 1) == 1 && clip != null) audioSource.PlayOneShot(clip); } IEnumerator AnimasiGetar(RectTransform target) { Vector2 originalPos = target.anchoredPosition; float timer = 0, duration = 0.3f, strength = 10f; while (timer < duration) { timer += Time.deltaTime; target.anchoredPosition = originalPos + new Vector2(Mathf.Sin(timer * 50f) * strength, 0); yield return null; } target.anchoredPosition = originalPos; } IEnumerator AnimasiKedip(int index) { float timer = 0, duration = 0.6f; while(timer < duration) { timer += Time.deltaTime; Color c = (timer % 0.2f < 0.1f) ? warnaBenar : Color.Lerp(warnaBenar, Color.white, 0.5f); imageOval[index].color = imageHuruf[index].color = c; yield return null; } imageOval[index].color = imageHuruf[index].color = warnaBenar; } // ======================================================== // 6. LOGIKA HASIL // ======================================================== void SelesaiKuis() { kuisBerjalan = false; if(panelGameplay) panelGameplay.SetActive(false); panelHasil.SetActive(true); float nilaiAkhir = (dataKuis.daftarSoal.Count > 0) ? ((float)score / dataKuis.daftarSoal.Count) * 100f : 0; textSkorAkhir.text = "SKOR: " + Mathf.RoundToInt(nilaiAkhir); foreach(var b in bintangEmas) { b.SetActive(false); b.transform.localScale = Vector3.zero; } int jumlahBintang = (nilaiAkhir == 100) ? 3 : (nilaiAkhir >= 60) ? 2 : 1; textJudul.text = (nilaiAkhir == 100) ? "SEMPURNA!" : (nilaiAkhir >= 60) ? "HEBAT!" : "COBA LAGI!"; StartCoroutine(AnimasiSequence(jumlahBintang)); } IEnumerator AnimasiSequence(int jumlah) { yield return new WaitForSeconds(0.3f); for (int i = 0; i < jumlah; i++) { bintangEmas[i].SetActive(true); MainkanSuara(i == 2 && jumlah == 3 ? sfxBintangSempurna : sfxBintangBiasa); float timer = 0, durasiPop = 0.4f; while(timer < durasiPop) { timer += Time.deltaTime; bintangEmas[i].transform.localScale = Vector3.one * (1.0f - Mathf.Pow(1.0f - (timer/durasiPop), 2)); yield return null; } bintangEmas[i].transform.localScale = Vector3.one; yield return new WaitForSeconds(0.15f); } } public void MainLagi() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); } public void KeMenu() { if (Audio.instance != null) Audio.instance.PlayMainMusic(); SceneManager.LoadScene("PilihKuis"); } // === FITUR ONLINE (DENGAN LOGIKA BANK SOAL) === public void SetSoalOnline(QuizCollection dataBaru) { // 1. Acak bank soal dari database online AcakSemuaSoal(dataBaru.daftarSoal); // 2. Ambil sejumlah soal yang ditentukan saja int jumlahAmbil = Mathf.Min(jumlahSoalPerSesi, dataBaru.daftarSoal.Count); dataKuis = new QuizCollection(); dataKuis.daftarSoal = dataBaru.daftarSoal.GetRange(0, jumlahAmbil); currentSoal = 0; score = 0; isAnswering = false; MulaiTimer(); // Update timer berdasarkan jumlah soal baru if(panelGameplay) panelGameplay.SetActive(true); if(panelHasil) panelHasil.SetActive(false); TampilSoal(); } }