464 lines
15 KiB
C#
464 lines
15 KiB
C#
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<DataSayur> 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<RectTransform>());
|
|
}
|
|
else if (panelMateri.activeSelf) {
|
|
PutarSuara(listSayur[indexMateri]);
|
|
PlayShake(imgSayurMateri.GetComponent<RectTransform>());
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
} |