Hey, welcome to the second part of the this amazing tutorial, I hope you enjoyed the first part but now it’s time to move forward and develop all the logic to move the cells inside our beautiful grid.
This is part 2 of the tutorial on How to Create a match 3 game in Unity, here you can find the links to other parts:
- Part 1: Setting up the project
- Part 2: Move cells
- Part 3: Identify matches
- Part 4: Add GUI and sounds
Selecting cells
We created our grid, now we have to make it interactive! What we are going to do now is to give the possibility to select the cells with a click, and swap them with a neighboring cell by clicking on it. If you click on a non-adjacent cell, the currently selected one will be deselected, the new one selected without any swap. We must provide feedback to the user to show if a cell is checked or not, to make it change the color of the selected cell.
Let’s create a script, call it Tile and write the following:
private static Tile selected; // 1 private SpriteRenderer Renderer; // 2 private void Start() // 3 { Renderer = GetComponent<SpriteRenderer>(); } public void Select() // 4 { Renderer.color = Color.grey; } public void Unselect() // 5 { Renderer.color = Color.white; } private void OnMouseDown() //6 { if(selected != null) { selected.Unselect(); } selected = this; Select(); }
The lines we added deal with the selection, as we said a little while ago. In particular:
- Declare a static variable to store the currently selected tile. A static variable means that it is shared among all instances of a class, so for each tile, that variable will always have the same value.
- Declare a variable to store the TileRenderer of this cell. We will need it to change the color of the cell.
- By means of a GetComponent we store the reference in the variable declared in the previous point. This caching technique will allow us to avoid calling GetComponent every time, which is considered a heavy operation and can slow down our game.
- This function is called when you want to check the current cell. Let’s just change the color of the current tile, making it turn gray. This color will add to the main color, making the cell darker.
- Since there is a function for selecting, we have also added a function for deselecting, which will restore the original color of the cell.
- This feature is special, as it is automatically called by Unity for us. When we click on a cell, Unity notices it and takes care of calling the OnMouseDown in all the components of the GameObject concerned.
We must now add this component to the cells when they are created, so let’s go back to GridManager.cs and add the bold line inside the InitGrid function:
SpriteRenderer renderer = newTile.GetComponent<SpriteRenderer>(); renderer.sprite = possibleSprites[Random.Range(0, possibleSprites.Count)]; Tile tile = newTile.AddComponent<Tile>(); newTile.transform.parent = transform;
Let’s save and go back to Unity. To our disappointment, you will notice that clicking on the grid will not do anything: the cells will not be checked. The reason is that in order for this mechanism to work, we need a collider that can intercept events like mouse clicks.
Let’s add a new component to the Tile prefab:
- Double click on the prefab to open it. By double-clicking on a prefab, it will be opened showing its content so that you can modify it. Add here the collider and it will be replicated in all the cells.
- Add a 2D Collider component.
Do not worry about the warning, enter the following values and the collider will be configured correctly:
In the image I temporarily added a Sprite to the SpriteRenderer to make sure that the collider, the green square around the image, matched. If you do too, remember to remove it before exiting the prefab mode.
Now that the collider is present, Unity is able to recognize our clicks and call the OnMouseDown function that we wrote earlier.
Cells swapping
Now that the cells are selectable, let’s continue on our path to be able to move them. In order to do this, we must verify that the two cells are adjacent, if this happens we can perform the exchange of sprites and uncheck the cell. If it is not verified, we check the new cell without doing anything.
Let’s start by setting up the GridManager to move the cells. Let’s create a function that does exactly this, which takes the position of two cells as parameters:
public void SwapTiles(Vector2Int tile1Position, Vector2Int tile2Position) // 1 { // 2 GameObject tile1 = Grid[tile1Position.x, tile1Position.y]; SpriteRenderer renderer1 = tile1.GetComponent<SpriteRenderer>(); GameObject tile2 = Grid[tile2Position.x, tile2Position.y]; SpriteRenderer renderer2 = tile2.GetComponent<SpriteRenderer>(); // 3 Sprite temp = renderer1.sprite; renderer1.sprite = renderer2.sprite; renderer2.sprite = temp; }
- To represent the positions and take advantage of some convenient functions as regards the vectors, we use Vector2Int objects, which are nothing more than an object with two integer fields called x and y, therefore perfect for representing the row + column indices!
- Get the SpriteRenderer of the two cells
- Carry out the exchange
To call the swap function, we will need the GridManager instance. A very simple method to make the instance available globally at any point in the code is to use the Singleton pattern, which solves the problem through a static variable. To implement the pattern, let’s add the following lines of code before the Start() function: public static GridManager Instance { get; private set; } void Awake() { Instance = this; }
What does this mean in practice? Soon, within the Tile.cs script, we will need the instance of the GridManager, and to obtain it we will only need to use GridManager.Instance to obtain it.
For more information on the Singleton pattern, its pros and cons, I recommend reading the related page on Wikipedia. Use this pattern carefully, since it can be very dangerous if used badly.
Now that we can exchange cells, we need to store their location information in these cells. To do this we must add the information on its position on the grid to the Tile.cs script so that when a cell receives a click signal it can eventually notify the GridManager to make the exchange.
So let’s add the bold line:
private SpriteRenderer Renderer; public Vector2Int Position; private void Start() { ...
We are going to set this property shortly, but for now, we remain on Tile.cs and modify the OnMouseDown function as follows:
private void OnMouseDown() { if (selected != null) { if (selected == this) return; selected.Unselect(); if (Vector2Int.Distance(selected.Position, Position) == 1) { GridManager.Instance.SwapTiles(Position, selected.Position); selected = null; } else { selected = this; Select(); } } else { selected = this; Select(); } }
At first, let’s check if the selected attribute is different from null. If it is null, we check the current cell and return. Otherwise, we make sure that the cell that has just been clicked is not the one selected, in which case we do nothing. Otherwise, let’s start by unchecking the cell, and then check if the two cells are adjacent: to do this, we use the Distance function, which does exactly what the name says: if the distance is not 1, we select the current cell and we do nothing else.
If the distance between the two cells is one, then we fall back in the case we are interested in, so we advise the GridManager that it is time to make the swap by calling the SwapTiles function, passing the positions of the two cells as a parameter, and reset the reference to the cell selected.
The last thing we need to do is to let the cell know its position on the grid. This will be done by the GridManager when creating the cells. We then open the GridManager again, and in the InitGrid function we add this row just after adding the Tile component:
Tile tile = newTile.AddComponent<Tile>(); tile.Position = new Vector2Int(column, row); newTile.transform.parent = transform;
Now we can save both files, come back in Unity and press play to see our cells being swapped!
Match 3 game in Unity: The Conclusions of part 2
In this tutorial, we have seen how to manage clicks of the user and change the grid accordingly. In the next part we will learn how to identify matches and delete cells, make the upper cells fall down and refill the holes formed at the top. In the meantime think about this algorithm, how would you implement it? Try it yourself, then compare with the solution of the next part!
If you like our work, please share this tutorial with your friends and subscribe to our newsletter to get notified when new tutorials are published!
Hi,
I had trouble swapping the tiles because i think the onmousedown event is triggered twice instead of once(found out that my logs are doubling with every click). Do you know what are the possible solutions ?
hey,
in the code
SpriteRenderer renderer = newTile.GetComponent();
renderer.sprite = possibleSprites[Random.Range(0, possibleSprites.Count)];
Tile tile = newTile.AddComponent();
newTile.transform.parent = transform;
we have not defined newTile & possibleSprites. This will give an error
Even if both the tiles are attached to the a same object
I get a index out of bounds issue when trying to swop the tiles
Great tutorial… its simple and easy to follow and understand