MIF_E31230838/Assets/Scripts/Quiz.cs

334 lines
11 KiB
C#

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<AudioSource>();
// 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<QuestionData> 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<QuizCollection>(json);
} else {
TextAsset fileBawaan = Resources.Load<TextAsset>(namaFile.Replace(".json", ""));
if (fileBawaan != null) fullData = JsonUtility.FromJson<QuizCollection>(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();
}
}