Kodeco Forums

How to Make a Match 3 Game in Unity

Match 3 games are some of the most popular games on the market. From Candy Crush to Bejeweled, Match 3 games are enjoyed by many, and are part of a classic age-old genre of tile-matching games. In a Match 3 game, the goal is simple: swap pieces around till there’s 3 or more in a […]


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/673-how-to-make-a-match-3-game-in-unity

hi @jayfeesh : How multi device for ios: (iphone 6, iphone 7, ipad, ipad pro) …

Hi @hieulsw the tutorial uses mouse input, which Unity will by default read as touches on mobile or tablet devices. You can learn more about this default behaviour here: Unity - Scripting API: Input.simulateMouseWithTouches

i mean the screen is not correct. The tile is not show full in screen

it’s iphone 6 screen (resulotion: 750 x 1334 )

Hi @hieulsw,

To make everything fit on screen you can try scaling the pieces down, or take a column itself out and re-position the board. The board is quite dynamic, and can be re-sized to fit any resolution you’d like.

Cheers!
Jeff

Hi !

Thank you very much for this awesome tutorial.

I had a very small issue that prevented me from continuing the tutorial, in the “Finding Adjacent Tiles”.
The issue was that the object I clicked on didn’t detect the side objects, but instead detected only itself.

It came from a default setting in the Unity2D engine. To fix it, I had to go to “Edit → Project Settings → Physcis 2D” and CHECK OFF the “Queries Start in Colliders” box.

Thanks again for this great tutorial and website !

Hi @fatalbobo,

Glad you enjoyed the tutorial!

As for the issue, if you downloaded and used the Starter Project that setting should already be checked off for you. However, if you started a new Unity Project with the default project settings, then you will need to check that setting off.

Thanks for the friendly reminder :]

Hi @jayfeesh,

Thanks for the reply.

I’m trying to go further in this game but I’m strugling on something I thought I could do easily … I’m feeling super dumb for not finding how to … Maybe you could help me figure out how to do it ?

I’d like 2 of the characters (characters at index 0 and 1 for example) to act “super” and destroy all characters on the same line or column based on how they were aligned.
For example, if I align 3 Oranges, I want it to destroy not just the 3 aligned oranges, but the 3 plus all the other fruits on the same line or column.

I tried in vain for the last 2 days, so I come to you in shame to find an answer :frowning:

Thanks in advance if you could help me !

I did it ! :smiley:
Yeah !

Maybe there is a better solution for this, but here it is :slight_smile:

So I added a few lines in the ClearMatch() function.
First, I created a bool to tell me if the matchingTiles that I already have in my list are of the kinf of character I want.
Then if yes, I create a list of all the tiles in the game.
Then I check if their x or y are the same than the ones on my matchingTiles list.
Abd if so, I add them in the list of matching tiles.

private void ClearMatch(Vector2 paths) {
List matchingTiles = new List();
for (int i = 0; i < paths.Length; i++) {
matchingTiles.AddRange(FindMatch(paths[i]));
}

  if (matchingTiles.Count >= 2) {
  	bool isGood = false;
  	for (int i = 0; i < matchingTiles.Count; i++)
  		if (matchingTiles [i].GetComponent<SpriteRenderer> ().sprite == BoardManager.instance.characters [0] || matchingTiles [i].GetComponent<SpriteRenderer> ().sprite == BoardManager.instance.characters [1]) {
  			isGood = true;
  		} else
  			isGood = false;
  	if (isGood) {
  		Tile[] allTiles = FindObjectsOfType<Tile> ();
  		for (int i = 0; i < allTiles.Length; i++)
  			if (allTiles [i].transform.position.x == matchingTiles [0].transform.position.x && allTiles [i].transform.position.x == matchingTiles [1].transform.position.x) {
  				matchingTiles.Add (allTiles [i].gameObject);
  			} else if (allTiles [i].transform.position.y == matchingTiles [0].transform.position.y && allTiles [i].transform.position.y == matchingTiles [1].transform.position.y) {
  				matchingTiles.Add (allTiles [i].gameObject);
  			} else
  				continue;
  			
  	}
  	for (int i = 0; i < matchingTiles.Count; i++) {
  		matchFound = true;
  		matchingTiles[i].GetComponent<SpriteRenderer>().sprite = null;
  	}
  }

}

Yay !

Glad you found a solution! Nice work :]

Good day

Thanks for the wonderful tutorial, it really helps a lot. but I have noticed that if you match the uppermost line (top) in a horizontal way the tiles do not get refilled again and it creates an empty space. if you match in a vertical way the tile get refilled again…please help me with this as I have spent the whole weekend trying to figure out a solution.

