using UnityEngine; using UnityEngine.UI; using TMPro; using System.Collections; using System.Collections.Generic; public class BelajarManager : MonoBehaviour { // ... isi script kamu di bawahnya tetap biarkan saja [Header("Data Utama")] public List listSayur; [Header("Panel Switcher")] public GameObject panelBaris; public GameObject panelKolom; public GameObject panelMateri; [Header("Tombol Switcher UI (Image)")] public Image imgBtnBaris; public Image imgBtnKolom; public Image imgBtnMateri; [Header("Aset Gambar Tombol (Sprite)")] public Sprite btnBarisColor, btnBarisBW; public Sprite btnKolomColor, btnKolomBW; public Sprite btnMateriColor, btnMateriBW; [Header("Animasi Scale Tombol (Tab)")] public float scaleAktif = 1.15f; public float scaleMati = 1.0f; public float speedAnimasi = 10f; private Vector3 targetScaleBaris, targetScaleKolom, targetScaleMateri; [Header("UI Scene Baris")] public TextMeshProUGUI txtNama; public Image imgSayur; public TextMeshProUGUI txtInpo; // Slot untuk teks info kanan atas public TextMeshProUGUI txtBubble; // Slot untuk teks catatan di dalam bubble public Image imgBubbleMaskot; // Slot untuk UI Image kepala maskot public GameObject panelBubbleParent; // Slot untuk objek parent 'Bubble' (biar bisa di-pop up) private int indexSekarang = 0; [Header("UI Scene Kolom (Galeri)")] public KartuSayur[] slotKartu; private int halamanSekarang = 0; [Header("UI Scene Materi (Detail)")] public TextMeshProUGUI txtNamaMateri; public Image imgSayurMateri; public TextMeshProUGUI txtRasaMateri; public TextMeshProUGUI txtManfaatMateri; private int indexMateri = 0; [Header("Pengaturan Audio VO")] public AudioSource sourceVO; [Header("Pengaturan Efek Juiciness")] public float floatingSpeed = 2f; public float floatingAmount = 15f; private Vector3 posisiAwalSayurBaris; private Vector3 posisiAwalSayurMateri; [Header("Pengaturan Auto Next")] public Image imgBtnAuto; public Sprite btnAutoOn, btnAutoOff; public float jedaAuto = 3f; private bool isAutoActive = false; private Coroutine autoCoroutine; [Header("Pengaturan Efek Shake")] public float durasiShake = 0.2f; public float kekuatanShake = 5f; [Header("Pengaturan Ducking Musik")] public AudioSource sourceBGM; public float volumeNormal = 0.5f; public float volumeDucking = 0.1f; // --- BATAS VARIABEL ATAS --- void Start() { targetScaleBaris = targetScaleKolom = targetScaleMateri = Vector3.one * scaleMati; // Simpan posisi awal untuk efek melayang posisiAwalSayurBaris = imgSayur.transform.localPosition; posisiAwalSayurMateri = imgSayurMateri.transform.localPosition; BukaMenuBaris(); } void Update() { // Animasi Scale Tab Menu imgBtnBaris.transform.localScale = Vector3.Lerp(imgBtnBaris.transform.localScale, targetScaleBaris, Time.deltaTime * speedAnimasi); imgBtnKolom.transform.localScale = Vector3.Lerp(imgBtnKolom.transform.localScale, targetScaleKolom, Time.deltaTime * speedAnimasi); imgBtnMateri.transform.localScale = Vector3.Lerp(imgBtnMateri.transform.localScale, targetScaleMateri, Time.deltaTime * speedAnimasi); // --- EFEK MELAYANG (FLOATING) --- float sinWave = Mathf.Sin(Time.time * floatingSpeed) * floatingAmount; if(panelBaris.activeSelf) imgSayur.transform.localPosition = posisiAwalSayurBaris + new Vector3(0, sinWave, 0); if(panelMateri.activeSelf) imgSayurMateri.transform.localPosition = posisiAwalSayurMateri + new Vector3(0, sinWave, 0); } // --- FUNGSI SWITCHER PANEL --- public void BukaMenuBaris() { panelBaris.SetActive(true); panelKolom.SetActive(false); panelMateri.SetActive(false); imgBtnBaris.sprite = btnBarisColor; imgBtnKolom.sprite = btnKolomBW; imgBtnMateri.sprite = btnMateriBW; imgBtnBaris.transform.SetAsLastSibling(); targetScaleBaris = Vector3.one * scaleAktif; targetScaleKolom = Vector3.one * scaleMati; targetScaleMateri = Vector3.one * scaleMati; UpdateTampilan(); } public void BukaMenuKolom() { panelBaris.SetActive(false); panelKolom.SetActive(true); panelMateri.SetActive(false); imgBtnBaris.sprite = btnBarisBW; imgBtnKolom.sprite = btnKolomColor; imgBtnMateri.sprite = btnMateriBW; imgBtnKolom.transform.SetAsLastSibling(); targetScaleBaris = Vector3.one * scaleMati; targetScaleKolom = Vector3.one * scaleAktif; targetScaleMateri = Vector3.one * scaleMati; UpdateTampilanKolom(); } public void BukaMenuMateri() { panelBaris.SetActive(false); panelKolom.SetActive(false); panelMateri.SetActive(true); imgBtnBaris.sprite = btnBarisBW; imgBtnKolom.sprite = btnKolomBW; imgBtnMateri.sprite = btnMateriColor; imgBtnMateri.transform.SetAsLastSibling(); targetScaleBaris = Vector3.one * scaleMati; targetScaleKolom = Vector3.one * scaleMati; targetScaleMateri = Vector3.one * scaleAktif; } // --- LOGIKA SCENE BARIS --- public void TombolNext() { indexSekarang = (indexSekarang + 1) % listSayur.Count; UpdateTampilan(); } public void TombolPrev() { indexSekarang = (indexSekarang - 1 + listSayur.Count) % listSayur.Count; UpdateTampilan(); } void UpdateTampilan() { if(listSayur.Count > 0) { DataSayur sayurAktif = listSayur[indexSekarang]; // Setup data standar txtNama.text = sayurAktif.namaSayur; imgSayur.sprite = sayurAktif.gambarSayurVisual; // --- AKTIFKAN & ISI DATA INFO --- txtInpo.text = sayurAktif.infoStatusKonsumsi; // --- LOGIKA PENGECEKAN BUBBLE CHAT --- if (string.IsNullOrEmpty(sayurAktif.teksBubbleCatatan)) { // Sembunyikan bubble secara utuh jika datanya dikosongkan panelBubbleParent.SetActive(false); } else { panelBubbleParent.SetActive(true); txtBubble.text = sayurAktif.teksBubbleCatatan; // Ganti gambar ekspresi maskot jika disiapkan di asset if(sayurAktif.spriteEkspresiMaskot != null) { imgBubbleMaskot.sprite = sayurAktif.spriteEkspresiMaskot; } // Jalankan Efek Animasi Pop Up Khusus Bubble StopCoroutine("EfekPopUpBubble"); StartCoroutine(EfekPopUpBubble()); } // Putar VO suara PutarSuara(sayurAktif); // Animasi Pop Sayur Utama StopCoroutine("EfekPopSayur"); StartCoroutine(EfekPopSayur(imgSayur)); } } // --- LOGIKA SCENE KOLOM (PAGING 5 KARTU) --- public void TombolNextHalaman() { // Hitung total halaman yang dibutuhkan // Misal: 12 sayur / 5 = 2.4, dibulatkan ke atas jadi 3 halaman int totalHalaman = Mathf.CeilToInt((float)listSayur.Count / 5); halamanSekarang++; // JIKA sudah melewati halaman terakhir, BALIK ke halaman pertama (0) if (halamanSekarang >= totalHalaman) { halamanSekarang = 0; } UpdateTampilanKolom(); } public void TombolPrevHalaman() { int totalHalaman = Mathf.CeilToInt((float)listSayur.Count / 5); halamanSekarang--; // JIKA sudah mundur sebelum halaman pertama, LOMPAT ke halaman terakhir if (halamanSekarang < 0) { halamanSekarang = totalHalaman - 1; } UpdateTampilanKolom(); } void UpdateTampilanKolom() { int indexMulai = halamanSekarang * 5; for (int i = 0; i < slotKartu.Length; i++) { int indexSayurData = indexMulai + i; if (indexSayurData < listSayur.Count) { // Jika data ada, aktifkan kartu dan isi datanya slotKartu[i].gameObject.SetActive(true); slotKartu[i].Setup(listSayur[indexSayurData]); } else { // Jika data habis di halaman tersebut, sembunyikan kartu sisanya slotKartu[i].gameObject.SetActive(false); } } } public void AmbilDataDariKartu(DataSayur dataDiterima) { indexMateri = listSayur.IndexOf(dataDiterima); // Buka panelnya dulu BukaMenuMateri(); // Kasih jeda dikit biar panelnya bener-bener "bangun" sebelum keluar suara StartCoroutine(DelaySuaraMateri()); } IEnumerator DelaySuaraMateri() { yield return new WaitForSeconds(0.05f); // Jeda sangat singkat UpdateTampilanMateri(); } void UpdateTampilanMateri() { if (listSayur.Count > 0) { DataSayur s = listSayur[indexMateri]; txtNamaMateri.text = s.namaSayur; imgSayurMateri.sprite = s.gambarSayurVisual; txtRasaMateri.text = s.rasaSayur; txtManfaatMateri.text = s.manfaatSayur; // Pastikan ini ada di sini! PutarSuara(s); StopCoroutine("EfekPopSayur"); StartCoroutine(EfekPopSayur(imgSayurMateri)); } } public void NextMateri() { indexMateri = (indexMateri + 1) % listSayur.Count; UpdateTampilanMateri(); } public void PrevMateri() { indexMateri = (indexMateri - 1 + listSayur.Count) % listSayur.Count; UpdateTampilanMateri(); } // --- COROUTINE ANIMASI POP --- IEnumerator EfekPopSayur(Image targetImage) { targetImage.transform.localScale = Vector3.zero; float timer = 0; float durasi = 0.3f; while (timer < durasi) { timer += Time.deltaTime; float progress = timer / durasi; // Rumus bounce sederhana float curve = Mathf.Sin(progress * Mathf.PI * 0.8f) * 1.15f; targetImage.transform.localScale = new Vector3(curve, curve, curve); yield return null; } targetImage.transform.localScale = Vector3.one; } public void KlikTombolAuto() { isAutoActive = !isAutoActive; // Switch ON/OFF if (isAutoActive) { imgBtnAuto.sprite = btnAutoOn; // Mulai hitung mundur otomatis autoCoroutine = StartCoroutine(ProsesAutoNext()); } else { imgBtnAuto.sprite = btnAutoOff; // Hentikan hitung mundur if (autoCoroutine != null) StopCoroutine(autoCoroutine); } } IEnumerator ProsesAutoNext() { while (isAutoActive) { yield return new WaitForSeconds(jedaAuto); // Cek panel mana yang lagi aktif, lalu panggil fungsi Next-nya if (panelBaris.activeSelf) { TombolNext(); } else if (panelKolom.activeSelf) { TombolNextHalaman(); } else if (panelMateri.activeSelf) { NextMateri(); } } } void PutarSuara(DataSayur data) { // 1. Cek apakah datanya ada dan punya suara if (data == null || data.suaraNamaSayur == null) return; // 2. Hentikan suara lama dan mainkan yang baru sourceVO.Stop(); sourceVO.clip = data.suaraNamaSayur; sourceVO.Play(); // 3. Efek Ducking Musik (BGM mengecil) // Kita pastikan sourceBGM tidak kosong sebelum dijalankan if (sourceBGM != null) { StopCoroutine("ProsesDucking"); StartCoroutine(ProsesDucking(volumeDucking)); // 4. Kita buat coroutine baru untuk mengembalikan volume musik // setelah suara selesai (lebih akurat dari Invoke) StopCoroutine("TungguSuaraSelesai"); StartCoroutine(TungguSuaraSelesai(sourceVO.clip.length)); } } // --- FUNGSI FEEDBACK & SHAKE --- public void KlikGambarSayur() { if (panelBaris.activeSelf) { PutarSuara(listSayur[indexSekarang]); PlayShake(imgSayur.GetComponent()); } else if (panelMateri.activeSelf) { PutarSuara(listSayur[indexMateri]); PlayShake(imgSayurMateri.GetComponent()); } } public void PlayShake(RectTransform target) { StopCoroutine("ProsesShake"); StartCoroutine(ProsesShake(target)); } IEnumerator ProsesShake(RectTransform target) { Vector3 posisiAsli = target.localPosition; float timer = 0f; while (timer < durasiShake) { timer += Time.deltaTime; float x = Random.Range(-1f, 1f) * kekuatanShake; target.localPosition = new Vector3(posisiAsli.x + x, posisiAsli.y, posisiAsli.z); yield return null; } target.localPosition = posisiAsli; } // --- FUNGSI AUDIO PENDUKUNG --- IEnumerator ProsesDucking(float targetVolume) { if (sourceBGM == null) yield break; while (Mathf.Abs(sourceBGM.volume - targetVolume) > 0.01f) { sourceBGM.volume = Mathf.MoveTowards(sourceBGM.volume, targetVolume, Time.deltaTime * 2f); yield return null; } sourceBGM.volume = targetVolume; } void ResetMusik() { if (gameObject.activeInHierarchy) { StopCoroutine("ProsesDucking"); StartCoroutine(ProsesDucking(volumeNormal)); } } // Cukup tulis satu kali saja di sini IEnumerator TungguSuaraSelesai(float durasi) { yield return new WaitForSeconds(durasi); ResetMusik(); } IEnumerator EfekKedipCatatan() { if (txtBubble == null) yield break; // <-- Ganti jadi txtBubble while (panelBaris.activeSelf && txtBubble.gameObject.activeSelf) { // <-- Ganti jadi txtBubble float timer = 0; while (timer < 0.5f) { timer += Time.deltaTime; txtBubble.alpha = Mathf.Lerp(1f, 0.2f, timer / 0.5f); // <-- Ganti jadi txtBubble yield return null; } timer = 0; while (timer < 0.5f) { timer += Time.deltaTime; txtBubble.alpha = Mathf.Lerp(0.2f, 1f, timer / 0.5f); // <-- Ganti jadi txtBubble yield return null; } yield return new WaitForSeconds(0.3f); } } // --- COROUTINE ANIMASI POP UP BUBBLE CHAT --- IEnumerator EfekPopUpBubble() { // Set ukuran awal bubble jadi nol (tidak terlihat) panelBubbleParent.transform.localScale = Vector3.zero; float timer = 0; float durasi = 0.35f; while (timer < durasi) { timer += Time.deltaTime; float progress = timer / durasi; // Menggunakan rumus matematika Sinus untuk efek bounce (membal) yang kenyal float bounceCurve = Mathf.Sin(progress * Mathf.PI * 0.85f) * 1.1f; panelBubbleParent.transform.localScale = new Vector3(bounceCurve, bounceCurve, bounceCurve); yield return null; } // Kunci ukuran akhir ke normal (1,1,1) panelBubbleParent.transform.localScale = Vector3.one; } }