// GameManager.cs - Main game controller
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public class GameManager : MonoBehaviour
{
[Header("Game Settings")]
public int gridWidth = 8;
public int gridHeight = 10;
public float gemMoveSpeed = 10f;
public int scoreMultiplier = 10;
[Header("UI References")]
public Text scoreText;
public Text movesText;
public Text levelText;
public GameObject gameOverPanel;
public Button restartButton;
[Header("Prefabs")]
public GameObject[] gemPrefabs;
public GameObject explosionEffect;
[Header("Audio")]
public AudioSource audioSource;
public AudioClip matchSound;
public AudioClip moveSound;
private Gem[,] grid;
private int score = 0;
private int moves = 30;
private int level = 1;
private bool isProcessingMove = false;
void Start()
{
InitializeGame();
}
void InitializeGame()
{
grid = new Gem[gridWidth, gridHeight];
CreateInitialGrid();
UpdateUI();
restartButton.onClick.AddListener(RestartGame);
}
void CreateInitialGrid()
{
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
Vector2 position = new Vector2(x - gridWidth / 2f + 0.5f, y - gridHeight / 2f + 0.5f);
CreateGem(x, y, position);
}
}
// Ensure no initial matches
while (HasMatches())
{
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
if (grid[x, y] != null && IsPartOfMatch(x, y))
{
Destroy(grid[x, y].gameObject);
Vector2 pos = new Vector2(x - gridWidth / 2f + 0.5f, y - gridHeight / 2f + 0.5f);
CreateGem(x, y, pos);
}
}
}
}
}
void CreateGem(int x, int y, Vector2 position)
{
int gemType = Random.Range(0, gemPrefabs.Length);
GameObject gemObj = Instantiate(gemPrefabs[gemType], position, Quaternion.identity);
gemObj.transform.SetParent(transform);
Gem gem = gemObj.GetComponent();
gem.Initialize(x, y, gemType, this);
grid[x, y] = gem;
}
public void SwapGems(Gem gem1, Gem gem2)
{
if (isProcessingMove || moves <= 0) return;
StartCoroutine(ProcessSwap(gem1, gem2));
}
IEnumerator ProcessSwap(Gem gem1, Gem gem2)
{
isProcessingMove = true;
// Swap positions in grid
Vector2Int pos1 = new Vector2Int(gem1.gridX, gem1.gridY);
Vector2Int pos2 = new Vector2Int(gem2.gridX, gem2.gridY);
grid[pos1.x, pos1.y] = gem2;
grid[pos2.x, pos2.y] = gem1;
gem1.SetGridPosition(pos2.x, pos2.y);
gem2.SetGridPosition(pos1.x, pos1.y);
// Animate swap
yield return StartCoroutine(MoveGemToPosition(gem1, GetWorldPosition(pos2.x, pos2.y)));
yield return StartCoroutine(MoveGemToPosition(gem2, GetWorldPosition(pos1.x, pos1.y)));
audioSource.PlayOneShot(moveSound);
// Check for matches
if (HasMatches())
{
moves--;
yield return StartCoroutine(ProcessMatches());
}
else
{
// Invalid move - swap back
grid[pos1.x, pos1.y] = gem1;
grid[pos2.x, pos2.y] = gem2;
gem1.SetGridPosition(pos1.x, pos1.y);
gem2.SetGridPosition(pos2.x, pos2.y);
yield return StartCoroutine(MoveGemToPosition(gem1, GetWorldPosition(pos1.x, pos1.y)));
yield return StartCoroutine(MoveGemToPosition(gem2, GetWorldPosition(pos2.x, pos2.y)));
}
UpdateUI();
CheckGameOver();
isProcessingMove = false;
}
IEnumerator ProcessMatches()
{
bool foundMatches = true;
while (foundMatches)
{
List matchedGems = FindAllMatches();
if (matchedGems.Count > 0)
{
// Add score
score += matchedGems.Count * scoreMultiplier;
audioSource.PlayOneShot(matchSound);
// Create explosion effects and destroy gems
foreach (Gem gem in matchedGems)
{
if (explosionEffect)
{
Instantiate(explosionEffect, gem.transform.position, Quaternion.identity);
}
grid[gem.gridX, gem.gridY] = null;
Destroy(gem.gameObject);
}
yield return new WaitForSeconds(0.3f);
// Drop gems down
yield return StartCoroutine(DropGems());
// Fill empty spaces
yield return StartCoroutine(FillEmptySpaces());
}
else
{
foundMatches = false;
}
}
}
List FindAllMatches()
{
HashSet matches = new HashSet();
// Check horizontal matches
for (int y = 0; y < gridHeight; y++)
{
for (int x = 0; x < gridWidth - 2; x++)
{
if (grid[x, y] != null && grid[x + 1, y] != null && grid[x + 2, y] != null)
{
if (grid[x, y].gemType == grid[x + 1, y].gemType && grid[x, y].gemType == grid[x + 2, y].gemType)
{
matches.Add(grid[x, y]);
matches.Add(grid[x + 1, y]);
matches.Add(grid[x + 2, y]);
}
}
}
}
// Check vertical matches
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight - 2; y++)
{
if (grid[x, y] != null && grid[x, y + 1] != null && grid[x, y + 2] != null)
{
if (grid[x, y].gemType == grid[x, y + 1].gemType && grid[x, y].gemType == grid[x, y + 2].gemType)
{
matches.Add(grid[x, y]);
matches.Add(grid[x, y + 1]);
matches.Add(grid[x, y + 2]);
}
}
}
}
return new List(matches);
}
bool HasMatches()
{
return FindAllMatches().Count > 0;
}
bool IsPartOfMatch(int x, int y)
{
if (grid[x, y] == null) return false;
int gemType = grid[x, y].gemType;
// Check horizontal
int horizontalCount = 1;
// Check left
for (int i = x - 1; i >= 0; i--)
{
if (grid[i, y] != null && grid[i, y].gemType == gemType)
horizontalCount++;
else
break;
}
// Check right
for (int i = x + 1; i < gridWidth; i++)
{
if (grid[i, y] != null && grid[i, y].gemType == gemType)
horizontalCount++;
else
break;
}
if (horizontalCount >= 3) return true;
// Check vertical
int verticalCount = 1;
// Check down
for (int i = y - 1; i >= 0; i--)
{
if (grid[x, i] != null && grid[x, i].gemType == gemType)
verticalCount++;
else
break;
}
// Check up
for (int i = y + 1; i < gridHeight; i++)
{
if (grid[x, i] != null && grid[x, i].gemType == gemType)
verticalCount++;
else
break;
}
return verticalCount >= 3;
}
IEnumerator DropGems()
{
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
if (grid[x, y] == null)
{
// Find gem above to drop
for (int aboveY = y + 1; aboveY < gridHeight; aboveY++)
{
if (grid[x, aboveY] != null)
{
grid[x, y] = grid[x, aboveY];
grid[x, aboveY] = null;
grid[x, y].SetGridPosition(x, y);
StartCoroutine(MoveGemToPosition(grid[x, y], GetWorldPosition(x, y)));
break;
}
}
}
}
}
yield return new WaitForSeconds(0.5f);
}
IEnumerator FillEmptySpaces()
{
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
if (grid[x, y] == null)
{
Vector2 startPos = GetWorldPosition(x, gridHeight + 1);
Vector2 endPos = GetWorldPosition(x, y);
CreateGem(x, y, startPos);
StartCoroutine(MoveGemToPosition(grid[x, y], endPos));
}
}
}
yield return new WaitForSeconds(0.5f);
}
IEnumerator MoveGemToPosition(Gem gem, Vector2 targetPosition)
{
while (Vector2.Distance(gem.transform.position, targetPosition) > 0.1f)
{
gem.transform.position = Vector2.MoveTowards(gem.transform.position, targetPosition, gemMoveSpeed * Time.deltaTime);
yield return null;
}
gem.transform.position = targetPosition;
}
Vector2 GetWorldPosition(int x, int y)
{
return new Vector2(x - gridWidth / 2f + 0.5f, y - gridHeight / 2f + 0.5f);
}
void UpdateUI()
{
scoreText.text = "Score: " + score.ToString();
movesText.text = "Moves: " + moves.ToString();
levelText.text = "Level: " + level.ToString();
}
void CheckGameOver()
{
if (moves <= 0)
{
gameOverPanel.SetActive(true);
}
}
public void RestartGame()
{
score = 0;
moves = 30;
level = 1;
// Clear existing grid
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
if (grid[x, y] != null)
{
Destroy(grid[x, y].gameObject);
}
}
}
gameOverPanel.SetActive(false);
CreateInitialGrid();
UpdateUI();
}
}
// Gem.cs - Individual gem behavior
using UnityEngine;
public class Gem : MonoBehaviour
{
public int gridX;
public int gridY;
public int gemType;
private GameManager gameManager;
private bool isDragging = false;
private Vector2 startTouchPos;
private Gem targetGem;
public void Initialize(int x, int y, int type, GameManager manager)
{
gridX = x;
gridY = y;
gemType = type;
gameManager = manager;
}
public void SetGridPosition(int x, int y)
{
gridX = x;
gridY = y;
}
void OnMouseDown()
{
if (Input.touchCount > 0)
{
startTouchPos = Camera.main.ScreenToWorldPoint(Input.touches[0].position);
}
else
{
startTouchPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
isDragging = true;
}
void OnMouseDrag()
{
if (!isDragging) return;
Vector2 currentPos;
if (Input.touchCount > 0)
{
currentPos = Camera.main.ScreenToWorldPoint(Input.touches[0].position);
}
else
{
currentPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
Vector2 diff = currentPos - startTouchPos;
if (diff.magnitude > 0.5f)
{
// Determine swipe direction
if (Mathf.Abs(diff.x) > Mathf.Abs(diff.y))
{
// Horizontal swipe
if (diff.x > 0 && gridX < gameManager.gridWidth - 1)
{
targetGem = gameManager.grid[gridX + 1, gridY];
}
else if (diff.x < 0 && gridX > 0)
{
targetGem = gameManager.grid[gridX - 1, gridY];
}
}
else
{
// Vertical swipe
if (diff.y > 0 && gridY < gameManager.gridHeight - 1)
{
targetGem = gameManager.grid[gridX, gridY + 1];
}
else if (diff.y < 0 && gridY > 0)
{
targetGem = gameManager.grid[gridX, gridY - 1];
}
}
if (targetGem != null)
{
gameManager.SwapGems(this, targetGem);
isDragging = false;
}
}
}
void OnMouseUp()
{
isDragging = false;
targetGem = null;
}
}
// ExplosionEffect.cs - Particle effect for matches
using UnityEngine;
public class ExplosionEffect : MonoBehaviour
{
void Start()
{
// Auto-destroy after animation
Destroy(gameObject, 1f);
}
}
// AudioManager.cs - Manages game audio
using UnityEngine;
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance;
[Header("Audio Sources")]
public AudioSource musicSource;
public AudioSource sfxSource;
[Header("Audio Clips")]
public AudioClip backgroundMusic;
public AudioClip matchSound;
public AudioClip moveSound;
public AudioClip gameOverSound;
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
void Start()
{
PlayBackgroundMusic();
}
public void PlayBackgroundMusic()
{
if (backgroundMusic && musicSource)
{
musicSource.clip = backgroundMusic;
musicSource.loop = true;
musicSource.Play();
}
}
public void PlaySFX(AudioClip clip)
{
if (clip && sfxSource)
{
sfxSource.PlayOneShot(clip);
}
}
public void SetMusicVolume(float volume)
{
if (musicSource)
musicSource.volume = volume;
}
public void SetSFXVolume(float volume)
{
if (sfxSource)
sfxSource.volume = volume;
}
}
// SceneTransition.cs - Handles scene transitions
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
public class SceneTransition : MonoBehaviour
{
public Animator transitionAnimator;
public void LoadScene(string sceneName)
{
StartCoroutine(LoadSceneCoroutine(sceneName));
}
IEnumerator LoadSceneCoroutine(string sceneName)
{
if (transitionAnimator)
{
transitionAnimator.SetTrigger("Start");
yield return new WaitForSeconds(1f);
}
SceneManager.LoadScene(sceneName);
}
public void RestartCurrentScene()
{
LoadScene(SceneManager.GetActiveScene().name);
}
public void QuitGame()
{
Application.Quit();
}
}
// TouchInputHandler.cs - Enhanced touch input for mobile
using UnityEngine;
public class TouchInputHandler : MonoBehaviour
{
public float swipeThreshold = 50f;
public float tapThreshold = 0.1f;
private Vector2 startTouchPosition;
private Vector2 endTouchPosition;
private float touchStartTime;
void Update()
{
HandleTouch();
}
void HandleTouch()
{
if (Input.touchCount > 0)
{
Touch touch = Input.touches[0];
switch (touch.phase)
{
case TouchPhase.Began:
startTouchPosition = touch.position;
touchStartTime = Time.time;
break;
case TouchPhase.Ended:
endTouchPosition = touch.position;
float touchDuration = Time.time - touchStartTime;
float swipeDistance = Vector2.Distance(startTouchPosition, endTouchPosition);
if (touchDuration < tapThreshold && swipeDistance < swipeThreshold)
{
// Tap detected
HandleTap(touch.position);
}
else if (swipeDistance > swipeThreshold)
{
// Swipe detected
HandleSwipe();
}
break;
}
}
// Mouse input for testing in editor
#if UNITY_EDITOR
if (Input.GetMouseButtonDown(0))
{
startTouchPosition = Input.mousePosition;
touchStartTime = Time.time;
}
else if (Input.GetMouseButtonUp(0))
{
endTouchPosition = Input.mousePosition;
float touchDuration = Time.time - touchStartTime;
float swipeDistance = Vector2.Distance(startTouchPosition, endTouchPosition);
if (touchDuration < tapThreshold && swipeDistance < swipeThreshold)
{
HandleTap(Input.mousePosition);
}
else if (swipeDistance > swipeThreshold)
{
HandleSwipe();
}
}
#endif
}
void HandleTap(Vector2 screenPosition)
{
Vector2 worldPosition = Camera.main.ScreenToWorldPoint(screenPosition);
RaycastHit2D hit = Physics2D.Raycast(worldPosition, Vector2.zero);
if (hit.collider != null)
{
Gem gem = hit.collider.GetComponent();
if (gem != null)
{
// Handle gem selection
Debug.Log("Gem tapped: " + gem.gemType);
}
}
}
void HandleSwipe()
{
Vector2 swipeDirection = endTouchPosition - startTouchPosition;
swipeDirection.Normalize();
// Determine swipe direction
if (Mathf.Abs(swipeDirection.x) > Mathf.Abs(swipeDirection.y))
{
if (swipeDirection.x > 0)
{
Debug.Log("Swipe Right");
}
else
{
Debug.Log("Swipe Left");
}
}
else
{
if (swipeDirection.y > 0)
{
Debug.Log("Swipe Up");
}
else
{
Debug.Log("Swipe Down");
}
}
}
}
// LevelManager.cs - Manages different levels and difficulty
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class LevelData
{
public int levelNumber;
public int targetScore;
public int movesAllowed;
public int gemTypes;
public Color backgroundColor;
}
public class LevelManager : MonoBehaviour
{
public List levels;
public int currentLevel = 0;
public LevelData GetCurrentLevel()
{
if (currentLevel < levels.Count)
return levels[currentLevel];
// Generate endless levels
LevelData endlessLevel = new LevelData
{
levelNumber = currentLevel + 1,
targetScore = 1000 + (currentLevel * 500),
movesAllowed = 30 - (currentLevel / 5),
gemTypes = Mathf.Min(6, 4 + (currentLevel / 3)),
backgroundColor = Color.HSVToRGB((currentLevel * 0.1f) % 1f, 0.3f, 0.8f)
};
return endlessLevel;
}
public void NextLevel()
{
currentLevel++;
}
public void ResetToLevel(int level)
{
currentLevel = level;
}
}
// PowerUp.cs - Special gem power-ups
using UnityEngine;
using System.Collections;
public enum PowerUpType
{
Bomb,
LineHorizontal,
LineVertical,
Rainbow
}
public class PowerUp : MonoBehaviour
{
public PowerUpType powerUpType;
public GameObject explosionPrefab;
public void ActivatePowerUp(int gridX, int gridY, GameManager gameManager)
{
switch (powerUpType)
{
case PowerUpType.Bomb:
ActivateBomb(gridX, gridY, gameManager);
break;
case PowerUpType.LineHorizontal:
ActivateLineHorizontal(gridY, gameManager);
break;
case PowerUpType.LineVertical:
ActivateLineVertical(gridX, gameManager);
break;
case PowerUpType.Rainbow:
ActivateRainbow(gameManager);
break;
}
}
void ActivateBomb(int centerX, int centerY, GameManager gameManager)
{
for (int x = centerX - 1; x <= centerX + 1; x++)
{
for (int y = centerY - 1; y <= centerY + 1; y++)
{
if (x >= 0 && x < gameManager.gridWidth && y >= 0 && y < gameManager.gridHeight)
{
if (gameManager.grid[x, y] != null)
{
if (explosionPrefab)
{
Instantiate(explosionPrefab, gameManager.grid[x, y].transform.position, Quaternion.identity);
}
Destroy(gameManager.grid[x, y].gameObject);
gameManager.grid[x, y] = null;
}
}
}
}
}
void ActivateLineHorizontal(int row, GameManager gameManager)
{
for (int x = 0; x < gameManager.gridWidth; x++)
{
if (gameManager.grid[x, row] != null)
{
if (explosionPrefab)
{
Instantiate(explosionPrefab, gameManager.grid[x, row].transform.position, Quaternion.identity);
}
Destroy(gameManager.grid[x, row].gameObject);
gameManager.grid[x, row] = null;
}
}
}
void ActivateLineVertical(int column, GameManager gameManager)
{
for (int y = 0; y < gameManager.gridHeight; y++)
{
if (gameManager.grid[column, y] != null)
{
if (explosionPrefab)
{
Instantiate(explosionPrefab, gameManager.grid[column, y].transform.position, Quaternion.identity);
}
Destroy(gameManager.grid[column, y].gameObject);
gameManager.grid[column, y] = null;
}
}
}
void ActivateRainbow(GameManager gameManager)
{
// Destroy all gems of the most common type
int[] gemCounts = new int[6];
for (int x = 0; x < gameManager.gridWidth; x++)
{
for (int y = 0; y < gameManager.gridHeight; y++)
{
if (gameManager.grid[x, y] != null)
{
gemCounts[gameManager.grid[x, y].gemType]++;
}
}
}
int mostCommonType = 0;
int maxCount = 0;
for (int i = 0; i < gemCounts.Length; i++)
{
if (gemCounts[i] > maxCount)
{
maxCount = gemCounts[i];
mostCommonType = i;
}
}
for (int x = 0; x < gameManager.gridWidth; x++)
{
for (int y = 0; y < gameManager.gridHeight; y++)
{
if (gameManager.grid[x, y] != null && gameManager.grid[x, y].gemType == mostCommonType)
{
if (explosionPrefab)
{
Instantiate(explosionPrefab, gameManager.grid[x, y].transform.position, Quaternion.identity);
}
Destroy(gameManager.grid[x, y].gameObject);
gameManager.grid[x, y] = null;
}
}
}
}
}