327 lines
10 KiB
C#
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();
|
|
}
|
|
} |