using UnityEngine; using UnityEngine.UI; using System.Collections; // WAJIB ADA untuk IEnumerator (Animasi) using System.Collections.Generic; using TMPro; using UnityEngine.SceneManagement; public class GameManager : MonoBehaviour { public static GameManager instance; [Header("Sistem Chapter")] public List semuaChapter; private int indexChapterSekarang = 0; [Header("Sistem Skor & Progress")] public Slider sliderSkor; public TextMeshProUGUI textSkor; public Image bintang1, bintang2, bintang3; public Sprite bintangNyala; private float skorSekarangVisual = 0; private int skorTarget = 0; [Header("Sistem Timer")] public TextMeshProUGUI textTimer; public float durasiMenit = 1f; private float waktuTersisa; private bool gameAktif = false; [Header("Prefab & Area")] public GameObject prefabItem; public GameObject prefabTarget; public Transform areaTarget; public Transform areaPilihan; [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; // Board Utama (Asset 38) public GameObject latarHitam; // Panel Hitam Transparan public TextMeshProUGUI textNilaiAkhir; public Image hasilBintang1, hasilBintang2, hasilBintang3; private int jumlahTerselesaikan = 0; void Awake() { instance = this; } void Start() { skorTarget = 0; skorSekarangVisual = 0; if(sliderSkor != null) { sliderSkor.maxValue = 100; sliderSkor.value = 0; } UpdateVisualSkor(); waktuTersisa = durasiMenit * 60f; gameAktif = true; indexChapterSekarang = 0; if(popupWahKeren != null) popupWahKeren.SetActive(false); if(popupSalah != null) popupSalah.SetActive(false); if(panelBoardHasil != null) panelBoardHasil.SetActive(false); if(latarHitam != null) latarHitam.SetActive(false); MulaiChapter(0); } void Update() { if (gameAktif && waktuTersisa > 0) { waktuTersisa -= Time.deltaTime; UpdateDisplayTimer(); } else if (gameAktif && waktuTersisa <= 0) { MunculkanBoardAkhir(); } if (sliderSkor != null && skorSekarangVisual < skorTarget) { skorSekarangVisual = Mathf.MoveTowards(skorSekarangVisual, skorTarget, 50f * Time.deltaTime); sliderSkor.value = skorSekarangVisual; CheckBintangNyala(skorSekarangVisual, bintang1, bintang2, bintang3); } } public void MunculkanBoardAkhir() { if (!gameAktif) return; gameAktif = false; // --- TAMBAHKAN KODE SIMPAN DISINI --- float durasiMain = 0; TimeTracker tt = FindObjectOfType(); // Cari stopwatch if (tt != null) durasiMain = tt.GetDurasiBermain(); // Ambil detiknya if (SaveSystem.instance != null) { // Simpan: Index 2 (Cocokkan), Skor yang didapat, Durasi waktu SaveSystem.instance.SimpanHasilGame(2, skorTarget, durasiMain); } // ------------------------------------ waktuTersisa = 0; textTimer.text = "00:00"; 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(); // 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()) hasilBintang1.GetComponent().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()) hasilBintang2.GetComponent().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()) hasilBintang3.GetComponent().enabled = true; // Kembalikan pitch ke normal setelah selesai if(audio != null) audio.pitch = 1f; } } void UpdateVisualSkor() { if(textSkor != null) textSkor.text = skorTarget.ToString(); } 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; } 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 MulaiChapter(int index) { indexChapterSekarang = index; jumlahTerselesaikan = 0; foreach (Transform child in areaTarget) Destroy(child.gameObject); foreach (Transform child in areaPilihan) Destroy(child.gameObject); LevelData chapterAktif = semuaChapter[indexChapterSekarang]; // 1. MUNCULKAN SILUET (TARGET) foreach (VegetableData data in chapterAktif.sayurDiChapterIni) { GameObject s = Instantiate(prefabTarget, areaTarget); s.GetComponent().vData = data; // Pastikan kita ambil RectTransform dari siluet di dalam Slot if (s.transform.childCount > 0) { RectTransform siluetRT = s.transform.GetChild(0).GetComponent(); if (siluetRT != null) { // PAKSA ukuran sesuai data siluetRT.sizeDelta = data.ukuranCustom; } } } // 2. KOCOK DAN MUNCULKAN PILIHAN SAYUR (BAWAH) List sayurAcak = new List(chapterAktif.sayurDiChapterIni); // ... (Logika Shuffle kamu tetap sama di sini) ... for (int i = 0; i < sayurAcak.Count; i++) { VegetableData temp = sayurAcak[i]; int randomIndex = Random.Range(i, sayurAcak.Count); sayurAcak[i] = sayurAcak[randomIndex]; sayurAcak[randomIndex] = temp; } foreach (VegetableData dataPilihan in sayurAcak) { GameObject itm = Instantiate(prefabItem, areaPilihan); itm.GetComponent().vData = dataPilihan; RectTransform itmRT = itm.GetComponent(); if (itmRT != null) { // PAKSA ukuran pilihan sesuai data itmRT.sizeDelta = dataPilihan.ukuranCustom; } } // --- RAHASIA AGAR KONSISTEN --- // Memaksa Unity menghitung ulang layout seketika itu juga LayoutRebuilder.ForceRebuildLayoutImmediate(areaTarget.GetComponent()); LayoutRebuilder.ForceRebuildLayoutImmediate(areaPilihan.GetComponent()); } // Ganti fungsi TambahSkor yang lama dengan ini public void TambahSkor() { jumlahTerselesaikan++; LevelData chapterAktif = semuaChapter[indexChapterSekarang]; if (jumlahTerselesaikan >= chapterAktif.sayurDiChapterIni.Count) { // Kita panggil Coroutine supaya bisa dikasih jeda waktu StartCoroutine(JedaMunculPopup()); } } IEnumerator JedaMunculPopup() { // 1. Kasih jeda (misal 0.8 detik) biar sayur terakhir nempel dulu dengan jelas yield return new WaitForSeconds(1.5f); // 2. Tambah skor target (20 poin per chapter) skorTarget += 20; UpdateVisualSkor(); // 3. Munculkan popup "Wah Keren" if(popupWahKeren != null) popupWahKeren.SetActive(true); // 4. Jeda lagi sebelum pindah chapter (2 detik) yield return new WaitForSeconds(1.5f); SembunyikanPopupDanLanjut(); } void SembunyikanPopupDanLanjut() { if(popupWahKeren != null) popupWahKeren.SetActive(false); if (indexChapterSekarang < semuaChapter.Count - 1) { MulaiChapter(indexChapterSekarang + 1); } else { MunculkanBoardAkhir(); } } public void MunculkanWrong() { if(popupSalah != null) { popupSalah.SetActive(true); Invoke("SembunyikanWrong", 1f); } } void SembunyikanWrong() { if(popupSalah != null) popupSalah.SetActive(false); } // --- FUNGSI SUARA (REVISI) --- public void PutarSuaraKlik() { AudioSource audio = GetComponent(); if (audio != null) audio.Play(); } // Di dalam class GameManager public void PutarVO(AudioClip klip) { AudioSource audio = GetComponent(); if (audio != null && klip != null) { StartCoroutine(ProsesPutarVO(audio, klip)); } } IEnumerator ProsesPutarVO(AudioSource audio, AudioClip klip) { // 1. Ambil CanvasGroup dari ChoiceArea untuk mematikan interaksi CanvasGroup cgArea = areaPilihan.GetComponent(); // Jika belum ada CanvasGroup di Inspector areaPilihan, kodenya akan error. // Jadi pastikan kamu sudah Add Component 'Canvas Group' di objek ChoiceArea. if (cgArea != null) cgArea.blocksRaycasts = false; audio.PlayOneShot(klip); // 2. Tunggu sampai VO selesai yield return new WaitForSeconds(klip.length); // 3. Nyalakan kembali interaksinya if (cgArea != null) cgArea.blocksRaycasts = true; } public void TombolReplay() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); } public void TombolKembaliMenu() { SceneManager.LoadScene("MenuBermain"); } }