after matching screenshot :

warm regards

Hi @gamedeveloper23!

Glad you enjoyed the tutorial. For a quick fix just increase the number of rows by +1 on the BoardManager. That way there will always be a row above the top visible row. Making sure it will always fill correctly!

Cheers!
Jeff

Hey again

Thank you so much for the quick response and solution, its working fine ,but I don’t think it is the best way to tackle this problem, because matches are now being made with the new row that have been inserted at the top (not visible) and in some instances spaces are still being created from that top row (which is not visible)…I think the problem here is refilling the board appropriately…any suggestions on this? and thank you again

warm regards

Hi, first of all, thank you for this great tutorial.
I have a question, how would you implement swipe controls? I mean, tap on item and then swap them by swipe. What is the best way to do this?
Thanks

@victor_wolf You’ll want to take a look at the GetTouch and TouchPhase documentation:

A common practice for detecting swipes is to take the initial touch position using TouchPhase.Began and then compare it to the final touch position using TouchPhase.Ended and if the finalPos.y > startPos.y it’s a swipe up. Less than would be a swipe down. Then compare the x’s to see if it was a swipe left or right.

Hope that helps!

@gamedeveloper23 Try disabling the colliders for the top row only. This way the RayCasts will not pick them up as matches.

Also since the sprites are swapped, and not the actual objects, you should only need to disable the top row colliders once at the start.

Cheers!
Jeff

Excellent tutorial, @jayfeesh.

I managed a few tweaks along the way… including adding a return value to ClearAllMatches (returns true if any matches were found, then on a false return, I swap the tiles -=back=- as this was an invalid move).

The one thing I’ve noticed is that while it picks up most matches, it misses a match where both tiles moved create independent matches under certain circumstances… This is because the tiles are removed and dropped on the first tile, then the second tile is checked… I’m thinking the solution is to queue the tile removal until after the checks are completed, then remove them all at once… perhaps an array of “matched tiles”, or simply a flag in tiles “matched” so the coroutine to fill the vacancies clears any matches it finds?

A better fix for the blank tiles after tile match bug:

The recommended fix above helps some of the time, but under certain annoying circumstances, it can still creep up. It looks like it’s related to stopping and restarting the FindNullTiles(), sometimes there will be a tile or two that wasn’t done yet, and when it restarts, for some reason it’s missing those…

So… what I did was encapsulate the entire function in a while loop… with the condition being “tilesareblank”… start with that as true… (we’re calling this because we know there are blank tiles, we made them). At the end of the function, I added a quick check for blank tiles. If there are no blank tiles, set tilesareblank to false, otherwise, set it to true.

 bool tilesareblank=true;
 while(tilesareblank) 
 {
      All that good juicy code for finding blank tiles and scrolling
      tilesareblank=false;
      for (int x=0;x<xSize;x++)
            for (inty=0;y<xSize;y++) 
            {
                  if(tiles[x,y].GetComponent<SpriteRenderer>().sprite==null)
                  {
                        tilesareblank=true;
                   }
            }
   }

My first clever attempt to fix this was to simply call the routine at the end of the routine… (i.e. just keep looking for nulls), but that led to a beautiful lockup…

Combined with the extra row of tiles, this appears to completely eliminate the missing tile bug.

I included a new int in board Manager yTop, during the creation phase, if y>yTop then I deactivate newTile’s collider. You could have 50 ySize and 11yTop and never get a match off screen. I also added a boolean PreventCascades. If true, this activates the code in the GetNewSprite(x,y) function that prevents matches. This allows for spawns to create those epic cascading matches you see in some games.

Next, I jiggered the code to cue up the tiles for clearing instead of clearing them outright. This eliminates some missed match opportunities where if both tiles would make a match, but the first tile’s matchup causes the tiles to start shifting before the 2nd tile’s match can be determined… I added a boolean “isMatched” instead of clearing the sprite. Then after checking for matches, I call a global function to null all sprites who have isMatched set, THEN call FindNullTiles.

1 Like

Hi there,

Love your tutorial and your preview project works just fine.
I’ve been working on a mobile version of a match 3 game.
When I copy over your 2 scripts for the boardmanager and the tiles and set everything up it looks like it’s working.
But when I luanch, for isntance the raycast to check nearby tiles only detects itself. I fexed the raycast thing and now the whole of unity just freezes when it starts the matching methods. Am I missing something?
Your game seems to be set up to run with just the 2 scripts? (Tile and boardmanager)

Kind regards