Hello,
I’m trying to make a match 3 game similar to Toon Blast. I added a functionality to the script, which shuffles the dots on the board when there are no more matches left. The shuffling works but it’s broken. When the board is shuffled some of the dots go behind the dots standing above or next to them thus leaving blanks on the board. Like this:
I’m not sure what the source of the problem is. This is the Board script where shuffle method is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum GameState
{
wait,
move
}
public enum TileKind
{
Breakable,
Blank,
Normal
}
[System.Serializable]
public class TileType
{
public int x;
public int y;
public TileKind tileKind;
}
public class Board : MonoBehaviour
{
public GameState currentState = GameState.move;
public int width;
public int height;
public int offSet;
public GameObject tilePrefab;
public GameObject[] dots;
public GameObject destroyEffect;
public TileType[] boardLayout;
private bool[,] blankSpaces;
public GameObject[,] allDots;
// Start is called before the first frame update
void Start()
{
blankSpaces = new bool[width, height];
allDots = new GameObject[width, height];
SetUp();
}
private void SetUp()
{
GenerateBlankSpaces();
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (!blankSpaces[i, j])
{
Vector2 tempPosition = new Vector2(i, j + offSet);
GameObject backgroundTile = Instantiate(tilePrefab, tempPosition, Quaternion.identity) as GameObject;
backgroundTile.transform.parent = this.transform;
backgroundTile.name = "(" + i + ", " + j + ")";
int dotToUse = Random.Range(0, dots.Length);
GameObject dot = Instantiate(dots[dotToUse], tempPosition, Quaternion.identity);
dot.GetComponent<Dot>().row = j;
dot.GetComponent<Dot>().column = i;
dot.transform.parent = this.transform;
dot.name = "(" + i + ", " + j + ")";
allDots[i, j] = dot;
dot.GetComponent<Dot>().board = this;
}
}
}
}
public void GenerateBlankSpaces()
{
for (int i = 0; i < boardLayout.Length; i++)
{
if (boardLayout[i].tileKind == TileKind.Blank)
{
blankSpaces[boardLayout[i].x, boardLayout[i].y] = true;
}
}
}
private void DestroyMatchesAt(int column, int row)
{
if (allDots[column, row].GetComponent<Dot>().isMatched)
{
GameObject particle = Instantiate(destroyEffect, allDots[column, row].transform.position, Quaternion.identity);
Destroy(particle, .5f);
Destroy(allDots[column, row]);
allDots[column, row] = null;
}
}
public void DestroyMatches()
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (allDots[i, j] != null)
{
DestroyMatchesAt(i, j);
}
}
}
StartCoroutine(DecreaseRowCo2());
}
private IEnumerator DecreaseRowCo2()
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
//if the current spot isn't blank and is empty...
if (!blankSpaces[i, j] && allDots[i, j] == null)
{
//loop from the space above to the top of the column
for (int k = j + 1; k < height; k++)
{
//if a dot is found...
if (allDots[i, k] != null)
{
//move that dot to this empty space
allDots[i, k].GetComponent<Dot>().row = j;
//set that spot to be null
allDots[i, k] = null;
//break out of the loop
break;
}
}
}
}
}
yield return new WaitForSeconds(.4f);
StartCoroutine(FillBoardCo());
}
private IEnumerator DecreaseRowCo()
{
int nullCount = 0;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (allDots[i, j] == null)
{
nullCount++;
}
else if (nullCount > 0)
{
allDots[i, j].GetComponent<Dot>().row -= nullCount;
allDots[i, j] = null;
}
}
nullCount = 0;
}
yield return new WaitForSeconds(.4f);
StartCoroutine(FillBoardCo());
}
private void RefillBoard()
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (allDots[i, j] == null && !blankSpaces[i, j])
{
Vector2 tempPosition = new Vector2(i, height - 1 + offSet);
int dotToUse = Random.Range(0, dots.Length);
GameObject piece = Instantiate(dots[dotToUse], tempPosition, Quaternion.identity);
allDots[i, j] = piece;
piece.GetComponent<Dot>().row = j;
piece.GetComponent<Dot>().column = i;
}
}
}
}
private bool MatchesOnBoard()
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (allDots[i, j] != null)
{
if (allDots[i, j].GetComponent<Dot>().isMatched)
{
return true;
}
}
}
}
return false;
}
private IEnumerator FillBoardCo()
{
RefillBoard();
yield return new WaitForSeconds(.5f);
while (MatchesOnBoard())
{
yield return new WaitForSeconds(.5f);
DestroyMatches();
}
yield return new WaitForSeconds(.5f);
if (IsDeadLocked())
{
ShuffleBoard();
Debug.Log("Deadlocked");
}
currentState = GameState.move;
}
private bool CheckForMatches()
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (allDots[i, j] != null)
{
//Make sure that one dot to the right is in the board
if (i < width - 1)
{
//Check if the dots to the right exist
if (allDots[i + 1, j] != null)
{
if (allDots[i + 1, j].tag == allDots[i, j].tag)
{
return true;
}
}
}
if (j < height - 1)
{
//Check if the dots above exist
if (allDots[i, j + 1] != null)
{
if (allDots[i, j + 1].tag == allDots[i, j].tag)
{
return true;
}
}
}
}
}
}
return false;
}
private bool IsDeadLocked()
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (allDots[i, j] != null)
{
if (CheckForMatches())
{
return false;
}
}
}
}
return true;
}
private void ShuffleBoard()
{
//Create a list of game objects
List<GameObject> newBoard = new List<GameObject>();
//Add every piece to this list
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
if (allDots[i, j] != null)
{
int randomIndex = Random.Range(0, newBoard.Count + 1);
newBoard.Insert(randomIndex, allDots[i, j]);
}
}
}
//For every spot on the board...
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
//If this spot shouldn't be blank
if (!blankSpaces[i, j])
{
//Pick random number
int pieceToUse = Random.Range(0, newBoard.Count);
//Make a container for the piece
Dot piece = newBoard[pieceToUse].GetComponent<Dot>();
//Assign the column to the piece
piece.column = i;
//Assing the row to the piece
piece.row = j;
//Fill in the dots array with this new piece
allDots[i, j] = newBoard[pieceToUse];
//Remove it from the list
newBoard.Remove(newBoard[pieceToUse]);
}
}
}
//Check if it's still deadlocked
if (IsDeadLocked())
{
ShuffleBoard();
}
}
}
And just in case i share the Dot script too bcs it’s where the matching and movement of the dots are handled:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dot : MonoBehaviour
{
public int column;
public int row;
public Board board;
public bool isMatched = false;
public int targetX;
public int targetY;
private Vector2 tempPosition;
// Start is called before the first frame update
void Start()
{
board = FindAnyObjectByType<Board>();
}
// Update is called once per frame
void Update()
{
targetX = column;
targetY = row;
if (Mathf.Abs(targetY - transform.position.y) > .1)
{
//Move Towards the target
tempPosition = new Vector2(transform.position.x, targetY);
transform.position = Vector2.Lerp(transform.position, tempPosition, .1f);
if (board.allDots[column, row] != this.gameObject)
{
board.allDots[column, row] = this.gameObject;
}
}
else
{
//Directly set the position
tempPosition = new Vector2(transform.position.x, targetY);
transform.position = tempPosition;
}
}
private void OnMouseDown()
{
if (board.currentState == GameState.move)
{
FindMatches();
board.DestroyMatches();
// Set the game state to wait
board.currentState = GameState.wait;
}
}
void FindMatches()
{
List<GameObject> matchingDots = new List<GameObject>();
if (column > 0)
{
GameObject leftDot = board.allDots[column - 1, row];
if (leftDot != null && leftDot.tag == this.gameObject.tag)
{
matchingDots.Add(leftDot);
}
}
if (column < board.width - 1)
{
GameObject rightDot = board.allDots[column + 1, row];
if (rightDot != null && rightDot.tag == this.gameObject.tag)
{
matchingDots.Add(rightDot);
}
}
if (row > 0)
{
GameObject upDot = board.allDots[column, row - 1];
if (upDot != null && upDot.tag == this.gameObject.tag)
{
matchingDots.Add(upDot);
}
}
if (row < board.height - 1)
{
GameObject downDot = board.allDots[column, row + 1];
if (downDot != null && downDot.tag == this.gameObject.tag)
{
matchingDots.Add(downDot);
}
}
if (matchingDots.Count > 0)
{
isMatched = true;
foreach (GameObject dot in matchingDots)
{
Dot dotScript = dot.GetComponent<Dot>();
if (!dotScript.isMatched)
{
dotScript.FindMatches();
}
}
}
else
{
isMatched = false;
}
}
}
I resorted ChatGPT but still couldn’t find what the problem is. Can somebody please help me?