370 lines
12 KiB
C#
370 lines
12 KiB
C#
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<LevelData> 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<TimeTracker>(); // 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<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;
|
|
}
|
|
}
|
|
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<DropZone>().vData = data;
|
|
|
|
// Pastikan kita ambil RectTransform dari siluet di dalam Slot
|
|
if (s.transform.childCount > 0)
|
|
{
|
|
RectTransform siluetRT = s.transform.GetChild(0).GetComponent<RectTransform>();
|
|
if (siluetRT != null)
|
|
{
|
|
// PAKSA ukuran sesuai data
|
|
siluetRT.sizeDelta = data.ukuranCustom;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. KOCOK DAN MUNCULKAN PILIHAN SAYUR (BAWAH)
|
|
List<VegetableData> sayurAcak = new List<VegetableData>(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<DragItem>().vData = dataPilihan;
|
|
|
|
RectTransform itmRT = itm.GetComponent<RectTransform>();
|
|
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<RectTransform>());
|
|
LayoutRebuilder.ForceRebuildLayoutImmediate(areaPilihan.GetComponent<RectTransform>());
|
|
}
|
|
|
|
// 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<AudioSource>();
|
|
if (audio != null) audio.Play();
|
|
}
|
|
|
|
// Di dalam class GameManager
|
|
public void PutarVO(AudioClip klip)
|
|
{
|
|
AudioSource audio = GetComponent<AudioSource>();
|
|
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<CanvasGroup>();
|
|
|
|
// 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");
|
|
}
|
|
|
|
} |