Couple of questions on my Match 3 grid class
Hi,
First, i'm new to unity and try to learn how to use it with a Match 3 game. I've learned how to code a class to create the grid, fill it and make matches with my pieces.
Now, i'm trying to extend it with some features :
Fill the grid even if there are ROCK or NONE pieces on top (they can't be cleared);
Make a NoMoreMatches function to know if there is still even one match available on the grid.
Here is my Grid Class. Can someone tell me how to do this by extending my current code ?
Thanks :)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Grid : MonoBehaviour {
public enum PieceType
{
EMPTY,
NORMAL,
ICE,
ROW_CLEAR,
COLUMN_CLEAR,
RAINBOW,
NONE,
ROCK,
COUNT,
};
[System.Serializable]
public struct PiecePrefab
{
public PieceType type;
public GameObject prefab;
};
[System.Serializable]
public struct PiecePosition
{
public PieceType type;
public int x;
public int y;
};
// Grid size
public int xDim;
public int yDim;
// Time to fill/refill the grid
public float fillTime;
// Used to call the level class
public Level level;
// Array of piece types that can be defined in the inspector
public PiecePrefab[] piecePrefabs;
// GameObject to set a background for our pieces
public GameObject backgroundPrefab;
// Used to set our initial pieces in the inspector, for each level
public PiecePosition[] initialPieces;
// Dictionary for attaching a piece to its prefab
private Dictionary<PieceType, GameObject> piecePrefabDict;
// 2D array to contain all our pieces
private GamePiece[,] pieces;
// Used to move pieces in diag (for filling a grid with obstacles)
private bool inverse = false;
// Used to know wich pieces will be swapped
private GamePiece pressedPiece;
private GamePiece enteredPiece;
// Set the game over. Doesn't mean that the player loose
private bool gameOver = false;
// Use this for initialization
void Awake () {
piecePrefabDict = new Dictionary<PieceType, GameObject> ();
// First we need to check if we have keys in our dictionary. If not, add them
for (int i = 0; i < piecePrefabs.Length; i++) {
if (!piecePrefabDict.ContainsKey (piecePrefabs [i].type)) {
piecePrefabDict.Add (piecePrefabs [i].type, piecePrefabs [i].prefab);
}
}
// Instantiate the background for all our pieces on the grid
for (int x = 0; x < xDim; x++) {
for (int y = 0; y < yDim; y++) {
GameObject background = (GameObject)Instantiate (backgroundPrefab, GetWorldPosition(x, y), Quaternion.identity);
background.transform.parent = transform;
}
}
// Now we need to add our initial and empty pieces on the grid
pieces = new GamePiece[xDim, yDim];
for (int i = 0; i < initialPieces.Length; i++) {
if (initialPieces [i].x >= 0 && initialPieces [i].x < xDim
&& initialPieces [i].y >= 0 && initialPieces [i].y < yDim) {
SpawnNewPiece (initialPieces [i].x, initialPieces [i].y, initialPieces [i].type);
}
}
for (int x = 0; x < xDim; x++) {
for (int y = 0; y < yDim; y++) {
if (pieces [x, y] == null) {
SpawnNewPiece (x, y, PieceType.EMPTY);
}
}
}
// Now the grid is filled with empty pieces, we need to replace them by our non-empty pieces with animation
StartCoroutine(Fill ());
}
// Update is called once per frame
void Update () {
}
// Fill the grid
public IEnumerator Fill()
{
bool needsRefill = true;
while (needsRefill) {
yield return new WaitForSeconds (fillTime);
while (FillStep ()) {
inverse = !inverse;
yield return new WaitForSeconds (fillTime);
}
needsRefill = ClearAllValidMatches ();
}
}
// Move a piece to fill the grid
// Todo : add a check for ROCK Pieces so that they'll be ignored when filling the grid as they can't be destroyed and can be on top of the grid
public bool FillStep()
{
bool movedPiece = false;
for (int y = yDim-2; y >= 0; y--)
{
for (int loopX = 0; loopX < xDim; loopX++)
{
int x = loopX;
if (inverse) {
x = xDim - 1 - loopX;
}
GamePiece piece = pieces [x, y];
if (piece.IsMovable ())
{
GamePiece pieceBelow = pieces [x, y + 1];
if (pieceBelow.Type == PieceType.EMPTY) {
Destroy (pieceBelow.gameObject);
piece.MovableComponent.Move (x, y + 1, fillTime);
pieces [x, y + 1] = piece;
SpawnNewPiece (x, y, PieceType.EMPTY);
movedPiece = true;
} else {
for (int diag = -1; diag <= 1; diag++)
{
if (diag != 0)
{
int diagX = x + diag;
if (inverse)
{
diagX = x - diag;
}
if (diagX >= 0 && diagX < xDim)
{
GamePiece diagonalPiece = pieces [diagX, y + 1];
if (diagonalPiece.Type == PieceType.EMPTY)
{
bool hasPieceAbove = true;
for (int aboveY = y; aboveY >= 0; aboveY--)
{
GamePiece pieceAbove = pieces [diagX, aboveY];
if (pieceAbove.IsMovable ())
{
break;
}
else if(!pieceAbove.IsMovable() && pieceAbove.Type != PieceType.EMPTY)
{
hasPieceAbove = false;
break;
}
}
if (!hasPieceAbove)
{
Destroy (diagonalPiece.gameObject);
piece.MovableComponent.Move (diagX, y + 1, fillTime);
pieces [diagX, y + 1] = piece;
SpawnNewPiece (x, y, PieceType.EMPTY);
movedPiece = true;
break;
}
}
}
}
}
}
}
}
}
for (int x = 0; x < xDim; x++)
{
GamePiece pieceBelow = pieces [x, 0];
if (pieceBelow.Type == PieceType.EMPTY)
{
Destroy (pieceBelow.gameObject);
GameObject newPiece = (GameObject)Instantiate(piecePrefabDict[PieceType.NORMAL], GetWorldPosition(x, -1), Quaternion.identity);
newPiece.transform.parent = transform;
pieces [x, 0] = newPiece.GetComponent<GamePiece> ();
pieces [x, 0].Init (x, -1, this, PieceType.NORMAL);
pieces [x, 0].MovableComponent.Move (x, 0, fillTime);
pieces [x, 0].ColorComponent.SetColor ((ColorPiece.ColorType)Random.Range (0, pieces [x, 0].ColorComponent.NumColors));
movedPiece = true;
}
}
return movedPiece;
}
// Used to get the offset of our pieces
public Vector2 GetWorldPosition(int x, int y)
{
return new Vector2 (transform.position.x - xDim / 2.0f + x,
transform.position.y + yDim / 2.0f - y);
}
// Spawn a new piece
public GamePiece SpawnNewPiece(int x, int y, PieceType type)
{
GameObject newPiece = (GameObject)Instantiate (piecePrefabDict [type], GetWorldPosition (x, y), Quaternion.identity);
newPiece.transform.parent = transform;
pieces [x, y] = newPiece.GetComponent<GamePiece> ();
pieces [x, y].Init (x, y, this, type);
return pieces [x, y];
}
// Used to know if piece1 is adjacent to piece2
public bool IsAdjacent(GamePiece piece1, GamePiece piece2)
{
return (piece1.X == piece2.X && (int)Mathf.Abs (piece1.Y - piece2.Y) == 1)
|| (piece1.Y == piece2.Y && (int)Mathf.Abs (piece1.X - piece2.X) == 1);
}
// Swap pieces if they are adjacent
public void SwapPieces(GamePiece piece1, GamePiece piece2)
{
// If the game is over, just disabled this function
if (gameOver) {
return;
}
// Swap only if they are movable
if (piece1.IsMovable () && piece2.IsMovable ()) {
pieces [piece1.X, piece1.Y] = piece2;
pieces [piece2.X, piece2.Y] = piece1;
if (GetMatch (piece1, piece2.X, piece2.Y) != null || GetMatch (piece2, piece1.X, piece1.Y) != null
|| piece1.Type == PieceType.RAINBOW || piece2.Type == PieceType.RAINBOW) {
int piece1X = piece1.X;
int piece1Y = piece1.Y;
// Make some animation while swaping
piece1.MovableComponent.Move (piece2.X, piece2.Y, fillTime);
piece2.MovableComponent.Move (piece1X, piece1Y, fillTime);
// Clear the Rainbow piece manually before clearing matches
if (piece1.Type == PieceType.RAINBOW && piece1.IsClearable () && piece2.IsColored ()) {
ClearColorPiece clearColor = piece1.GetComponent<ClearColorPiece> ();
if (clearColor) {
clearColor.Color = piece2.ColorComponent.Color;
}
ClearPiece (piece1.X, piece1.Y);
}
if (piece2.Type == PieceType.RAINBOW && piece2.IsClearable () && piece1.IsColored ()) {
ClearColorPiece clearColor = piece2.GetComponent<ClearColorPiece> ();
if (clearColor) {
clearColor.Color = piece1.ColorComponent.Color;
}
ClearPiece (piece2.X, piece2.Y);
}
// Now we need to clear all pieces from this match
ClearAllValidMatches ();
if (piece1.Type == PieceType.ROW_CLEAR || piece1.Type == PieceType.COLUMN_CLEAR) {
ClearPiece (piece1.X, piece1.Y);
}
if (piece2.Type == PieceType.ROW_CLEAR || piece2.Type == PieceType.COLUMN_CLEAR) {
ClearPiece (piece2.X, piece2.Y);
}
pressedPiece = null;
enteredPiece = null;
// And finally refilling the board
StartCoroutine (Fill ());
} else {
// No match, return pieces to their own positions
pieces [piece1.X, piece1.Y] = piece1;
pieces [piece2.X, piece2.Y] = piece2;
}
}
}
// Assign piece when piece is pressed
public void PressPiece(GamePiece piece)
{
pressedPiece = piece;
}
// Assign piece when piece is entered
public void EnterPiece(GamePiece piece)
{
enteredPiece = piece;
}
// Swap pieces when piece is released
public void ReleasePiece()
{
if (IsAdjacent (pressedPiece, enteredPiece)) {
SwapPieces (pressedPiece, enteredPiece);
// Todo: uncomment this when the NoMoreMatches is written
/* if (NoMoreMatches ()) {
Debug.Log ("No More Matches, shuffle the grid");
Shuffle ();
}*/
}
}
// Used to know if pieces can match
public List<GamePiece> GetMatch(GamePiece piece, int newX, int newY)
{
if (piece.IsColored ()) {
ColorPiece.ColorType color = piece.ColorComponent.Color;
List<GamePiece> horizontalPieces = new List<GamePiece> ();
List<GamePiece> verticalPieces = new List<GamePiece> ();
List<GamePiece> matchingPieces = new List<GamePiece> ();
// First check horizontal
horizontalPieces.Add(piece);
for (int dir = 0; dir <= 1; dir++) {
for (int xOffset = 1; xOffset < xDim; xOffset++) {
int x;
if (dir == 0) { // Left
x = newX - xOffset;
} else { // Right
x = newX + xOffset;
}
if (x < 0 || x >= xDim) {
break;
}
if (pieces [x, newY].IsColored () && pieces [x, newY].ColorComponent.Color == color) {
horizontalPieces.Add (pieces [x, newY]);
} else {
break;
}
}
}
if (horizontalPieces.Count >= 3) {
for (int i = 0; i < horizontalPieces.Count; i++) {
matchingPieces.Add (horizontalPieces [i]);
}
}
// Traverse vertically if we found a match (for L and T shapes)
if (horizontalPieces.Count >= 3) {
for (int i = 0; i < horizontalPieces.Count; i++) {
for (int dir = 0; dir <= 1; dir++) {
for (int yOffset = 1; yOffset < yDim; yOffset++) {
int y;
if (dir == 0) { // Up
y = newY - yOffset;
} else { // Down
y = newY + yOffset;
}
if (y < 0 || y >= yDim) {
break;
}
if (pieces [horizontalPieces [i].X, y].IsColored () && pieces [horizontalPieces [i].X, y].ColorComponent.Color == color) {
verticalPieces.Add (pieces [horizontalPieces [i].X, y]);
} else {
break;
}
}
}
if (verticalPieces.Count < 2) {
verticalPieces.Clear ();
} else {
for (int j = 0; j < verticalPieces.Count; j++) {
matchingPieces.Add (verticalPieces [j]);
}
break;
}
}
}
if (matchingPieces.Count >= 3) {
return matchingPieces;
}
// Didn't find anything going horizontally first,
// so now check vertically
horizontalPieces.Clear();
verticalPieces.Clear ();
verticalPieces.Add(piece);
for (int dir = 0; dir <= 1; dir++) {
for (int yOffset = 1; yOffset < yDim; yOffset++) {
int y;
if (dir == 0) { // Up
y = newY - yOffset;
} else { // Down
y = newY + yOffset;
}
if (y < 0 || y >= yDim) {
break;
}
if (pieces [newX, y].IsColored () && pieces [newX, y].ColorComponent.Color == color) {
verticalPieces.Add (pieces [newX, y]);
} else {
break;
}
}
}
if (verticalPieces.Count >= 3) {
for (int i = 0; i < verticalPieces.Count; i++) {
matchingPieces.Add (verticalPieces [i]);
}
}
// Traverse horizontally if we found a match (for L and T shapes)
if (verticalPieces.Count >= 3) {
for (int i = 0; i < verticalPieces.Count; i++) {
for (int dir = 0; dir <= 1; dir++) {
for (int xOffset = 1; xOffset < xDim; xOffset++) {
int x;
if (dir == 0) { // Left
x = newX - xOffset;
} else { // Right
x = newX + xOffset;
}
if (x < 0 || x >= xDim) {
break;
}
if (pieces [x, verticalPieces[i].Y].IsColored () && pieces [x, verticalPieces[i].Y].ColorComponent.Color == color) {
horizontalPieces.Add (pieces [x, verticalPieces[i].Y]);
} else {
break;
}
}
}
if (horizontalPieces.Count < 2) {
horizontalPieces.Clear ();
} else {
for (int j = 0; j < horizontalPieces.Count; j++) {
matchingPieces.Add (horizontalPieces [j]);
}
break;
}
}
}
if (matchingPieces.Count >= 3) {
return matchingPieces;
}
}
return null;
}
// Used to know if there is still valid matches on the grid
public bool NoMoreMatches()
{
bool noMoreMatches = false;
// Todo: write this function to check if there is still one possible match on the grid
return noMoreMatches;
}
// Clear all matches for one player move
public bool ClearAllValidMatches()
{
bool needsRefill = false;
for (int y = 0; y < yDim; y++) {
for (int x = 0; x < xDim; x++) {
if (pieces [x, y].IsClearable ()) {
List<GamePiece> match = GetMatch (pieces [x, y], x, y);
if (match != null) {
// Start Special piece spawning
PieceType specialPieceType = PieceType.COUNT;
GamePiece randomPiece = match [Random.Range (0, match.Count)];
int specialPieceX = randomPiece.X;
int specialPieceY = randomPiece.Y;
if (match.Count == 4) {
if (pressedPiece == null || enteredPiece == null) {
specialPieceType = (PieceType)Random.Range ((int)PieceType.ROW_CLEAR, (int)PieceType.COLUMN_CLEAR);
} else if (pressedPiece.Y == enteredPiece.Y) {
specialPieceType = PieceType.ROW_CLEAR;
} else {
specialPieceType = PieceType.COLUMN_CLEAR;
}
} else if (match.Count >= 5) {
specialPieceType = PieceType.RAINBOW;
}
// End Special piece spawning
for (int i = 0; i < match.Count; i++) {
if (ClearPiece (match [i].X, match [i].Y)) {
needsRefill = true;
if (match [i] == pressedPiece || match [i] == enteredPiece) {
specialPieceX = match [i].X;
specialPieceY = match [i].Y;
}
}
}
// Spawn the special piece
if (specialPieceType != PieceType.COUNT) {
Destroy (pieces [specialPieceX, specialPieceY]);
GamePiece newPiece = SpawnNewPiece (specialPieceX, specialPieceY, specialPieceType);
if ((specialPieceType == PieceType.ROW_CLEAR || specialPieceType == PieceType.COLUMN_CLEAR)
&& newPiece.IsColored () && match [0].IsColored ()) {
newPiece.ColorComponent.SetColor(match[0].ColorComponent.Color);
} else if (specialPieceType == PieceType.RAINBOW && newPiece.IsColored ()) {
newPiece.ColorComponent.SetColor (ColorPiece.ColorType.ANY);
}
}
}
}
}
}
return needsRefill;
}
// Clear a piece and respawn an empty piece
public bool ClearPiece(int x, int y)
{
if (pieces [x, y].IsClearable () && !pieces [x, y].ClearableComponent.IsBeingCleared) {
pieces [x, y].ClearableComponent.Clear ();
SpawnNewPiece (x, y, PieceType.EMPTY);
ClearObstacles (x, y);
return true;
}
return false;
}
// Used to clear Ice pieces
public void ClearObstacles(int x, int y)
{
for (int adjacentX = x - 1; adjacentX <= x + 1; adjacentX++) {
if (adjacentX != x && adjacentX >= 0 && adjacentX < xDim) {
if (pieces [adjacentX, y].Type == PieceType.ICE && pieces [adjacentX, y].IsClearable ()) {
pieces [adjacentX, y].ClearableComponent.Clear ();
SpawnNewPiece (adjacentX, y, PieceType.EMPTY);
}
}
}
for (int adjacentY = y - 1; adjacentY <= y + 1; adjacentY++) {
if (adjacentY != y && adjacentY >= 0 && adjacentY < yDim) {
if (pieces [x, adjacentY].Type == PieceType.ICE && pieces [x, adjacentY].IsClearable ()) {
pieces [x, adjacentY].ClearableComponent.Clear ();
SpawnNewPiece (x, adjacentY, PieceType.EMPTY);
}
}
}
}
// Clear entire row
public void ClearRow(int row)
{
for (int x = 0; x < xDim; x++) {
ClearPiece (x, row);
}
}
// Clear entire column
public void ClearColumn(int column)
{
for (int y = 0; y < yDim; y++) {
ClearPiece (column, y);
}
}
// Clear all pieces with the same color
public void ClearColor(ColorPiece.ColorType color)
{
for (int x = 0; x < xDim; x++) {
for (int y = 0; y < yDim; y++) {
if (pieces [x, y].IsColored () && (pieces [x, y].ColorComponent.Color == color
|| color == ColorPiece.ColorType.ANY)) {
ClearPiece (x, y);
}
}
}
}
// Set the game over so the current level ends
public void GameOver()
{
gameOver = true;
}
// Returns a list of pieces on the grid with the given type parameter
public List<GamePiece> GetPiecesOfType(PieceType type)
{
List<GamePiece> piecesOfType = new List<GamePiece> ();
for (int x = 0; x < xDim; x++) {
for (int y = 0; y < yDim; y++) {
if (pieces [x, y].Type == type) {
piecesOfType.Add (pieces [x, y]);
}
}
}
return piecesOfType;
}
// Shuffle the grid
public void Shuffle()
{
for (int x = 0; x < xDim; x++) {
for (int y = 0; y < yDim; y++) {
// We only need to shuffle movable pieces
if (pieces [x, y].IsMovable ()) {
pieces [x, y].ColorComponent.SetColor((ColorPiece.ColorType)Random.Range(0, 5));
}
}
}
}
}
Your answer
Follow this Question
Related Questions
C# Unity dot syntax exercise correct solution? 1 Answer
how do i keep an item destroyed when i change the scene c# 0 Answers
Dreamlo Leaderbord issue 0 Answers
please help me, i'm creating a first person game, but my script is incomplete and i can't crouch 0 Answers
Having trouble deleting objects from a list after they reach a certain scale. 0 Answers