329 lines
11 KiB
C#
329 lines
11 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
public class DragDropManager : MonoBehaviour
|
|
{
|
|
[System.Serializable]
|
|
public struct LevelConfig
|
|
{
|
|
public string namaLevel;
|
|
public int itemCount;
|
|
public float maxTime;
|
|
}
|
|
|
|
[Header("Game Settings")]
|
|
public List<LevelConfig> levelConfigs;
|
|
public List<Sprite> spritePool;
|
|
|
|
[Header("Prefabs")]
|
|
public GameObject prefabSlotBayangan;
|
|
public GameObject prefabSlotWarna;
|
|
|
|
[Header("Containers")]
|
|
public Transform shadowContainer;
|
|
public Transform cardContainer;
|
|
|
|
[Header("Timer UI")]
|
|
public Image timerFill;
|
|
public float penguranganWaktu = 3f; // Jumlah detik yang dikurangi kalau salah
|
|
public float tambahanWaktu = 2f; // Jumlah detik yang ditambah kalau benar (BARU)
|
|
private float currentTime, totalMaxTime;
|
|
private Color warnaAsliTimer; // Untuk menyimpan warna hijau aslinya
|
|
|
|
[Header("Win UI & Stars")]
|
|
public GameObject winPopup;
|
|
public GameObject[] activeStars;
|
|
|
|
[Header("Audio SFX")]
|
|
public AudioSource sfxSource;
|
|
public AudioClip klipBenar;
|
|
public AudioClip klipSalah;
|
|
|
|
private int matchedCount = 0;
|
|
private int goal;
|
|
private bool isGameActive = false;
|
|
|
|
void Start()
|
|
{
|
|
if (timerFill) warnaAsliTimer = timerFill.color; // Simpan warna aslinya
|
|
SetupLevel();
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (isGameActive)
|
|
{
|
|
currentTime -= Time.deltaTime;
|
|
|
|
// Jaga biar waktu nggak minus
|
|
if (currentTime < 0) currentTime = 0;
|
|
|
|
if (timerFill) timerFill.fillAmount = currentTime / totalMaxTime;
|
|
|
|
if (currentTime <= 0)
|
|
{
|
|
isGameActive = false;
|
|
StartCoroutine(ShowWinPopup(true));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void MainkanSuaraBenar()
|
|
{
|
|
if (sfxSource != null && klipBenar != null)
|
|
{
|
|
sfxSource.PlayOneShot(klipBenar);
|
|
}
|
|
}
|
|
|
|
public void MainkanSuaraSalah()
|
|
{
|
|
if (sfxSource != null && klipSalah != null)
|
|
{
|
|
sfxSource.PlayOneShot(klipSalah);
|
|
}
|
|
}
|
|
|
|
// --- FITUR BARU: REWARD FEEDBACK POSITIF ---
|
|
public void BerikanBonusWaktu()
|
|
{
|
|
currentTime += tambahanWaktu;
|
|
|
|
// Jaga agar penambahan waktu tidak meluber melebihi kapasitas maksimal Bar Timer
|
|
if (currentTime > totalMaxTime) currentTime = totalMaxTime;
|
|
|
|
StartCoroutine(TimerBerkedipHijau());
|
|
MainkanSuaraBenar(); // Otomatis bunyikan sfx benar
|
|
}
|
|
|
|
IEnumerator TimerBerkedipHijau()
|
|
{
|
|
if (timerFill != null)
|
|
{
|
|
timerFill.color = Color.green; // Flash warna hijau cerah sebagai reward
|
|
yield return new WaitForSeconds(0.2f);
|
|
timerFill.color = warnaAsliTimer; // Balik ke warna UI semula
|
|
}
|
|
}
|
|
|
|
public void BerikanHukumanWaktu()
|
|
{
|
|
currentTime -= penguranganWaktu;
|
|
StartCoroutine(TimerBerkedipMerah());
|
|
MainkanSuaraSalah(); // Otomatis bunyikan sfx salah
|
|
}
|
|
|
|
IEnumerator TimerBerkedipMerah()
|
|
{
|
|
if (timerFill != null)
|
|
{
|
|
timerFill.color = Color.red; // Berubah merah
|
|
yield return new WaitForSeconds(0.2f);
|
|
timerFill.color = warnaAsliTimer; // Balik ke warna semula
|
|
}
|
|
}
|
|
|
|
void SetupLevel()
|
|
{
|
|
int lv = LevelData.SelectedLevel;
|
|
if (lv >= levelConfigs.Count) lv = 0;
|
|
|
|
LevelConfig config = levelConfigs[lv];
|
|
goal = config.itemCount;
|
|
totalMaxTime = config.maxTime;
|
|
currentTime = totalMaxTime;
|
|
|
|
// =========================================================================
|
|
// FORMULA UTAMA: KALKULASI UKURAN KARTU DINAMIS (RESPONSIVE UI)
|
|
// =========================================================================
|
|
|
|
GridLayoutGroup shadowGrid = shadowContainer.GetComponent<GridLayoutGroup>();
|
|
RectTransform shadowRect = shadowContainer.GetComponent<RectTransform>();
|
|
|
|
GridLayoutGroup cardGrid = cardContainer.GetComponent<GridLayoutGroup>();
|
|
RectTransform cardRect = cardContainer.GetComponent<RectTransform>();
|
|
|
|
float spacingX = 30f;
|
|
Vector2 paddingDanSpacing = new Vector2(spacingX, 0);
|
|
|
|
if (shadowGrid != null && shadowRect != null)
|
|
{
|
|
float lebarContainer = shadowRect.rect.width;
|
|
float paddingKananKiri = 160f;
|
|
float lebarTersedia = lebarContainer - paddingKananKiri - (spacingX * (goal - 1));
|
|
float dynamicWidth = lebarTersedia / goal;
|
|
|
|
if (dynamicWidth > 240f) dynamicWidth = 240f;
|
|
|
|
float dynamicHeight = dynamicWidth * 1.35f;
|
|
Vector2 dynamicCellSize = new Vector2(dynamicWidth, dynamicHeight);
|
|
|
|
shadowGrid.padding.left = 0; shadowGrid.padding.right = 0;
|
|
shadowGrid.cellSize = dynamicCellSize;
|
|
shadowGrid.spacing = paddingDanSpacing;
|
|
shadowGrid.childAlignment = TextAnchor.MiddleCenter;
|
|
shadowGrid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
|
|
shadowGrid.constraintCount = goal;
|
|
}
|
|
|
|
if (cardGrid != null && cardRect != null)
|
|
{
|
|
cardGrid.padding.left = 0; cardGrid.padding.right = 0;
|
|
cardGrid.cellSize = shadowGrid.cellSize;
|
|
cardGrid.spacing = paddingDanSpacing;
|
|
cardGrid.childAlignment = TextAnchor.MiddleCenter;
|
|
cardGrid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
|
|
cardGrid.constraintCount = goal;
|
|
}
|
|
|
|
// =========================================================================
|
|
|
|
List<Sprite> selected = new List<Sprite>();
|
|
List<Sprite> tempPool = new List<Sprite>(spritePool);
|
|
|
|
for (int i = 0; i < goal; i++)
|
|
{
|
|
if (tempPool.Count == 0) break;
|
|
int r = Random.Range(0, tempPool.Count);
|
|
selected.Add(tempPool[r]);
|
|
tempPool.RemoveAt(r);
|
|
}
|
|
StartCoroutine(SpawnSequence(selected));
|
|
}
|
|
|
|
IEnumerator SpawnSequence(List<Sprite> selected)
|
|
{
|
|
for (int i = 0; i < selected.Count; i++)
|
|
{
|
|
GameObject s = Instantiate(prefabSlotBayangan, shadowContainer);
|
|
ShadowSlot slot = s.GetComponentInChildren<ShadowSlot>();
|
|
if (slot != null) slot.Setup(selected[i], i);
|
|
|
|
s.transform.localScale = Vector3.zero;
|
|
StartCoroutine(AnimasiSpawnKeren(s.transform));
|
|
yield return new WaitForSeconds(0.1f);
|
|
}
|
|
|
|
List<int> shuffledIndices = new List<int>();
|
|
for (int i = 0; i < selected.Count; i++) shuffledIndices.Add(i);
|
|
for (int i = 0; i < shuffledIndices.Count; i++)
|
|
{
|
|
int temp = shuffledIndices[i];
|
|
int r = Random.Range(i, shuffledIndices.Count);
|
|
shuffledIndices[i] = shuffledIndices[r];
|
|
shuffledIndices[r] = temp;
|
|
}
|
|
|
|
foreach (int idx in shuffledIndices)
|
|
{
|
|
GameObject c = Instantiate(prefabSlotWarna, cardContainer);
|
|
DragDropItem ddi = c.GetComponentInChildren<DragDropItem>();
|
|
if (ddi != null) ddi.Setup(selected[idx], idx, this);
|
|
|
|
c.transform.localScale = Vector3.zero;
|
|
StartCoroutine(AnimasiSpawnKeren(c.transform));
|
|
yield return new WaitForSeconds(0.1f);
|
|
}
|
|
|
|
yield return new WaitForSeconds(0.2f);
|
|
isGameActive = true;
|
|
}
|
|
|
|
IEnumerator AnimasiSpawnKeren(Transform t)
|
|
{
|
|
float time = 0;
|
|
t.localRotation = Quaternion.Euler(0, 0, 180f);
|
|
|
|
while (time < 1)
|
|
{
|
|
time += Time.deltaTime * 4;
|
|
|
|
float scaleCrv = 1f + Mathf.Sin(time * Mathf.PI) * 0.2f;
|
|
|
|
t.localScale = Vector3.one * (time < 1 ? time * scaleCrv : 1f);
|
|
t.localRotation = Quaternion.Euler(0, 0, Mathf.Lerp(180f, 0f, time));
|
|
|
|
yield return null;
|
|
}
|
|
|
|
t.localScale = Vector3.one;
|
|
t.localRotation = Quaternion.Euler(0, 0, 0);
|
|
}
|
|
|
|
public void CheckWin()
|
|
{
|
|
matchedCount++;
|
|
if (matchedCount >= goal)
|
|
{
|
|
MainkanSuaraBenar(); // Putar sfx menang untuk kartu terakhir
|
|
isGameActive = false;
|
|
StartCoroutine(ShowWinPopup(false));
|
|
}
|
|
else
|
|
{
|
|
BerikanBonusWaktu(); // Jika belum menang total, berikan bonus waktu + flash hijau + sfx benar
|
|
}
|
|
}
|
|
|
|
IEnumerator ShowWinPopup(bool isTimeOut)
|
|
{
|
|
yield return new WaitForSeconds(0.5f);
|
|
if (winPopup)
|
|
{
|
|
winPopup.SetActive(true);
|
|
winPopup.transform.SetAsLastSibling();
|
|
}
|
|
|
|
foreach (GameObject star in activeStars)
|
|
{
|
|
if (star != null) star.SetActive(false);
|
|
}
|
|
|
|
yield return new WaitForSeconds(0.3f);
|
|
|
|
int starsEarned = 0;
|
|
if (!isTimeOut)
|
|
{
|
|
float ratio = currentTime / totalMaxTime;
|
|
if (ratio >= 0.6f) starsEarned = 3;
|
|
else if (ratio >= 0.2f) starsEarned = 2;
|
|
else starsEarned = 1;
|
|
|
|
int levelTerbuka = PlayerPrefs.GetInt("ProgresDrag", 1);
|
|
if (LevelData.SelectedLevel + 1 >= levelTerbuka)
|
|
{
|
|
PlayerPrefs.SetInt("ProgresDrag", LevelData.SelectedLevel + 2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float progress = (float)matchedCount / goal;
|
|
if (progress >= 0.8f) starsEarned = 2;
|
|
else if (progress >= 0.4f) starsEarned = 1;
|
|
}
|
|
|
|
for (int i = 0; i < starsEarned; i++)
|
|
{
|
|
if (i < activeStars.Length && activeStars[i] != null)
|
|
{
|
|
activeStars[i].transform.localScale = Vector3.zero;
|
|
activeStars[i].SetActive(true);
|
|
StartCoroutine(AnimasiSpawnKeren(activeStars[i].transform));
|
|
yield return new WaitForSeconds(0.3f);
|
|
}
|
|
}
|
|
|
|
PlayerPrefs.SetInt("Stars_Drag_" + LevelData.SelectedLevel, starsEarned);
|
|
}
|
|
|
|
public void TombolRetry() { SceneManager.LoadScene(SceneManager.GetActiveScene().name); }
|
|
public void TombolHome() { SceneManager.LoadScene("MenuLevel_DragDrop"); }
|
|
public void TombolNext()
|
|
{
|
|
LevelData.SelectedLevel++;
|
|
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
|
|
}
|
|
} |