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