using System.Collections; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using TMPro; public class MenuCeritaDuaDialogController : MonoBehaviour { [Header("Dialog Atas")] public Image imageAvatarAtas; public TMP_Text textNamaAtas; public TMP_Text textDialogAtas; public CanvasGroup canvasDialogAtas; public RectTransform rectDialogAtas; [Header("Dialog Bawah")] public Image imageAvatarBawah; public TMP_Text textNamaBawah; public TMP_Text textDialogBawah; public CanvasGroup canvasDialogBawah; public RectTransform rectDialogBawah; [Header("Tombol")] public Button buttonSebelumnya; public Button buttonSelanjutnya; public CanvasGroup canvasButtonSebelumnya; public CanvasGroup canvasButtonSelanjutnya; public RectTransform rectButtonSebelumnya; public RectTransform rectButtonSelanjutnya; [Header("Pengaturan Animasi")] public float kecepatanKetik = 0.03f; public float durasiMuncul = 0.2f; public float alphaTombolAktif = 1f; public float alphaTombolNonaktif = 0.45f; [Header("Data Cerita")] public DialogCerita[] daftarCerita; private int indexCerita = 0; // false = sedang berada di dialog atas // true = sedang berada di dialog bawah private bool sedangDialogBawah = false; private bool sedangAnimasi = false; private bool skipAnimasi = false; private Coroutine coroutineAktif; private Vector3 scaleNormal = Vector3.one; private Vector3 scaleKecil = new Vector3(0.85f, 0.85f, 0.85f); private Vector3 scaleTombolTekan = new Vector3(0.92f, 0.92f, 0.92f); private void Start() { TampilkanCeritaBaru(); } public void Selanjutnya() { if (!BisaSelanjutnya()) { return; } if (rectButtonSelanjutnya != null) { StartCoroutine(AnimasiKlikTombol(rectButtonSelanjutnya)); } // Jika animasi sedang berjalan, klik hanya untuk skip animasi if (sedangAnimasi) { SkipAnimasiDialog(); return; } if (!sedangDialogBawah) { // Dari dialog atas lanjut ke dialog bawah sedangDialogBawah = true; JalankanCoroutine(TampilkanDialogBawah()); } else { // Dari dialog bawah lanjut ke cerita berikutnya if (indexCerita < daftarCerita.Length - 1) { indexCerita++; sedangDialogBawah = false; TampilkanCeritaBaru(); } } UpdateTombol(); } public void Sebelumnya() { if (!BisaSebelumnya()) { return; } if (rectButtonSebelumnya != null) { StartCoroutine(AnimasiKlikTombol(rectButtonSebelumnya)); } // Jika animasi sedang berjalan dan tombol sebelumnya memang aktif, // klik hanya untuk skip animasi if (sedangAnimasi) { SkipAnimasiDialog(); return; } if (sedangDialogBawah) { // Dari dialog bawah kembali ke dialog atas sedangDialogBawah = false; SembunyikanDialogBawah(); } else { // Dari dialog atas kembali ke cerita sebelumnya if (indexCerita > 0) { indexCerita--; sedangDialogBawah = true; TampilkanCeritaSebelumnyaLengkap(); } } UpdateTombol(); } public void KembaliKeMenuBelajar() { SceneManager.LoadScene("Menu Belajar"); } private void TampilkanCeritaBaru() { if (daftarCerita == null || daftarCerita.Length == 0) { Debug.LogWarning("Data cerita belum diisi."); return; } JalankanCoroutine(TampilkanDialogAtas()); UpdateTombol(); } private IEnumerator TampilkanDialogAtas() { sedangAnimasi = true; skipAnimasi = false; UpdateTombol(); DialogCerita data = daftarCerita[indexCerita]; textNamaAtas.text = data.namaKarakterAtas; textDialogAtas.text = ""; if (data.avatarAtas != null) { imageAvatarAtas.sprite = data.avatarAtas; } // Saat mulai cerita baru, dialog bawah disembunyikan dulu canvasDialogBawah.alpha = 0; textNamaBawah.text = ""; textDialogBawah.text = ""; canvasDialogAtas.alpha = 0; rectDialogAtas.localScale = scaleKecil; yield return StartCoroutine(FadeDanScale(canvasDialogAtas, rectDialogAtas, durasiMuncul)); yield return StartCoroutine(KetikTeks(textDialogAtas, data.dialogAtas)); textDialogAtas.text = data.dialogAtas; canvasDialogAtas.alpha = 1; rectDialogAtas.localScale = scaleNormal; sedangAnimasi = false; skipAnimasi = false; UpdateTombol(); } private IEnumerator TampilkanDialogBawah() { sedangAnimasi = true; skipAnimasi = false; UpdateTombol(); DialogCerita data = daftarCerita[indexCerita]; textNamaBawah.text = data.namaKarakterBawah; textDialogBawah.text = ""; if (data.avatarBawah != null) { imageAvatarBawah.sprite = data.avatarBawah; } canvasDialogBawah.alpha = 0; rectDialogBawah.localScale = scaleKecil; yield return StartCoroutine(FadeDanScale(canvasDialogBawah, rectDialogBawah, durasiMuncul)); yield return StartCoroutine(KetikTeks(textDialogBawah, data.dialogBawah)); textDialogBawah.text = data.dialogBawah; canvasDialogBawah.alpha = 1; rectDialogBawah.localScale = scaleNormal; sedangAnimasi = false; skipAnimasi = false; UpdateTombol(); } private void SkipAnimasiDialog() { skipAnimasi = true; DialogCerita data = daftarCerita[indexCerita]; if (!sedangDialogBawah) { textDialogAtas.text = data.dialogAtas; canvasDialogAtas.alpha = 1; rectDialogAtas.localScale = scaleNormal; } else { textDialogBawah.text = data.dialogBawah; canvasDialogBawah.alpha = 1; rectDialogBawah.localScale = scaleNormal; } } private void TampilkanCeritaSebelumnyaLengkap() { if (daftarCerita == null || daftarCerita.Length == 0) { return; } HentikanCoroutineAktif(); DialogCerita data = daftarCerita[indexCerita]; textNamaAtas.text = data.namaKarakterAtas; textDialogAtas.text = data.dialogAtas; if (data.avatarAtas != null) { imageAvatarAtas.sprite = data.avatarAtas; } canvasDialogAtas.alpha = 1; rectDialogAtas.localScale = scaleNormal; textNamaBawah.text = data.namaKarakterBawah; textDialogBawah.text = data.dialogBawah; if (data.avatarBawah != null) { imageAvatarBawah.sprite = data.avatarBawah; } canvasDialogBawah.alpha = 1; rectDialogBawah.localScale = scaleNormal; sedangAnimasi = false; skipAnimasi = false; UpdateTombol(); } private void SembunyikanDialogBawah() { canvasDialogBawah.alpha = 0; textNamaBawah.text = ""; textDialogBawah.text = ""; UpdateTombol(); } private IEnumerator FadeDanScale(CanvasGroup canvasGroup, RectTransform rectTransform, float durasi) { float waktu = 0f; while (waktu < durasi) { if (skipAnimasi) { canvasGroup.alpha = 1; rectTransform.localScale = scaleNormal; yield break; } waktu += Time.deltaTime; float t = waktu / durasi; canvasGroup.alpha = Mathf.Lerp(0, 1, t); rectTransform.localScale = Vector3.Lerp(scaleKecil, scaleNormal, t); yield return null; } canvasGroup.alpha = 1; rectTransform.localScale = scaleNormal; } private IEnumerator KetikTeks(TMP_Text targetText, string isiTeks) { targetText.text = ""; foreach (char huruf in isiTeks) { if (skipAnimasi) { targetText.text = isiTeks; yield break; } targetText.text += huruf; yield return new WaitForSeconds(kecepatanKetik); } targetText.text = isiTeks; } private IEnumerator AnimasiKlikTombol(RectTransform rectTombol) { rectTombol.localScale = scaleTombolTekan; yield return new WaitForSeconds(0.08f); rectTombol.localScale = scaleNormal; } private void JalankanCoroutine(IEnumerator coroutine) { HentikanCoroutineAktif(); coroutineAktif = StartCoroutine(coroutine); } private void HentikanCoroutineAktif() { if (coroutineAktif != null) { StopCoroutine(coroutineAktif); coroutineAktif = null; } } private bool BisaSebelumnya() { // Tombol sebelumnya aktif hanya jika: // 1. Sudah bukan cerita pertama, atau // 2. Sedang berada di dialog bawah. // Di awal cerita dan dialog atas sedang animasi, tombol tetap nonaktif. return indexCerita > 0 || sedangDialogBawah; } private bool BisaSelanjutnya() { // Tombol selanjutnya aktif jika: // 1. Belum cerita terakhir, atau // 2. Masih berada di dialog atas, atau // 3. Sedang animasi untuk skip. return indexCerita < daftarCerita.Length - 1 || !sedangDialogBawah || sedangAnimasi; } private void UpdateTombol() { bool sebelumnyaAktif = BisaSebelumnya(); bool selanjutnyaAktif = BisaSelanjutnya(); if (buttonSebelumnya != null) { buttonSebelumnya.interactable = sebelumnyaAktif; } if (buttonSelanjutnya != null) { buttonSelanjutnya.interactable = selanjutnyaAktif; } if (canvasButtonSebelumnya != null) { canvasButtonSebelumnya.alpha = sebelumnyaAktif ? alphaTombolAktif : alphaTombolNonaktif; canvasButtonSebelumnya.blocksRaycasts = sebelumnyaAktif; canvasButtonSebelumnya.interactable = sebelumnyaAktif; } if (canvasButtonSelanjutnya != null) { canvasButtonSelanjutnya.alpha = selanjutnyaAktif ? alphaTombolAktif : alphaTombolNonaktif; canvasButtonSelanjutnya.blocksRaycasts = selanjutnyaAktif; canvasButtonSelanjutnya.interactable = selanjutnyaAktif; } } } [System.Serializable] public class DialogCerita { [Header("Dialog Atas")] public string namaKarakterAtas; [TextArea(2, 4)] public string dialogAtas; public Sprite avatarAtas; [Header("Dialog Bawah")] public string namaKarakterBawah; [TextArea(2, 4)] public string dialogBawah; public Sprite avatarBawah; }