MIF_E31230837/Assets/Script/QuizSystem.cs

327 lines
10 KiB
C#

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<BabJSON> bab;
}
[System.Serializable]
public class BabJSON
{
public string nama;
public List<SoalJSON> 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<SoalData> listSoalMain = new List<SoalData>();
private int indexSoalSekarang = 0;
private int totalSkor = 0;
private Dictionary<string, int> benarPerBab = new Dictionary<string, int>();
private Dictionary<string, int> totalPerBab = new Dictionary<string, int>();
void Start()
{
audioSource = gameObject.AddComponent<AudioSource>();
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<string, List<SoalData>> tempBab = new Dictionary<string, List<SoalData>>();
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<SoalData>();
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<TextAsset>(namaFileJSON);
if (fileTeks == null) { Debug.LogError("File JSON tidak ditemukan!"); return; }
KoleksiSoalJSON data = JsonUtility.FromJson<KoleksiSoalJSON>(fileTeks.text);
Dictionary<string, List<SoalData>> tempBab = new Dictionary<string, List<SoalData>>();
foreach (var b in data.bab)
{
if (!tempBab.ContainsKey(b.nama)) tempBab[b.nama] = new List<SoalData>();
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<string, List<SoalData>> bahan)
{
listSoalMain.Clear();
for (int i = 0; i < daftarNamaBab.Length; i++)
{
string target = daftarNamaBab[i];
if (!bahan.ContainsKey(target)) continue;
List<SoalData> 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<Image>().color = Color.white;
btnJawaban[i].GetComponentInChildren<TextMeshProUGUI>().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<Image>().color = Color.green;
if (suaraBenar != null) audioSource.PlayOneShot(suaraBenar);
}
else
{
btnJawaban[k].GetComponent<Image>().color = Color.red;
btnJawaban[s.jawabanBenar].GetComponent<Image>().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<string> hasil = new List<string>();
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();
}
}