MIF_E31230979/Assets/Scripts/KuisSayur/QuizManager.cs

388 lines
12 KiB
C#

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine.SceneManagement;
public class QuizManager : MonoBehaviour
{
public static QuizManager instance;
[Header("Daftar Soal")]
public List<QuizData> daftarSoal;
private int soalSekarangIndex = 0;
[Header("UI Layout Kiri")]
public GameObject panelKiri;
public Image imageSayurKiri;
public TextMeshProUGUI textSoalKiri;
public TextMeshProUGUI[] textTombolKiri;
[Header("UI Layout Tengah")]
public GameObject panelTengah;
public TextMeshProUGUI textSoalTengah;
public Image[] imageTombolTengah;
[Header("Warna Feedback")]
public Color warnaBenar = Color.green;
public Color warnaSalah = Color.red;
public Color warnaNormal = Color.white;
[Header("Animasi & Feedback")]
public string namaTriggerShake = "Shake";
[Header("Sistem Skor & Progress")]
public Slider sliderSkor;
public TextMeshProUGUI textSkor;
public Image bintang1, bintang2, bintang3;
public Sprite bintangNyala;
private float skorVisual = 0;
private int skorTarget = 0;
[Header("Sistem Timer")]
public TextMeshProUGUI textTimer;
public float durasiMenit = 2f;
private float waktuTersisa;
private bool gameAktif = false;
[Header("Sistem Suara")]
public AudioClip suaraBintang; // Tarik file suara bintang ke sini di Inspector
[Header("UI PopUp & Board Hasil Akhir")]
public GameObject popupWahKeren;
public GameObject popupSalah;
public GameObject panelBoardHasil;
public GameObject latarHitam;
public TextMeshProUGUI textNilaiAkhir;
public Image hasilBintang1, hasilBintang2, hasilBintang3;
void Awake() { instance = this; }
void Start()
{
// 1. ACAK DAFTAR SOAL (Fisher-Yates Shuffle)
// Dilakukan paling awal agar urutan soal berbeda setiap main
AcakSoal();
// 2. RESET DATA PERMAINAN
soalSekarangIndex = 0;
skorTarget = 0;
skorVisual = 0;
waktuTersisa = durasiMenit * 60f;
gameAktif = true;
// 3. RESET UI & SLIDER
if(sliderSkor != null) {
sliderSkor.maxValue = 100;
sliderSkor.value = 0;
}
// 4. PASTIKAN SEMUA POPUP MATI
if(popupWahKeren != null) popupWahKeren.SetActive(false);
if(popupSalah != null) popupSalah.SetActive(false);
if(panelBoardHasil != null) panelBoardHasil.SetActive(false);
if(latarHitam != null) latarHitam.SetActive(false);
// 5. MULAI TAMPILKAN SOAL PERTAMA
TampilkanSoal();
}
void Update()
{
if (!gameAktif) return;
// Logika Timer
if (waktuTersisa > 0)
{
waktuTersisa -= Time.deltaTime;
UpdateDisplayTimer();
}
else
{
MunculkanBoardAkhir();
}
// Logika Animasi Skor & Progress Bintang
if (skorVisual < skorTarget)
{
skorVisual = Mathf.MoveTowards(skorVisual, skorTarget, 50f * Time.deltaTime);
if(sliderSkor != null) sliderSkor.value = skorVisual;
if(textSkor != null) textSkor.text = Mathf.FloorToInt(skorVisual).ToString();
CheckBintangNyala(skorVisual, bintang1, bintang2, bintang3);
}
}
// FUNGSI UNTUK MENGACAK LIST SOAL
void AcakSoal()
{
for (int i = 0; i < daftarSoal.Count; i++)
{
QuizData temp = daftarSoal[i];
int randomIndex = Random.Range(i, daftarSoal.Count);
daftarSoal[i] = daftarSoal[randomIndex];
daftarSoal[randomIndex] = temp;
}
}
public void TampilkanSoal()
{
if (soalSekarangIndex >= daftarSoal.Count)
{
Invoke("MunculkanBoardAkhir", 0.5f);
return;
}
QuizData data = daftarSoal[soalSekarangIndex];
ResetWarnaTombol();
if (data.jenisLayout == QuizData.LayoutType.LayoutKiri)
{
panelKiri.SetActive(true);
panelTengah.SetActive(false);
textSoalKiri.text = data.pertanyaan;
imageSayurKiri.sprite = data.gambarUtama;
for (int i = 0; i < textTombolKiri.Length; i++)
{
if (i < data.pilihanJawabanTeks.Length)
textTombolKiri[i].text = data.pilihanJawabanTeks[i];
}
}
else
{
panelKiri.SetActive(false);
panelTengah.SetActive(true);
textSoalTengah.text = data.pertanyaan;
for (int i = 0; i < imageTombolTengah.Length; i++)
{
if (i < data.pilihanJawabanGambar.Length)
{
imageTombolTengah[i].gameObject.SetActive(true);
imageTombolTengah[i].sprite = data.pilihanJawabanGambar[i];
}
else
{
imageTombolTengah[i].gameObject.SetActive(false);
}
}
}
}
void ResetWarnaTombol()
{
foreach(var t in textTombolKiri)
if(t != null) t.transform.parent.GetComponent<Image>().color = warnaNormal;
foreach(var img in imageTombolTengah)
if(img != null) img.transform.parent.GetComponent<Image>().color = warnaNormal;
}
public void PilihJawaban(int indexDipilih)
{
if (!gameAktif) return;
int jawabanBenar = daftarSoal[soalSekarangIndex].indexJawabanBenar;
GameObject tombolKlik = UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject;
if (tombolKlik == null) return;
Image imageTombolKlik = tombolKlik.GetComponent<Image>();
if (indexDipilih == jawabanBenar)
{
skorTarget += 10;
StartCoroutine(EfekJawaban(imageTombolKlik, null, true));
}
else
{
Image imageTombolBenar = null;
if (daftarSoal[soalSekarangIndex].jenisLayout == QuizData.LayoutType.LayoutKiri)
imageTombolBenar = textTombolKiri[jawabanBenar].transform.parent.GetComponent<Image>();
else
imageTombolBenar = imageTombolTengah[jawabanBenar].transform.parent.GetComponent<Image>();
StartCoroutine(EfekJawaban(imageTombolKlik, imageTombolBenar, false));
}
}
IEnumerator EfekJawaban(Image imgKlik, Image imgBenar, bool isBenar)
{
gameAktif = false;
// 1. Kasih warna dulu ke tombolnya
if (isBenar)
{
if(imgKlik != null) imgKlik.color = warnaBenar;
}
else
{
if(imgKlik != null)
{
imgKlik.color = warnaSalah;
StartCoroutine(Shake(imgKlik.rectTransform));
}
if(imgBenar != null) imgBenar.color = warnaBenar;
}
// 2. NAH INI DIA! Kasih jeda sedikit sebelum pop-up muncul
// Ganti 0.2f sesuai keinginan kamu (makin besar makin lama jedanya)
yield return new WaitForSeconds(0.3f);
// 3. Baru munculin pop-upnya
if (isBenar)
{
if(popupWahKeren != null) popupWahKeren.SetActive(true);
}
else
{
if(popupSalah != null) popupSalah.SetActive(true);
}
// 4. Durasi berapa lama pop-up itu nongol di layar
yield return new WaitForSeconds(1.5f);
// 5. Bersihkan semuanya
if(popupWahKeren != null) popupWahKeren.SetActive(false);
if(popupSalah != null) popupSalah.SetActive(false);
soalSekarangIndex++;
TampilkanSoal();
gameAktif = true;
}
// FUNGSI GETAR MANUAL VIA SCRIPT
IEnumerator Shake(RectTransform rt)
{
Vector3 posisiAsli = rt.anchoredPosition;
float durasi = 0.4f;
float kekuatan = 15f;
float elapsed = 0f;
while (elapsed < durasi)
{
float x = Random.Range(-1f, 1f) * kekuatan;
rt.anchoredPosition = new Vector3(posisiAsli.x + x, posisiAsli.y, posisiAsli.z);
elapsed += Time.deltaTime;
yield return null;
}
rt.anchoredPosition = posisiAsli;
}
void UpdateDisplayTimer()
{
float menit = Mathf.FloorToInt(waktuTersisa / 60);
float detik = Mathf.FloorToInt(waktuTersisa % 60);
textTimer.text = string.Format("{0:00}:{1:00}", menit, detik);
}
void CheckBintangNyala(float skor, Image b1, Image b2, Image b3)
{
if (skor >= 40 && b1 != null) b1.sprite = bintangNyala;
if (skor >= 80 && b2 != null) b2.sprite = bintangNyala;
if (skor >= 100 && b3 != null) b3.sprite = bintangNyala;
}
public void MunculkanBoardAkhir()
{
// 1. Cek apakah game masih aktif agar data tidak tersimpan dua kali
if (!gameAktif) return;
gameAktif = false;
// --- PROSES SIMPAN DATA KE DATABASE JSON ---
float durasiMain = 0;
TimeTracker tt = FindObjectOfType<TimeTracker>();
if (tt != null) durasiMain = tt.GetDurasiBermain();
if (SaveSystem.instance != null)
{
// Index 4 adalah khusus untuk Game Quiz
SaveSystem.instance.SimpanHasilGame(4, skorTarget, durasiMain);
}
// --------------------------------------------
if(panelBoardHasil != null)
{
if(latarHitam != null) latarHitam.SetActive(true);
panelBoardHasil.SetActive(true);
StartCoroutine(AnimasiHasilAkhir());
}
}
IEnumerator AnimasiHasilAkhir()
{
float durasiAcak = 0.1f;
float timerAcak = 0;
// Ambil komponen AudioSource untuk memutar suara
AudioSource audio = GetComponent<AudioSource>();
// Efek angka acak sebelum skor asli muncul
while (timerAcak < durasiAcak)
{
timerAcak += Time.deltaTime;
int angkaTampil = Random.Range(0, skorTarget + 1);
if(textNilaiAkhir != null) textNilaiAkhir.text = angkaTampil.ToString();
yield return new WaitForSeconds(0.05f);
}
if(textNilaiAkhir != null) textNilaiAkhir.text = skorTarget.ToString();
yield return new WaitForSeconds(0.3f);
// --- PROSES MUNCUL BINTANG DENGAN SUARA ---
// Bintang 1 (Skor minimal 40)
if (skorTarget >= 40 && hasilBintang1 != null) {
hasilBintang1.sprite = bintangNyala;
// Mainkan Suara
if(audio != null && suaraBintang != null) audio.PlayOneShot(suaraBintang);
if(hasilBintang1.GetComponent<EfekPopKenyal>())
hasilBintang1.GetComponent<EfekPopKenyal>().enabled = true;
yield return new WaitForSeconds(0.5f); // Jeda sebelum bintang berikutnya
}
// Bintang 2 (Skor minimal 80)
if (skorTarget >= 80 && hasilBintang2 != null) {
hasilBintang2.sprite = bintangNyala;
// Mainkan Suara (Pitch dinaikkan dikit biar suaranya lebih tinggi/ceriam)
if(audio != null && suaraBintang != null) {
audio.pitch = 1.1f;
audio.PlayOneShot(suaraBintang);
}
if(hasilBintang2.GetComponent<EfekPopKenyal>())
hasilBintang2.GetComponent<EfekPopKenyal>().enabled = true;
yield return new WaitForSeconds(0.5f);
}
// Bintang 3 (Skor sempurna 100)
if (skorTarget >= 100 && hasilBintang3 != null) {
hasilBintang3.sprite = bintangNyala;
// Mainkan Suara (Pitch dinaikkan lagi)
if(audio != null && suaraBintang != null) {
audio.pitch = 1.2f;
audio.PlayOneShot(suaraBintang);
}
if(hasilBintang3.GetComponent<EfekPopKenyal>())
hasilBintang3.GetComponent<EfekPopKenyal>().enabled = true;
// Kembalikan pitch ke normal setelah selesai
if(audio != null) audio.pitch = 1f;
}
}
public void TombolReplay() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); }
public void TombolMenu() { SceneManager.LoadScene("MenuBermain"); }
}