388 lines
12 KiB
C#
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"); }
|
|
} |