354 lines
11 KiB
C#
354 lines
11 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using TMPro;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
public class GameManager : MonoBehaviour
|
|
{
|
|
[System.Serializable]
|
|
public struct LevelConfig
|
|
{
|
|
public int pairCount;
|
|
public int columnCount;
|
|
public Vector2 cellSize;
|
|
public float memorizeTime;
|
|
public float limitTime;
|
|
}
|
|
|
|
[Header("Pengaturan Level")]
|
|
public List<LevelConfig> levelList;
|
|
|
|
[Header("UI Timer (Bar)")]
|
|
public Image timerFill;
|
|
public float penaltyTime = 3f;
|
|
public float rewardTime = 2f; // FITUR BARU: Tambahan waktu 2 detik kalau tebakan benar
|
|
private Color warnaAsliTimer; // Menyimpan warna asli UI hijau timer
|
|
|
|
[Header("UI Win Popup")]
|
|
public GameObject winPopup;
|
|
public CanvasGroup dimmerCanvasGroup;
|
|
public GameObject gameplayBoard;
|
|
public Image[] starImages;
|
|
public Sprite activeStar;
|
|
public Sprite inactiveStar;
|
|
public GameObject nextButton;
|
|
|
|
[Header("Referensi UI & Objek")]
|
|
public GameObject cardPrefab;
|
|
public Transform gridParent;
|
|
public List<Sprite> cardImages;
|
|
public TextMeshProUGUI levelTitleText;
|
|
|
|
[Header("Audio SFX")]
|
|
public AudioSource sfxSource;
|
|
public AudioClip klipBenar;
|
|
public AudioClip klipSalah;
|
|
|
|
[HideInInspector] public bool canClick = false;
|
|
private List<Card> allCards = new List<Card>();
|
|
private List<Vector3> targetPositions = new List<Vector3>();
|
|
private Card firstSelected, secondSelected;
|
|
private int pairsFound = 0;
|
|
|
|
private float currentTime;
|
|
private float maxTime;
|
|
private bool isGameActive = false;
|
|
|
|
void Start()
|
|
{
|
|
if (timerFill != null) warnaAsliTimer = timerFill.color; // Simpan warna timer di awal
|
|
SetupCurrentLevel();
|
|
}
|
|
|
|
void SetupCurrentLevel()
|
|
{
|
|
int idx = LevelData.SelectedLevel;
|
|
if (levelList == null || idx >= levelList.Count) return;
|
|
|
|
LevelConfig config = levelList[idx];
|
|
maxTime = (config.limitTime > 0) ? config.limitTime : 60f;
|
|
currentTime = maxTime;
|
|
pairsFound = 0;
|
|
|
|
winPopup.SetActive(false);
|
|
if (dimmerCanvasGroup != null) dimmerCanvasGroup.alpha = 0;
|
|
if (gameplayBoard != null) gameplayBoard.SetActive(true);
|
|
|
|
if (levelTitleText != null) levelTitleText.text = "Level " + (idx + 1);
|
|
|
|
GridLayoutGroup gridLayout = gridParent.GetComponent<GridLayoutGroup>();
|
|
gridLayout.constraintCount = config.columnCount;
|
|
gridLayout.cellSize = config.cellSize;
|
|
gridLayout.enabled = true;
|
|
|
|
GenerateCards(config.pairCount);
|
|
StartCoroutine(GameOpeningSequence(config.memorizeTime));
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (isGameActive)
|
|
{
|
|
currentTime -= Time.deltaTime;
|
|
|
|
// Jaga biar waktu nggak minus
|
|
if (currentTime < 0) currentTime = 0;
|
|
|
|
if (timerFill != null) timerFill.fillAmount = currentTime / maxTime;
|
|
|
|
if (currentTime <= 0)
|
|
{
|
|
currentTime = 0;
|
|
isGameActive = false; // STOP SEGERA
|
|
HandleGameOver(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CardSelected(Card selected)
|
|
{
|
|
if (!canClick) return;
|
|
if (firstSelected == null)
|
|
{
|
|
firstSelected = selected;
|
|
firstSelected.ShowCard();
|
|
}
|
|
else if (secondSelected == null && selected != firstSelected)
|
|
{
|
|
secondSelected = selected;
|
|
secondSelected.ShowCard();
|
|
StartCoroutine(CheckMatch());
|
|
}
|
|
}
|
|
|
|
IEnumerator CheckMatch()
|
|
{
|
|
canClick = false;
|
|
yield return new WaitForSeconds(0.6f);
|
|
|
|
if (firstSelected.GetSprite() == secondSelected.GetSprite())
|
|
{
|
|
// --- JIKA TEBAKAN BENAR ---
|
|
if (sfxSource != null && klipBenar != null) sfxSource.PlayOneShot(klipBenar);
|
|
|
|
firstSelected.SetMatched();
|
|
secondSelected.SetMatched();
|
|
pairsFound++;
|
|
|
|
// Cek apakah sudah menyelesaikan seluruh level
|
|
if (pairsFound >= levelList[LevelData.SelectedLevel].pairCount)
|
|
{
|
|
HandleGameOver(true);
|
|
}
|
|
else
|
|
{
|
|
// --- INTEGRASI BARU: BERIKAN REWARD TAMBAHAN WAKTU ---
|
|
currentTime += rewardTime;
|
|
if (currentTime > maxTime) currentTime = maxTime; // Kunci agar tidak meluber dari batas maksimum bar
|
|
StartCoroutine(AnimasiTimerHijau()); // Jalankan efek kedip hijau sukses
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// --- JIKA TEBAKAN SALAH ---
|
|
if (sfxSource != null && klipSalah != null) sfxSource.PlayOneShot(klipSalah);
|
|
|
|
currentTime -= penaltyTime; // Kurangi Waktu
|
|
StartCoroutine(AnimasiTimerMerah()); // Panggil Efek Kedip Merah
|
|
|
|
firstSelected.HideCard();
|
|
secondSelected.HideCard();
|
|
}
|
|
|
|
firstSelected = null; secondSelected = null;
|
|
canClick = true;
|
|
}
|
|
|
|
// --- FITUR ANIMASI KEDIP MERAH (KETIKA SALAH) ---
|
|
IEnumerator AnimasiTimerMerah()
|
|
{
|
|
if (timerFill != null)
|
|
{
|
|
timerFill.color = Color.red;
|
|
yield return new WaitForSeconds(0.2f);
|
|
timerFill.color = warnaAsliTimer;
|
|
}
|
|
}
|
|
|
|
// --- FITUR BARU: ANIMASI KEDIP HIJAU (REWARD KETIKA BENAR) ---
|
|
IEnumerator AnimasiTimerHijau()
|
|
{
|
|
if (timerFill != null)
|
|
{
|
|
timerFill.color = Color.green;
|
|
yield return new WaitForSeconds(0.2f);
|
|
timerFill.color = warnaAsliTimer;
|
|
}
|
|
}
|
|
|
|
void HandleGameOver(bool isWin)
|
|
{
|
|
if (winPopup.activeSelf) return;
|
|
|
|
isGameActive = false;
|
|
canClick = false;
|
|
StopAllCoroutines();
|
|
StartCoroutine(GameOverSequence(isWin));
|
|
}
|
|
|
|
IEnumerator GameOverSequence(bool isWin)
|
|
{
|
|
// Munculkan Popup dan Fade Blur
|
|
winPopup.SetActive(true);
|
|
winPopup.transform.localScale = Vector3.zero;
|
|
|
|
float fadeTime = 0.3f;
|
|
float elapsed = 0;
|
|
while (elapsed < fadeTime)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = elapsed / fadeTime;
|
|
if (dimmerCanvasGroup != null) dimmerCanvasGroup.alpha = t;
|
|
winPopup.transform.localScale = Vector3.one * Mathf.SmoothStep(0, 1.1f, t);
|
|
yield return null;
|
|
}
|
|
winPopup.transform.localScale = Vector3.one;
|
|
|
|
if (gameplayBoard != null) gameplayBoard.SetActive(false);
|
|
|
|
if (nextButton != null) nextButton.SetActive(isWin);
|
|
|
|
int starCount = 0;
|
|
int totalPairs = levelList[LevelData.SelectedLevel].pairCount;
|
|
|
|
if (isWin)
|
|
{
|
|
starCount = 3;
|
|
int nextIdx = LevelData.SelectedLevel + 2;
|
|
|
|
if (nextIdx > PlayerPrefs.GetInt("ProgresMemory", 1))
|
|
{
|
|
PlayerPrefs.SetInt("ProgresMemory", nextIdx);
|
|
PlayerPrefs.Save();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float progress = (float)pairsFound / totalPairs;
|
|
if (progress >= 0.8f) starCount = 2;
|
|
else if (progress >= 0.4f) starCount = 1;
|
|
else starCount = 0;
|
|
}
|
|
|
|
StartCoroutine(ShowStarSequence(starCount));
|
|
}
|
|
|
|
IEnumerator ShowStarSequence(int count)
|
|
{
|
|
foreach (var s in starImages)
|
|
{
|
|
s.sprite = inactiveStar;
|
|
s.transform.localScale = Vector3.one;
|
|
}
|
|
|
|
yield return new WaitForSeconds(0.4f);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
starImages[i].sprite = activeStar;
|
|
starImages[i].transform.localScale = Vector3.zero;
|
|
float st = 0;
|
|
while (st < 1)
|
|
{
|
|
st += Time.deltaTime * 7f;
|
|
starImages[i].transform.localScale = Vector3.one * Mathf.Lerp(0, 1.25f, st);
|
|
yield return null;
|
|
}
|
|
starImages[i].transform.localScale = Vector3.one;
|
|
yield return new WaitForSeconds(0.15f);
|
|
}
|
|
}
|
|
|
|
public void ButtonRematch() => SceneManager.LoadScene(SceneManager.GetActiveScene().name);
|
|
public void ButtonMenu() => SceneManager.LoadScene("MenuLevel");
|
|
public void ButtonNext()
|
|
{
|
|
if (LevelData.SelectedLevel < levelList.Count - 1)
|
|
{
|
|
LevelData.SelectedLevel++;
|
|
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
|
|
}
|
|
else ButtonMenu();
|
|
}
|
|
|
|
void GenerateCards(int pairs)
|
|
{
|
|
foreach (Transform child in gridParent) Destroy(child.gameObject);
|
|
allCards.Clear(); targetPositions.Clear();
|
|
List<Sprite> pool = new List<Sprite>(cardImages);
|
|
List<Sprite> selectedIcons = new List<Sprite>();
|
|
int limit = Mathf.Min(pairs, pool.Count);
|
|
for (int i = 0; i < limit; i++)
|
|
{
|
|
int r = Random.Range(0, pool.Count);
|
|
selectedIcons.Add(pool[r]); pool.RemoveAt(r);
|
|
}
|
|
List<Sprite> pairList = new List<Sprite>();
|
|
foreach (Sprite s in selectedIcons) { pairList.Add(s); pairList.Add(s); }
|
|
for (int i = 0; i < pairList.Count; i++)
|
|
{
|
|
Sprite temp = pairList[i];
|
|
int rand = Random.Range(i, pairList.Count);
|
|
pairList[i] = pairList[rand]; pairList[rand] = temp;
|
|
}
|
|
foreach (Sprite img in pairList)
|
|
{
|
|
GameObject newCard = Instantiate(cardPrefab, gridParent);
|
|
Card cardScript = newCard.GetComponent<Card>();
|
|
cardScript.SetCardData(img, this);
|
|
allCards.Add(cardScript); newCard.transform.localScale = Vector3.zero;
|
|
}
|
|
}
|
|
|
|
IEnumerator GameOpeningSequence(float memTime)
|
|
{
|
|
canClick = false; isGameActive = false;
|
|
yield return new WaitForEndOfFrame();
|
|
GridLayoutGroup gridLayout = gridParent.GetComponent<GridLayoutGroup>();
|
|
foreach (Card c in allCards)
|
|
{
|
|
targetPositions.Add(c.transform.localPosition);
|
|
c.transform.localPosition = Vector3.zero; c.transform.localScale = Vector3.one;
|
|
}
|
|
gridLayout.enabled = false;
|
|
float dealDuration = 0.4f;
|
|
for (int i = 0; i < allCards.Count; i++)
|
|
{
|
|
StartCoroutine(MoveCardToPosition(allCards[i].transform, targetPositions[i], dealDuration));
|
|
yield return new WaitForSeconds(0.06f);
|
|
}
|
|
yield return new WaitForSeconds(dealDuration + 0.1f);
|
|
foreach (Card c in allCards) c.ShowCard();
|
|
yield return new WaitForSeconds(memTime);
|
|
foreach (Card c in allCards) c.HideCard();
|
|
yield return new WaitForSeconds(0.5f);
|
|
isGameActive = true; canClick = true;
|
|
}
|
|
|
|
IEnumerator MoveCardToPosition(Transform cardTr, Vector3 target, float duration)
|
|
{
|
|
Vector3 startPos = cardTr.localPosition;
|
|
float elapsed = 0;
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = elapsed / duration;
|
|
t = t * t * (3f - 2f * t);
|
|
cardTr.localPosition = Vector3.Lerp(startPos, target, t);
|
|
yield return null;
|
|
}
|
|
cardTr.localPosition = target;
|
|
}
|
|
} |