using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.Networking; using UnityEngine.SceneManagement; using TMPro; [System.Serializable] public class SoalData { public string bab; public string pertanyaan; public string[] pilihan; public int jawabanBenar; } [System.Serializable] public class KoleksiSoalJSON { public List bab; } [System.Serializable] public class BabJSON { public string nama; public List soal; } [System.Serializable] public class SoalJSON { public string pertanyaan; public string[] pilihan; public int jawaban; } public class QuizSystem : MonoBehaviour { [Header("UI Dasar")] public TextMeshProUGUI txtSoal; public Button[] btnJawaban; public GameObject panelHasil; public TextMeshProUGUI txtSkor; public TextMeshProUGUI txtRekap; [Header("Panel")] public GameObject panelLoading; public GameObject panelOffline; [Header("Audio")] public AudioClip suaraBenar; public AudioClip suaraSalah; private AudioSource audioSource; [Header("Nama Scene Main Menu")] public string namaSceneMainMenu = "Main Menu"; [Header("Konfigurasi")] public string csvURL = ""; public string namaFileJSON = "soalkuis"; public int[] jumlahSoalPerBab = { 3, 3, 2, 2 }; private readonly string[] daftarNamaBab = { "Struktur Bumi", "Pergerakan Lempeng", "Gempa Bumi", "Gunung Berapi" }; private List listSoalMain = new List(); private int indexSoalSekarang = 0; private int totalSkor = 0; private Dictionary benarPerBab = new Dictionary(); private Dictionary totalPerBab = new Dictionary(); void Start() { audioSource = gameObject.AddComponent(); panelHasil.SetActive(false); panelOffline.SetActive(false); CobaSambungOnline(); } // ─────────────────────────────────────────── // LOGIKA KONEKSI // ─────────────────────────────────────────── void CobaSambungOnline() { panelOffline.SetActive(false); panelLoading.SetActive(true); StartCoroutine(DownloadCSV()); } IEnumerator DownloadCSV() { using (UnityWebRequest webRequest = UnityWebRequest.Get(csvURL)) { webRequest.timeout = 10; yield return webRequest.SendWebRequest(); panelLoading.SetActive(false); if (webRequest.result != UnityWebRequest.Result.Success) { Debug.LogWarning("Gagal konek: " + webRequest.error); panelOffline.SetActive(true); } else { ParseCSV(webRequest.downloadHandler.text); MulaiGame(); } } } // ─────────────────────────────────────────── // TOMBOL PANEL OFFLINE // ─────────────────────────────────────────── public void KlikCobaLagi() { CobaSambungOnline(); } public void KlikMainOffline() { panelOffline.SetActive(false); MuatDataLokal(); } // ─────────────────────────────────────────── // PARSE & MUAT DATA // ─────────────────────────────────────────── void ParseCSV(string konten) { Dictionary> tempBab = new Dictionary>(); string[] baris = konten.Split(new string[] { "\r\n", "\n" }, System.StringSplitOptions.RemoveEmptyEntries); for (int i = 1; i < baris.Length; i++) { string[] kolom = PecahKolomCSV(baris[i]); if (kolom.Length < 7) continue; string namaBab = kolom[0].Trim(); if (int.TryParse(kolom[6].Trim(), out int indexJawaban)) { if (!tempBab.ContainsKey(namaBab)) tempBab[namaBab] = new List(); tempBab[namaBab].Add(new SoalData { bab = namaBab, pertanyaan = kolom[1].Trim(), pilihan = new string[] { kolom[2].Trim(), kolom[3].Trim(), kolom[4].Trim(), kolom[5].Trim() }, jawabanBenar = indexJawaban }); } } ProsesAcakSoal(tempBab); } void MuatDataLokal() { TextAsset fileTeks = Resources.Load(namaFileJSON); if (fileTeks == null) { Debug.LogError("File JSON tidak ditemukan!"); return; } KoleksiSoalJSON data = JsonUtility.FromJson(fileTeks.text); Dictionary> tempBab = new Dictionary>(); foreach (var b in data.bab) { if (!tempBab.ContainsKey(b.nama)) tempBab[b.nama] = new List(); foreach (var s in b.soal) tempBab[b.nama].Add(new SoalData { bab = b.nama, pertanyaan = s.pertanyaan, pilihan = s.pilihan, jawabanBenar = s.jawaban }); } ProsesAcakSoal(tempBab); MulaiGame(); } void ProsesAcakSoal(Dictionary> bahan) { listSoalMain.Clear(); for (int i = 0; i < daftarNamaBab.Length; i++) { string target = daftarNamaBab[i]; if (!bahan.ContainsKey(target)) continue; List pool = bahan[target]; for (int t = 0; t < pool.Count; t++) { SoalData tmp = pool[t]; int r = Random.Range(t, pool.Count); pool[t] = pool[r]; pool[r] = tmp; } int jatah = (i < jumlahSoalPerBab.Length) ? jumlahSoalPerBab[i] : 1; for (int j = 0; j < Mathf.Min(jatah, pool.Count); j++) listSoalMain.Add(pool[j]); } } // ─────────────────────────────────────────── // GAME LOGIC // ─────────────────────────────────────────── void MulaiGame() { if (listSoalMain.Count == 0) { Debug.LogError("Soal Kosong!"); return; } indexSoalSekarang = 0; totalSkor = 0; benarPerBab.Clear(); totalPerBab.Clear(); foreach (var s in listSoalMain) { if (!totalPerBab.ContainsKey(s.bab)) { totalPerBab[s.bab] = 0; benarPerBab[s.bab] = 0; } totalPerBab[s.bab]++; } TampilkanSoal(); } void TampilkanSoal() { if (indexSoalSekarang >= listSoalMain.Count) { TampilHasil(); return; } SoalData s = listSoalMain[indexSoalSekarang]; txtSoal.text = s.pertanyaan; for (int i = 0; i < btnJawaban.Length; i++) { btnJawaban[i].interactable = true; btnJawaban[i].GetComponent().color = Color.white; btnJawaban[i].GetComponentInChildren().text = s.pilihan[i]; int idx = i; btnJawaban[i].onClick.RemoveAllListeners(); btnJawaban[i].onClick.AddListener(() => Jawab(idx)); } } void Jawab(int k) { SoalData s = listSoalMain[indexSoalSekarang]; if (k == s.jawabanBenar) { totalSkor += 10; benarPerBab[s.bab]++; btnJawaban[k].GetComponent().color = Color.green; if (suaraBenar != null) audioSource.PlayOneShot(suaraBenar); } else { btnJawaban[k].GetComponent().color = Color.red; btnJawaban[s.jawabanBenar].GetComponent().color = Color.green; if (suaraSalah != null) audioSource.PlayOneShot(suaraSalah); } foreach (var b in btnJawaban) b.interactable = false; indexSoalSekarang++; Invoke("TampilkanSoal", 1.5f); } void TampilHasil() { panelHasil.SetActive(true); txtSkor.text = "Skor: " + totalSkor; string terlemah = ""; float nilaiMin = 1.1f; foreach (var sb in totalPerBab) { float rasio = (float)benarPerBab[sb.Key] / sb.Value; if (rasio < nilaiMin) { nilaiMin = rasio; terlemah = sb.Key; } } if (txtRekap != null) txtRekap.text = terlemah != "" && nilaiMin < 1f ? "Pelajari lagi:\n" + terlemah : "Semua sub bab sudah bagus!"; } // ─────────────────────────────────────────── // TOMBOL PANEL HASIL // ─────────────────────────────────────────── public void KlikUlangi() { panelHasil.SetActive(false); CobaSambungOnline(); } public void KlikKembali() { SceneManager.LoadScene(namaSceneMainMenu); } // ─────────────────────────────────────────── // HELPER // ─────────────────────────────────────────── string[] PecahKolomCSV(string baris) { List hasil = new List(); bool dalamKutip = false; string temp = ""; foreach (char c in baris) { if (c == '"') dalamKutip = !dalamKutip; else if (c == ',' && !dalamKutip) { hasil.Add(temp); temp = ""; } else temp += c; } hasil.Add(temp); return hasil.ToArray(); } }